diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 9050664fcee..7fd5854208c 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -1,6 +1,6 @@ # Code - OSS Development Container -[![Open in Remote - Containers](https://img.shields.io/static/v1?label=Remote%20-%20Containers&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/vscode) +[![Open in Dev Containers](https://img.shields.io/static/v1?label=Dev%20Containers&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/vscode) This repository includes configuration for a development container for working with Code - OSS in a local container or using [GitHub Codespaces](https://github.com/features/codespaces). @@ -8,7 +8,7 @@ This repository includes configuration for a development container for working w ## Quick start - local -If you already have VS Code and Docker installed, you can click the badge above or [here](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/vscode) to get started. Clicking these links will cause VS Code to automatically install the Remote - Containers extension if needed, clone the source code into a container volume, and spin up a dev container for use. +If you already have VS Code and Docker installed, you can click the badge above or [here](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/vscode) to get started. Clicking these links will cause VS Code to automatically install the Dev Containers extension if needed, clone the source code into a container volume, and spin up a dev container for use. 1. Install Docker Desktop or Docker for Linux on your local machine. (See [docs](https://aka.ms/vscode-remote/containers/getting-started) for additional details.) @@ -16,13 +16,13 @@ If you already have VS Code and Docker installed, you can click the badge above > **Note:** The [Resource Monitor](https://marketplace.visualstudio.com/items?itemName=mutantdino.resourcemonitor) extension is included in the container so you can keep an eye on CPU/Memory in the status bar. -3. Install [Visual Studio Code Stable](https://code.visualstudio.com/) or [Insiders](https://code.visualstudio.com/insiders/) and the [Remote - Containers](https://aka.ms/vscode-remote/download/containers) extension. +3. Install [Visual Studio Code Stable](https://code.visualstudio.com/) or [Insiders](https://code.visualstudio.com/insiders/) and the [Dev Containers](https://aka.ms/vscode-remote/download/containers) extension. - ![Image of Remote - Containers extension](https://microsoft.github.io/vscode-remote-release/images/remote-containers-extn.png) + ![Image of Dev Containers extension](https://microsoft.github.io/vscode-remote-release/images/remote-containers-extn.png) - > **Note:** The Remote - Containers extension requires the Visual Studio Code distribution of Code - OSS. See the [FAQ](https://aka.ms/vscode-remote/faq/license) for details. + > **Note:** The Dev Containers extension requires the Visual Studio Code distribution of Code - OSS. See the [FAQ](https://aka.ms/vscode-remote/faq/license) for details. -4. Press Ctrl/Cmd + Shift + P or F1 and select **Remote-Containers: Clone Repository in Container Volume...**. +4. Press Ctrl/Cmd + Shift + P or F1 and select **Dev Containers: Clone Repository in Container Volume...**. > **Tip:** While you can use your local source tree instead, operations like `yarn install` can be slow on macOS or when using the Hyper-V engine on Windows. We recommend the "clone repository in container" approach instead since it uses "named volume" rather than the local filesystem. diff --git a/.eslintignore b/.eslintignore index e3aedfea0d6..af5f9a4940b 100644 --- a/.eslintignore +++ b/.eslintignore @@ -6,6 +6,7 @@ **/extensions/css-language-features/server/test/pathCompletionFixtures/** **/extensions/html-language-features/server/lib/jquery.d.ts **/extensions/html-language-features/server/src/test/pathCompletionFixtures/** +**/extensions/ipynb/notebook-out/** **/extensions/markdown-language-features/media/** **/extensions/markdown-language-features/notebook-out/** **/extensions/markdown-math/notebook-out/** @@ -27,4 +28,5 @@ **/src/vs/base/test/common/filters.perf.data.js **/src/vs/loader.js **/test/unit/assert.js +**/test/automation/out/** **/typings/** diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 8a44ce8c7ac..ce9e17c1c96 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -13,10 +13,10 @@ Does this issue occur when all extensions are disabled?: Yes/No -- VS Code Version: -- OS Version: +- VS Code Version: +- OS Version: Steps to Reproduce: -1. -2. +1. +2. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 51e7f366043..35dab07012c 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -3,3 +3,6 @@ contact_links: - name: Question url: https://stackoverflow.com/questions/tagged/visual-studio-code about: Please ask and answer questions here. + - name: Extension Development + url: https://github.com/microsoft/vscode-discussions/discussions/5 + about: Please use this for extension development questions and ideas. diff --git a/.github/classifier.json b/.github/classifier.json index 99bdbe96a29..ca0e4f86467 100644 --- a/.github/classifier.json +++ b/.github/classifier.json @@ -18,7 +18,7 @@ "callhierarchy": {"assign": ["jrieken"]}, "code-lens": {"assign": ["jrieken"]}, "color-palette": {"assign": []}, - "comments": {"assign": ["rebornix"]}, + "comments": {"assign": ["alexr00"]}, "config": {"assign": ["sandy081"]}, "context-keys": {"assign": []}, "css-less-scss": {"assign": ["aeschli"]}, @@ -58,6 +58,7 @@ "editor-scrollbar": {"assign": ["alexdima"]}, "editor-symbols": {"assign": ["jrieken"]}, "editor-synced-region": {"assign": ["aeschli"]}, + "editor-sticky-scroll": {"assign": ["jrieken"]}, "editor-textbuffer": {"assign": ["alexdima", "rebornix"]}, "editor-theming": {"assign": ["alexdima"]}, "editor-wordnav": {"assign": ["alexdima"]}, diff --git a/.vscode/cgmanifest.schema.json b/.vscode/cgmanifest.schema.json deleted file mode 100644 index 2e719b02396..00000000000 --- a/.vscode/cgmanifest.schema.json +++ /dev/null @@ -1,142 +0,0 @@ -{ - "type": "object", - "properties": { - "registrations": { - "type": "array", - "items": { - "type": "object", - "properties": { - "component": { - "oneOf": [ - { - "type": "object", - "required": [ - "type", - "git" - ], - "properties": { - "type": { - "type": "string", - "enum": [ - "git" - ] - }, - "git": { - "type": "object", - "required": [ - "name", - "repositoryUrl", - "commitHash" - ], - "properties": { - "name": { - "type": "string" - }, - "repositoryUrl": { - "type": "string" - }, - "commitHash": { - "type": "string" - } - } - } - } - }, - { - "type": "object", - "required": [ - "type", - "npm" - ], - "properties": { - "type": { - "type": "string", - "enum": [ - "npm" - ] - }, - "npm": { - "type": "object", - "required": [ - "name", - "version" - ], - "properties": { - "name": { - "type": "string" - }, - "version": { - "type": "string" - } - } - } - } - }, - { - "type": "object", - "required": [ - "type", - "other" - ], - "properties": { - "type": { - "type": "string", - "enum": [ - "other" - ] - }, - "other": { - "type": "object", - "required": [ - "name", - "downloadUrl", - "version" - ], - "properties": { - "name": { - "type": "string" - }, - "downloadUrl": { - "type": "string" - }, - "version": { - "type": "string" - } - } - } - } - } - ] - }, - "repositoryUrl": { - "type": "string", - "description": "The git url of the component" - }, - "version": { - "type": "string", - "description": "The version of the component" - }, - "license": { - "type": "string", - "description": "The name of the license" - }, - "developmentDependency": { - "type": "boolean", - "description": "This component is inlined in the vscode repo and **is not shipped**." - }, - "isOnlyProductionDependency": { - "type": "boolean", - "description": "This component is shipped and **is not inlined in the vscode repo**." - }, - "licenseDetail": { - "type": "array", - "items": { - "type": "string" - }, - "description": "The license text" - } - } - } - } - } -} diff --git a/.vscode/launch.json b/.vscode/launch.json index ab98af7d3d2..3bf8c67198f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -230,7 +230,7 @@ "runtimeExecutable": "${workspaceFolder}/scripts/code.sh" }, "port": 9222, - "timeout": 20000, + "timeout": 0, "env": { "VSCODE_EXTHOST_WILL_SEND_SOCKET": null, "VSCODE_SKIP_PRELAUNCH": "1" @@ -322,6 +322,33 @@ "order": 3 } }, + { + "type": "chrome", + "request": "launch", + "outFiles": [], + "perScriptSourcemaps": "yes", + "name": "VS Code Web (Chrome)", + "url": "http://localhost:8080", + "preLaunchTask": "Run code web", + "presentation": { + "group": "0_vscode", + "order": 3 + } + }, + { + "type": "msedge", + "request": "launch", + "outFiles": [], + "perScriptSourcemaps": "yes", + "name": "VS Code Web (Edge)", + "url": "http://localhost:8080", + "pauseForSourceMap": false, + "preLaunchTask": "Run code web", + "presentation": { + "group": "0_vscode", + "order": 3 + } + }, { "type": "node", "request": "launch", diff --git a/.vscode/notebooks/api.github-issues b/.vscode/notebooks/api.github-issues index fd582d87cf1..9bf483dbbcc 100644 --- a/.vscode/notebooks/api.github-issues +++ b/.vscode/notebooks/api.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repo=repo:microsoft/vscode\n$milestone=milestone:\"September 2022\"" + "value": "$repo=repo:microsoft/vscode\n$milestone=milestone:\"October 2022\"" }, { "kind": 1, diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues index 0eac8b51a1b..82e9dbfbb8b 100644 --- a/.vscode/notebooks/endgame.github-issues +++ b/.vscode/notebooks/endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-remotehub repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-unpkg\n\n$MILESTONE=milestone:\"August 2022\"" + "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n\r\n\r\n$MILESTONE=milestone:\"October 2022\"" }, { "kind": 1, @@ -32,7 +32,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS -$MILESTONE is:issue is:closed label:bug label:insiders-released -label:verified -label:*duplicate -label:*as-designed -label:z-author-verified -label:on-testplan" + "value": "$REPOS -$MILESTONE is:issue is:closed reason:completed label:bug label:insiders-released -label:verified -label:*duplicate -label:*as-designed -label:z-author-verified -label:on-testplan" }, { "kind": 1, @@ -42,7 +42,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS -$MILESTONE is:issue is:closed label:feature-request label:insiders-released -label:on-testplan -label:verified -label:*duplicate" + "value": "$REPOS -$MILESTONE is:issue is:closed reason:completed label:feature-request label:insiders-released -label:on-testplan -label:verified -label:*duplicate" }, { "kind": 1, @@ -62,7 +62,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE is:issue is:closed label:feature-request -label:verification-needed -label:on-testplan -label:verified -label:*duplicate" + "value": "$REPOS $MILESTONE is:issue is:closed reason:completed label:feature-request -label:verification-needed -label:on-testplan -label:verified -label:*duplicate" }, { "kind": 1, @@ -97,7 +97,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE is:issue is:closed label:verification-needed -label:verified" + "value": "$REPOS $MILESTONE is:issue is:closed reason:completed label:verification-needed -label:verified" }, { "kind": 1, @@ -112,7 +112,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE is:issue is:closed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found -label:z-author-verified -label:unreleased" + "value": "$REPOS $MILESTONE is:issue is:closed reason:completed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found -label:z-author-verified -label:unreleased" }, { "kind": 1, @@ -122,7 +122,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE is:issue is:closed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found -label:z-author-verified label:unreleased" + "value": "$REPOS $MILESTONE is:issue is:closed reason:completed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found -label:z-author-verified label:unreleased" }, { "kind": 1, diff --git a/.vscode/notebooks/my-endgame.github-issues b/.vscode/notebooks/my-endgame.github-issues index 2032edb424b..3bfee2f4e4c 100644 --- a/.vscode/notebooks/my-endgame.github-issues +++ b/.vscode/notebooks/my-endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remotehub repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal\r\n\r\n$MILESTONE=milestone:\"August 2022\"\r\n\r\n$MINE=assignee:@me" + "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n\r\n\r\n$MILESTONE=milestone:\"October 2022\"\r\n\r\n$MINE=assignee:@me" }, { "kind": 1, @@ -42,7 +42,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE $MINE is:issue is:closed label:feature-request -label:verification-needed -label:on-testplan -label:verified -label:*duplicate" + "value": "$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:feature-request -label:verification-needed -label:on-testplan -label:verified -label:*duplicate" }, { "kind": 1, @@ -62,7 +62,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE $MINE is:issue is:closed label:feature-request label:verification-needed -label:verified" + "value": "$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:feature-request label:verification-needed -label:verified" }, { "kind": 1, @@ -87,7 +87,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE -$MINE is:issue is:closed -assignee:@me -label:verified -label:z-author-verified label:feature-request label:verification-needed" + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed -assignee:@me -label:verified -label:z-author-verified label:feature-request label:verification-needed -label:verification-steps-needed -label:unreleased" }, { "kind": 1, @@ -147,7 +147,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE -$MINE is:issue is:closed author:@me sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:triage-needed -label:verification-found" + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed author:@me sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:triage-needed -label:verification-found" }, { "kind": 1, @@ -157,7 +157,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE -$MINE is:issue is:closed sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found -author:aeschli -author:alexdima -author:alexr00 -author:AmandaSilver -author:andreamah -author:bamurtaugh -author:bpasero -author:chrisdias -author:chrmarti -author:Chuxel -author:claudiaregio -author:connor4312 -author:dbaeumer -author:deepak1556 -author:devinvalenciano -author:digitarald -author:DonJayamanne -author:egamma -author:fiveisprime -author:gregvanl -author:hediet -author:IanMatthewHuff -author:isidorn -author:joaomoreno -author:joyceerhl -author:jrieken -author:karrtikr -author:kieferrm -author:lramos15 -author:lszomoru -author:meganrogge -author:misolori -author:mjbvz -author:rebornix -author:roblourens -author:rzhao271 -author:sandy081 -author:sbatten -author:stevencl -author:tanhakabir -author:TylerLeonhardt -author:Tyriar -author:weinand -author:amunger" + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found -author:aeschli -author:alexdima -author:alexr00 -author:AmandaSilver -author:andreamah -author:bamurtaugh -author:bpasero -author:chrisdias -author:chrmarti -author:Chuxel -author:claudiaregio -author:connor4312 -author:dbaeumer -author:deepak1556 -author:devinvalenciano -author:digitarald -author:DonJayamanne -author:egamma -author:fiveisprime -author:gregvanl -author:hediet -author:IanMatthewHuff -author:isidorn -author:joaomoreno -author:joyceerhl -author:jrieken -author:karrtikr -author:kieferrm -author:lramos15 -author:lszomoru -author:meganrogge -author:misolori -author:mjbvz -author:rebornix -author:roblourens -author:rzhao271 -author:sandy081 -author:sbatten -author:stevencl -author:tanhakabir -author:TylerLeonhardt -author:Tyriar -author:weinand -author:amunger" }, { "kind": 1, @@ -167,7 +167,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE -$MINE is:issue is:closed -author:@me sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found" + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed -author:@me sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found" }, { "kind": 1, @@ -177,6 +177,6 @@ { "kind": 2, "language": "github-issues", - "value": "repo:microsoft/vscode $MILESTONE $MINE is:issue is:closed label:feature-request -label:on-release-notes" + "value": "repo:microsoft/vscode $MILESTONE $MINE is:issue is:closed reason:completed label:feature-request -label:on-release-notes" } ] \ No newline at end of file diff --git a/.vscode/notebooks/my-work.github-issues b/.vscode/notebooks/my-work.github-issues index 4562f07797d..69d50e05965 100644 --- a/.vscode/notebooks/my-work.github-issues +++ b/.vscode/notebooks/my-work.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce\n\n// current milestone name\n$milestone=milestone:\"September 2022\"" + "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n\n\n// current milestone name\n$milestone=milestone:\"October 2022\"" }, { "kind": 1, diff --git a/.vscode/notebooks/verification.github-issues b/.vscode/notebooks/verification.github-issues index 7015a401be5..5c6354a4e72 100644 --- a/.vscode/notebooks/verification.github-issues +++ b/.vscode/notebooks/verification.github-issues @@ -2,7 +2,7 @@ { "kind": 1, "language": "markdown", - "value": "### Bug Verification Queries\n\nBefore shipping we want to verify _all_ bugs. That means when a bug is fixed we check that the fix actually works. It's always best to start with bugs that you have filed and the proceed with bugs that have been filed from users outside the development team. " + "value": "### Bug Verification Queries\r\n\r\nBefore shipping we want to verify _all_ bugs. That means when a bug is fixed we check that the fix actually works. It's always best to start with bugs that you have filed and the proceed with bugs that have been filed from users outside the development team. " }, { "kind": 1, @@ -12,7 +12,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repos=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-jupyter repo:microsoft/vscode-python\n$milestone=milestone:\"May 2022\"" + "value": "$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n\n$milestone=milestone:\"May 2022\"" }, { "kind": 1, @@ -22,7 +22,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repos $milestone is:closed -assignee:@me label:bug -label:verified -label:*duplicate author:@me" + "value": "$repos $milestone is:closed reason:completed -assignee:@me label:bug -label:verified -label:*duplicate author:@me" }, { "kind": 1, @@ -32,7 +32,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repos $milestone is:closed -assignee:@me label:bug -label:verified -label:*duplicate -author:@me -assignee:@me label:bug -label:verified -author:@me -author:aeschli -author:alexdima -author:alexr00 -author:bpasero -author:chrisdias -author:chrmarti -author:connor4312 -author:dbaeumer -author:deepak1556 -author:eamodio -author:egamma -author:gregvanl -author:isidorn -author:JacksonKearl -author:joaomoreno -author:jrieken -author:lramos15 -author:lszomoru -author:meganrogge -author:misolori -author:mjbvz -author:rebornix -author:RMacfarlane -author:roblourens -author:sana-ajani -author:sandy081 -author:sbatten -author:Tyriar -author:weinand -author:rzhao271 -author:kieferrm -author:TylerLeonhardt -author:bamurtaugh -author:hediet -author:joyceerhl -author:rchiodo -author:IanMatthewHuff" + "value": "$repos $milestone is:closed reason:completed -assignee:@me label:bug -label:verified -label:*duplicate -author:@me -assignee:@me label:bug -label:verified -author:@me -author:aeschli -author:alexdima -author:alexr00 -author:bpasero -author:chrisdias -author:chrmarti -author:connor4312 -author:dbaeumer -author:deepak1556 -author:eamodio -author:egamma -author:gregvanl -author:isidorn -author:JacksonKearl -author:joaomoreno -author:jrieken -author:lramos15 -author:lszomoru -author:meganrogge -author:misolori -author:mjbvz -author:rebornix -author:RMacfarlane -author:roblourens -author:sana-ajani -author:sandy081 -author:sbatten -author:Tyriar -author:weinand -author:rzhao271 -author:kieferrm -author:TylerLeonhardt -author:bamurtaugh -author:hediet -author:joyceerhl -author:rchiodo -author:IanMatthewHuff" }, { "kind": 1, @@ -42,6 +42,6 @@ { "kind": 2, "language": "github-issues", - "value": "$repos $milestone is:closed -assignee:@me label:bug -label:verified -label:*duplicate" + "value": "$repos $milestone is:closed reason:completed -assignee:@me label:bug -label:verified -label:*duplicate" } ] \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 5fa37b2d551..76ac1983537 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -53,7 +53,7 @@ "fileMatch": [ "cgmanifest.json" ], - "url": "./.vscode/cgmanifest.schema.json" + "url": "https://json.schemastore.org/component-detection-manifest.json", }, { "fileMatch": [ @@ -65,11 +65,12 @@ "git.ignoreLimitWarning": true, "git.branchProtection": [ "main", + "distro", "release/*" ], "git.branchProtectionPrompt": "alwaysCommitToNewBranch", "git.branchRandomName.enable": true, - "git.fetchBeforeCheckout": true, + "git.pullBeforeCheckout": true, "git.mergeEditor": true, "remote.extensionKind": { "msjsdiag.debugger-for-chrome": "workspace" @@ -106,11 +107,6 @@ "git", "sash" ], - "editor.quickSuggestions": { - "other": "inline", - "comments": "inline", - "strings": "inline" - }, "githubPullRequests.assignCreated": "${user}", "githubPullRequests.defaultMergeMethod": "squash" } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 885f825cc2e..59b1893c293 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -203,6 +203,28 @@ "reveal": "never" } }, + { + "type": "shell", + "command": "./scripts/code-web.sh", + "windows": { + "command": ".\\scripts\\code-web.bat" + }, + "args": ["--port", "8080", "--browser", "none"], + "label": "Run code web", + "isBackground": true, + "problemMatcher": { + "pattern": { + "regexp": "" + }, + "background": { + "beginsPattern": ".*node .*", + "endsPattern": "Listening on .*" + } + }, + "presentation": { + "reveal": "never" + } + }, { "type": "npm", "script": "eslint", diff --git a/README.md b/README.md index eb76f98421e..0c7c6236c42 100644 --- a/README.md +++ b/README.md @@ -58,10 +58,10 @@ VS Code includes a set of built-in extensions located in the [extensions](extens ## Development Container -This repository includes a Visual Studio Code Remote - Containers / GitHub Codespaces development container. +This repository includes a Visual Studio Code Dev Containers / GitHub Codespaces development container. -- For [Remote - Containers](https://aka.ms/vscode-remote/download/containers), use the **Remote-Containers: Clone Repository in Container Volume...** command which creates a Docker volume for better disk I/O on macOS and Windows. - - If you already have VS Code and Docker installed, you can also click [here](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/vscode) to get started. This will cause VS Code to automatically install the Remote - Containers extension if needed, clone the source code into a container volume, and spin up a dev container for use. +- For [Dev Containers](https://aka.ms/vscode-remote/download/containers), use the **Dev Containers: Clone Repository in Container Volume...** command which creates a Docker volume for better disk I/O on macOS and Windows. + - If you already have VS Code and Docker installed, you can also click [here](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/vscode) to get started. This will cause VS Code to automatically install the Dev Containers extension if needed, clone the source code into a container volume, and spin up a dev container for use. - For Codespaces, install the [GitHub Codespaces](https://marketplace.visualstudio.com/items?itemName=GitHub.codespaces) extension in VS Code, and use the **Codespaces: Create New Codespace** command. Docker / the Codespace should have at least **4 Cores and 6 GB of RAM (8 GB recommended)** to run full build. See the [development container README](.devcontainer/README.md) for more information. diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 5c36a652b4b..9326900f1b4 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -1,86 +1,14 @@ -microsoft-vscode +NOTICES -THIRD-PARTY SOFTWARE NOTICES AND INFORMATION -Do Not Translate or Localize - -This project incorporates components from the projects listed below. The original copyright notices and the licenses under which Microsoft received such components are set forth below. Microsoft reserves all rights not expressly granted herein, whether by implication, estoppel or otherwise. - -1. atom/language-clojure version 0.22.8 (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-css version 0.45.1 (https://github.com/atom/language-css) -4. atom/language-java version 0.32.1 (https://github.com/atom/language-java) -5. atom/language-sass version 0.62.1 (https://github.com/atom/language-sass) -6. atom/language-shellscript version 0.28.2 (https://github.com/atom/language-shellscript) -7. atom/language-xml version 0.35.2 (https://github.com/atom/language-xml) -8. better-go-syntax version 1.0.0 (https://github.com/jeff-hykin/better-go-syntax/ ) -9. Colorsublime-Themes version 0.1.0 (https://github.com/Colorsublime/Colorsublime-Themes) -10. daaain/Handlebars version 1.8.0 (https://github.com/daaain/Handlebars) -11. dart-lang/dart-syntax-highlight (https://github.com/dart-lang/dart-syntax-highlight) -12. davidrios/pug-tmbundle (https://github.com/davidrios/pug-tmbundle) -13. definitelytyped (https://github.com/DefinitelyTyped/DefinitelyTyped) -14. demyte/language-cshtml version 0.3.0 (https://github.com/demyte/language-cshtml) -15. Document Object Model version 4.0.0 (https://www.w3.org/DOM/) -16. dompurify version 2.3.1 (https://github.com/cure53/DOMPurify) -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 version 2.3.8 (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. James-Yu/LaTeX-Workshop version 8.19.1 (https://github.com/James-Yu/LaTeX-Workshop) -26. jeff-hykin/better-c-syntax version 1.13.2 (https://github.com/jeff-hykin/better-c-syntax) -27. jeff-hykin/better-cpp-syntax version 1.15.18 (https://github.com/jeff-hykin/better-cpp-syntax) -28. jeff-hykin/better-objc-syntax version 0.2.0 (https://github.com/jeff-hykin/better-objc-syntax) -29. jeff-hykin/better-objcpp-syntax version 0.1.0 (https://github.com/jeff-hykin/better-objcpp-syntax) -30. jlelong/vscode-latex-basics version 1.3.0 (https://github.com/jlelong/vscode-latex-basics) -31. js-beautify version 1.6.8 (https://github.com/beautify-web/js-beautify) -32. JuliaEditorSupport/atom-language-julia version 0.22.1 (https://github.com/JuliaEditorSupport/atom-language-julia) -33. Jxck/assert version 1.0.0 (https://github.com/Jxck/assert) -34. language-docker (https://github.com/moby/moby) -35. language-less version 0.34.2 (https://github.com/atom/language-less) -36. language-php version 0.48.1 (https://github.com/atom/language-php) -37. MagicStack/MagicPython version 1.1.1 (https://github.com/MagicStack/MagicPython) -38. marked version 4.0.16 (https://github.com/markedjs/marked) -39. mdn-data version 1.1.12 (https://github.com/mdn/data) -40. microsoft/TypeScript-TmLanguage version 0.0.1 (https://github.com/microsoft/TypeScript-TmLanguage) -41. microsoft/vscode-JSON.tmLanguage (https://github.com/microsoft/vscode-JSON.tmLanguage) -42. microsoft/vscode-markdown-tm-grammar version 1.0.0 (https://github.com/microsoft/vscode-markdown-tm-grammar) -43. microsoft/vscode-mssql version 1.16.0 (https://github.com/microsoft/vscode-mssql) -44. mmims/language-batchfile version 0.7.6 (https://github.com/mmims/language-batchfile) -45. NVIDIA/cuda-cpp-grammar (https://github.com/NVIDIA/cuda-cpp-grammar) -46. PowerShell/EditorSyntax version 1.0.0 (https://github.com/PowerShell/EditorSyntax) -47. rust-syntax version 0.5.0 (https://github.com/dustypomerleau/rust-syntax) -48. semver version 5.5.0 (https://github.com/npm/node-semver) -49. seti-ui version 0.1.0 (https://github.com/jesseweed/seti-ui) -50. shaders-tmLanguage version 0.1.0 (https://github.com/tgjones/shaders-tmLanguage) -51. sumneko/lua.tmbundle version 1.0.0 (https://github.com/sumneko/lua.tmbundle) -52. textmate/asp.vb.net.tmbundle (https://github.com/textmate/asp.vb.net.tmbundle) -53. textmate/c.tmbundle (https://github.com/textmate/c.tmbundle) -54. textmate/diff.tmbundle (https://github.com/textmate/diff.tmbundle) -55. textmate/git.tmbundle (https://github.com/textmate/git.tmbundle) -56. textmate/groovy.tmbundle (https://github.com/textmate/groovy.tmbundle) -57. textmate/html.tmbundle (https://github.com/textmate/html.tmbundle) -58. textmate/ini.tmbundle (https://github.com/textmate/ini.tmbundle) -59. textmate/javascript.tmbundle (https://github.com/textmate/javascript.tmbundle) -60. textmate/markdown.tmbundle (https://github.com/textmate/markdown.tmbundle) -61. textmate/perl.tmbundle (https://github.com/textmate/perl.tmbundle) -62. textmate/ruby.tmbundle (https://github.com/textmate/ruby.tmbundle) -63. textmate/yaml.tmbundle (https://github.com/textmate/yaml.tmbundle) -64. trond-snekvik/vscode-rst version 1.5.1 (https://github.com/trond-snekvik/vscode-rst) -65. TypeScript-TmLanguage version 0.1.8 (https://github.com/microsoft/TypeScript-TmLanguage) -66. TypeScript-TmLanguage version 1.0.0 (https://github.com/microsoft/TypeScript-TmLanguage) -67. Unicode version 12.0.0 (https://home.unicode.org/) -68. vscode-codicons version 0.0.14 (https://github.com/microsoft/vscode-codicons) -69. vscode-logfile-highlighter version 2.15.0 (https://github.com/emilast/vscode-logfile-highlighter) -70. vscode-swift version 0.0.1 (https://github.com/owensd/vscode-swift) -71. vscode-win32-app-container-tokens (https://github.com/microsoft/vscode-win32-app-container-tokens) -72. Web Background Synchronization (https://github.com/WICG/background-sync) +This repository incorporates material as listed below or described in the code. -%% atom/language-clojure NOTICES AND INFORMATION BEGIN HERE -========================================= + +--------------------------------------------------------- + +atom/language-clojure 0.22.8 - MIT +https://github.com/atom/language-clojure + Copyright (c) 2014 GitHub Inc. Permission is hereby granted, free of charge, to any person obtaining @@ -128,11 +56,13 @@ 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 atom/language-clojure NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +atom/language-coffee-script 0.49.3 - MIT +https://github.com/atom/language-coffee-script -%% atom/language-coffee-script NOTICES AND INFORMATION BEGIN HERE -========================================= The MIT License (MIT) Copyright (c) 2014 GitHub Inc. @@ -183,11 +113,13 @@ 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 atom/language-coffee-script NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +atom/language-css 0.45.1 - GitHub License +https://github.com/atom/language-css -%% atom/language-css NOTICES AND INFORMATION BEGIN HERE -========================================= Copyright (c) 2014 GitHub Inc. Permission is hereby granted, free of charge, to any person obtaining @@ -219,11 +151,13 @@ 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 0.32.1 - MIT +https://github.com/atom/language-java -%% atom/language-java NOTICES AND INFORMATION BEGIN HERE -========================================= The MIT License (MIT) Copyright (c) 2014 GitHub Inc. @@ -256,11 +190,13 @@ 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-java NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +atom/language-sass 0.61.4 - MIT +https://github.com/atom/language-sass -%% atom/language-sass NOTICES AND INFORMATION BEGIN HERE -========================================= The MIT License (MIT) Copyright (c) 2014 GitHub Inc. @@ -309,11 +245,13 @@ 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 atom/language-sass NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +atom/language-shellscript 0.28.2 - MIT +https://github.com/atom/language-shellscript -%% atom/language-shellscript NOTICES AND INFORMATION BEGIN HERE -========================================= The MIT License (MIT) Copyright (c) 2014 GitHub Inc. @@ -346,11 +284,13 @@ 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-shellscript NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +atom/language-xml 0.35.2 - MIT +https://github.com/atom/language-xml -%% atom/language-xml NOTICES AND INFORMATION BEGIN HERE -========================================= The MIT License (MIT) Copyright (c) 2014 GitHub Inc. @@ -383,11 +323,13 @@ 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-xml NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +better-go-syntax 1.0.0 - MIT +https://github.com/jeff-hykin/better-go-syntax/ -%% better-go-syntax NOTICES AND INFORMATION BEGIN HERE -========================================= MIT License Copyright (c) 2019 Jeff Hykin @@ -409,11 +351,13 @@ 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 better-go-syntax NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +Colorsublime-Themes 0.1.0 +https://github.com/Colorsublime/Colorsublime-Themes -%% Colorsublime-Themes NOTICES AND INFORMATION BEGIN HERE -========================================= Copyright (c) 2015 Colorsublime.com Permission is hereby granted, free of charge, to any person obtaining a copy @@ -433,11 +377,13 @@ 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 Colorsublime-Themes NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +daaain/Handlebars 1.8.0 - MIT +https://github.com/daaain/Handlebars -%% daaain/Handlebars NOTICES AND INFORMATION BEGIN HERE -========================================= -- Credits Adapted from the great sublime-text-handlebars package by Nicholas Westlake. @@ -455,11 +401,13 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of 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 daaain/Handlebars NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +dart-lang/dart-syntax-highlight 0.0.0 - BSD +https://github.com/dart-lang/dart-syntax-highlight -%% dart-lang/dart-syntax-highlight NOTICES AND INFORMATION BEGIN HERE -========================================= Copyright 2020, the Dart project authors. Redistribution and use in source and binary forms, with or without @@ -487,11 +435,13 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -========================================= -END OF dart-lang/dart-syntax-highlight NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +davidrios/pug-tmbundle 0.0.0 - MIT +https://github.com/davidrios/pug-tmbundle -%% davidrios/pug-tmbundle NOTICES AND INFORMATION BEGIN HERE -========================================= The MIT License (MIT) Copyright (c) 2016 David Rios @@ -512,11 +462,13 @@ 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 davidrios/pug-tmbundle NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +definitelytyped - MIT +https://github.com/DefinitelyTyped/DefinitelyTyped -%% definitelytyped NOTICES AND INFORMATION BEGIN HERE -========================================= This project is licensed under the MIT license. Copyrights are respective of each contributor listed at the beginning of each definition file. @@ -525,11 +477,13 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of 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 definitelytyped NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +demyte/language-cshtml 0.3.0 - MIT +https://github.com/demyte/language-cshtml -%% demyte/language-cshtml NOTICES AND INFORMATION BEGIN HERE -========================================= The MIT License (MIT) Copyright (c) 2014 James Summerton @@ -552,11 +506,13 @@ 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 demyte/language-cshtml NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +Document Object Model 4.0.0 - W3C License +https://www.w3.org/DOM/ -%% Document Object Model NOTICES AND INFORMATION BEGIN HERE -========================================= W3C License This work is being provided by the copyright holders under the following license. By obtaining and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions. @@ -573,11 +529,13 @@ FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENT W COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT. The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the work without specific, written prior permission. Title to copyright in this work will at all times remain with copyright holders. -========================================= -END OF Document Object Model NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +dompurify 2.3.1 - Apache 2.0 +https://github.com/cure53/DOMPurify -%% dompurify NOTICES AND INFORMATION BEGIN HERE -========================================= DOMPurify Copyright 2015 Mario Heiderich @@ -955,11 +913,13 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -========================================= -END OF dompurify NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +dotnet/csharp-tmLanguage 0.1.0 - MIT +https://github.com/dotnet/csharp-tmLanguage -%% dotnet/csharp-tmLanguage NOTICES AND INFORMATION BEGIN HERE -========================================= MIT License Copyright (c) 2016 .NET Foundation @@ -981,11 +941,13 @@ 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 dotnet/csharp-tmLanguage NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +expand-abbreviation 0.5.8 - MIT +https://github.com/emmetio/expand-abbreviation -%% expand-abbreviation NOTICES AND INFORMATION BEGIN HERE -========================================= MIT License Copyright (c) 2017 Emmet.io @@ -1007,11 +969,13 @@ 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 expand-abbreviation NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +fadeevab/make.tmbundle 0.0.0 - TextMate Bundle License +https://github.com/fadeevab/make.tmbundle -%% fadeevab/make.tmbundle NOTICES AND INFORMATION BEGIN HERE -========================================= Copyright (c) textmate-make.tmbundle project authors If not otherwise specified (see below), files in this repository fall under the following license: @@ -1025,11 +989,13 @@ An exception is made for files in readable text which contain their own license or files where an accompanying file exists (in the same directory) with a "-license" suffix added to the base-name name of the original file, and an extension of txt, html, or similar. For example "tidy" is accompanied by "tidy-license.txt". -========================================= -END OF fadeevab/make.tmbundle NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +freebroccolo/atom-language-swift 0.0.0 - MIT +https://github.com/freebroccolo/atom-language-swift -%% freebroccolo/atom-language-swift NOTICES AND INFORMATION BEGIN HERE -========================================= The MIT License (MIT) Copyright (c) 2014 Darin Morrison @@ -1050,11 +1016,13 @@ 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 freebroccolo/atom-language-swift NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +HTML 5.1 W3C Working Draft 08 October 2015 - W3C Document License +http://www.w3.org/TR/2015/WD-html51-20151008/ -%% HTML 5.1 W3C Working Draft NOTICES AND INFORMATION BEGIN HERE -========================================= Copyright © 2015 W3C® (MIT, ERCIM, Keio, Beihang). This software or document includes material copied from or derived from HTML 5.1 W3C Working Draft (http://www.w3.org/TR/2015/WD-html51-20151008/.) @@ -1068,11 +1036,13 @@ DOCUMENT OR THE PERFORMANCE OR IMPLEMENTATION OF THE CONTENTS THEREOF. The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to this document or its contents without specific, written prior permission. Title to copyright in this document will at all times remain with copyright holders. -========================================= -END OF HTML 5.1 W3C Working Draft NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +Ikuyadeu/vscode-R 2.3.8 - MIT +https://github.com/Ikuyadeu/vscode-R -%% Ikuyadeu/vscode-R NOTICES AND INFORMATION BEGIN HERE -========================================= MIT License Copyright (c) 2022 REditorSupport @@ -1094,11 +1064,13 @@ 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 Ikuyadeu/vscode-R NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +Ionic documentation 1.2.4 - Apache2 +https://github.com/ionic-team/ionic-site -%% Ionic documentation NOTICES AND INFORMATION BEGIN HERE -========================================= Copyright Drifty Co. http://drifty.com/. Apache License @@ -1156,11 +1128,13 @@ If the Work includes a "NOTICE" text file as part of its distribution, then any 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS -========================================= -END OF Ionic documentation NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +ionide/ionide-fsgrammar 0.0.0 - MIT +https://github.com/ionide/ionide-fsgrammar -%% ionide/ionide-fsgrammar NOTICES AND INFORMATION BEGIN HERE -========================================= The MIT License (MIT) Copyright (c) 2015 Krzysztof Cieslak @@ -1182,11 +1156,13 @@ 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 ionide/ionide-fsgrammar NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +James-Yu/LaTeX-Workshop 8.19.1 - MIT +https://github.com/James-Yu/LaTeX-Workshop -%% James-Yu/LaTeX-Workshop NOTICES AND INFORMATION BEGIN HERE -========================================= The MIT License (MIT) Copyright (c) 2016 James Yu @@ -1208,11 +1184,13 @@ 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 James-Yu/LaTeX-Workshop NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +jeff-hykin/better-c-syntax 1.13.2 - MIT +https://github.com/jeff-hykin/better-c-syntax -%% jeff-hykin/better-c-syntax NOTICES AND INFORMATION BEGIN HERE -========================================= MIT License Copyright (c) 2019 Jeff Hykin @@ -1234,11 +1212,13 @@ 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/better-c-syntax NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +jeff-hykin/better-cpp-syntax 1.15.18 - MIT +https://github.com/jeff-hykin/better-cpp-syntax -%% jeff-hykin/better-cpp-syntax NOTICES AND INFORMATION BEGIN HERE -========================================= MIT License Copyright (c) 2019 Jeff Hykin @@ -1260,11 +1240,13 @@ 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/better-cpp-syntax NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +jeff-hykin/better-objc-syntax 0.2.0 - MIT +https://github.com/jeff-hykin/better-objc-syntax -%% jeff-hykin/better-objc-syntax NOTICES AND INFORMATION BEGIN HERE -========================================= MIT License Copyright (c) 2019 Jeff Hykin @@ -1286,11 +1268,13 @@ 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/better-objc-syntax NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +jeff-hykin/better-objcpp-syntax 0.1.0 - MIT +https://github.com/jeff-hykin/better-objcpp-syntax -%% jeff-hykin/better-objcpp-syntax NOTICES AND INFORMATION BEGIN HERE -========================================= MIT License Copyright (c) 2019 Jeff Hykin @@ -1312,11 +1296,13 @@ 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/better-objcpp-syntax NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +jlelong/vscode-latex-basics 1.3.0 - MIT +https://github.com/jlelong/vscode-latex-basics -%% jlelong/vscode-latex-basics NOTICES AND INFORMATION BEGIN HERE -========================================= Copyright (c) vscode-latex-basics authors If not otherwise specified (see below), files in this repository fall under the MIT License @@ -1335,11 +1321,13 @@ included in VSCode and falls under the license described in markdown-latex-combi The file syntaxes/cpp-grammar-bailout.tmLanguage.json is generated from https://github.com/jeff-hykin/better-cpp-syntax and falls under the license described in cpp-bailout-license.txt. -========================================= -END OF jlelong/vscode-latex-basics NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +js-beautify 1.6.8 - MIT +https://github.com/beautify-web/js-beautify -%% js-beautify NOTICES AND INFORMATION BEGIN HERE -========================================= The MIT License (MIT) Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. @@ -1349,11 +1337,13 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of 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 js-beautify NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +JuliaEditorSupport/atom-language-julia 0.22.1 - MIT +https://github.com/JuliaEditorSupport/atom-language-julia -%% JuliaEditorSupport/atom-language-julia NOTICES AND INFORMATION BEGIN HERE -========================================= The atom-language-julia package is licensed under the MIT "Expat" License: > Copyright (c) 2015 @@ -1376,11 +1366,13 @@ The atom-language-julia package is licensed under the MIT "Expat" License: > 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 JuliaEditorSupport/atom-language-julia NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +Jxck/assert 1.0.0 - MIT +https://github.com/Jxck/assert -%% Jxck/assert NOTICES AND INFORMATION BEGIN HERE -========================================= The MIT License (MIT) Copyright (c) 2011 Jxck @@ -1404,11 +1396,13 @@ 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 Jxck/assert NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +language-docker 0.0.0 - Apache2 +https://github.com/moby/moby -%% language-docker NOTICES AND INFORMATION BEGIN HERE -========================================= Apache License Version 2.0, January 2004 https://www.apache.org/licenses/ @@ -1599,11 +1593,13 @@ Apache License WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -========================================= -END OF language-docker NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +language-less 0.34.2 - MIT +https://github.com/atom/language-less -%% language-less NOTICES AND INFORMATION BEGIN HERE -========================================= The MIT License (MIT) Copyright (c) 2014 GitHub Inc. @@ -1651,11 +1647,13 @@ 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 language-less NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +language-php 0.48.1 - MIT +https://github.com/atom/language-php -%% language-php NOTICES AND INFORMATION BEGIN HERE -========================================= The MIT License (MIT) Copyright (c) 2014 GitHub Inc. @@ -1688,11 +1686,13 @@ 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 language-php NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +MagicStack/MagicPython 1.1.1 - MIT +https://github.com/MagicStack/MagicPython -%% MagicStack/MagicPython NOTICES AND INFORMATION BEGIN HERE -========================================= The MIT License Copyright (c) 2015-present MagicStack Inc. http://magic.io @@ -1714,11 +1714,13 @@ 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 MagicStack/MagicPython NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +marked 4.1.0 - MIT +https://github.com/markedjs/marked -%% marked NOTICES AND INFORMATION BEGIN HERE -========================================= information ## Contribution License Agreement @@ -1763,11 +1765,13 @@ Redistribution and use in source and binary forms, with or without modification, * Neither the name "Markdown" nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. This software is provided by the copyright holders and contributors "as is" and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright owner or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage. -========================================= -END OF marked NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +mdn-data 1.1.12 - MPL +https://github.com/mdn/data -%% mdn-data NOTICES AND INFORMATION BEGIN HERE -========================================= Mozilla Public License Version 2.0 Copyright (c) 2018 Mozilla Corporation @@ -2144,11 +2148,13 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -========================================= -END OF mdn-data NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +microsoft/TypeScript-TmLanguage 0.0.1 - MIT +https://github.com/microsoft/TypeScript-TmLanguage -%% microsoft/TypeScript-TmLanguage NOTICES AND INFORMATION BEGIN HERE -========================================= Copyright (c) Microsoft Corporation All rights reserved. @@ -2171,11 +2177,13 @@ 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 microsoft/TypeScript-TmLanguage NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +microsoft/vscode-JSON.tmLanguage 0.0.0 - MIT +https://github.com/microsoft/vscode-JSON.tmLanguage -%% microsoft/vscode-JSON.tmLanguage NOTICES AND INFORMATION BEGIN HERE -========================================= vscode-JSON.tmLanguage Copyright (c) Microsoft Corporation @@ -2195,11 +2203,13 @@ THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI 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 microsoft/vscode-JSON.tmLanguage NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +microsoft/vscode-markdown-tm-grammar 1.0.0 - MIT +https://github.com/microsoft/vscode-markdown-tm-grammar -%% microsoft/vscode-markdown-tm-grammar NOTICES AND INFORMATION BEGIN HERE -========================================= The MIT License (MIT) Copyright (c) Microsoft 2018 @@ -2221,11 +2231,13 @@ 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 microsoft/vscode-markdown-tm-grammar NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +microsoft/vscode-mssql 1.16.0 - MIT +https://github.com/microsoft/vscode-mssql -%% microsoft/vscode-mssql NOTICES AND INFORMATION BEGIN HERE -========================================= ------------------------------------------ START OF LICENSE ----------------------------------------- vscode-mssql Copyright (c) Microsoft Corporation @@ -2236,11 +2248,13 @@ Copyright (c) 2016 Microsoft 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 LICENSE ----------------------------------------- -========================================= -END OF microsoft/vscode-mssql NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +mmims/language-batchfile 0.7.6 - MIT +https://github.com/mmims/language-batchfile -%% mmims/language-batchfile NOTICES AND INFORMATION BEGIN HERE -========================================= The MIT License (MIT) Copyright (c) 2021 Michael Mims @@ -2262,11 +2276,13 @@ 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 mmims/language-batchfile NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +NVIDIA/cuda-cpp-grammar 0.0.0 - MIT +https://github.com/NVIDIA/cuda-cpp-grammar -%% NVIDIA/cuda-cpp-grammar NOTICES AND INFORMATION BEGIN HERE -========================================= The MIT License (MIT) Copyright 2021 NVIDIA Corporation @@ -2276,11 +2292,13 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of 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 NVIDIA/cuda-cpp-grammar NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +PowerShell/EditorSyntax 1.0.0 - MIT +https://github.com/PowerShell/EditorSyntax -%% PowerShell/EditorSyntax NOTICES AND INFORMATION BEGIN HERE -========================================= Copyright (c) Microsoft Corporation All rights reserved. @@ -2304,11 +2322,13 @@ 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 PowerShell/EditorSyntax NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +rust-syntax 0.5.0 - MIT +https://github.com/dustypomerleau/rust-syntax -%% rust-syntax NOTICES AND INFORMATION BEGIN HERE -========================================= MIT License Copyright (c) 2020 Dustin Pomerleau @@ -2330,11 +2350,13 @@ 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 rust-syntax NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +semver 5.5.0 - The ISC License +https://github.com/npm/node-semver -%% semver NOTICES AND INFORMATION BEGIN HERE -========================================= The ISC License Copyright (c) Isaac Z. Schlueter and Contributors @@ -2350,11 +2372,13 @@ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -========================================= -END OF semver NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +seti-ui 0.1.0 +https://github.com/jesseweed/seti-ui -%% seti-ui NOTICES AND INFORMATION BEGIN HERE -========================================= Copyright (c) 2014 Jesse Weed Permission is hereby granted, free of charge, to any person obtaining @@ -2375,11 +2399,13 @@ 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 seti-ui NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +shaders-tmLanguage 0.1.0 - MIT +https://github.com/tgjones/shaders-tmLanguage -%% shaders-tmLanguage NOTICES AND INFORMATION BEGIN HERE -========================================= MIT License Copyright (c) 2017 Tim Jones @@ -2401,11 +2427,13 @@ 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 shaders-tmLanguage NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +sumneko/lua.tmbundle 1.0.0 - TextMate Bundle License +https://github.com/sumneko/lua.tmbundle -%% sumneko/lua.tmbundle NOTICES AND INFORMATION BEGIN HERE -========================================= Copyright (c) sumneko-lua.tmbundle project authors If not otherwise specified (see below), files in this repository fall under the following license: @@ -2419,11 +2447,13 @@ An exception is made for files in readable text which contain their own license or files where an accompanying file exists (in the same directory) with a "-license" suffix added to the base-name name of the original file, and an extension of txt, html, or similar. For example "tidy" is accompanied by "tidy-license.txt". -========================================= -END OF sumneko/lua.tmbundle NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +textmate/asp.vb.net.tmbundle 0.0.0 - TextMate Bundle License +https://github.com/textmate/asp.vb.net.tmbundle -%% textmate/asp.vb.net.tmbundle NOTICES AND INFORMATION BEGIN HERE -========================================= Copyright (c) textmate-asp.vb.net.tmbundle project authors If not otherwise specified (see below), files in this folder fall under the following license: @@ -2437,11 +2467,13 @@ An exception is made for files in readable text which contain their own license or files where an accompanying file exists (in the same directory) with a "-license" suffix added to the base-name name of the original file, and an extension of txt, html, or similar. For example "tidy" is accompanied by "tidy-license.txt". -========================================= -END OF textmate/asp.vb.net.tmbundle NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +textmate/c.tmbundle 0.0.0 - TextMate Bundle License +https://github.com/textmate/c.tmbundle -%% textmate/c.tmbundle NOTICES AND INFORMATION BEGIN HERE -========================================= Copyright (c) textmate-c.tmbundle authors If not otherwise specified (see below), files in this repository fall under the following license: @@ -2455,11 +2487,13 @@ An exception is made for files in readable text which contain their own license or files where an accompanying file exists (in the same directory) with a "-license" suffix added to the base-name name of the original file, and an extension of txt, html, or similar. For example "tidy" is accompanied by "tidy-license.txt". -========================================= -END OF textmate/c.tmbundle NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +textmate/diff.tmbundle 0.0.0 - TextMate Bundle License +https://github.com/textmate/diff.tmbundle -%% textmate/diff.tmbundle NOTICES AND INFORMATION BEGIN HERE -========================================= Copyright (c) textmate-diff.tmbundle project authors If not otherwise specified (see below), files in this repository fall under the following license: @@ -2473,11 +2507,13 @@ An exception is made for files in readable text which contain their own license or files where an accompanying file exists (in the same directory) with a "-license" suffix added to the base-name name of the original file, and an extension of txt, html, or similar. For example "tidy" is accompanied by "tidy-license.txt". -========================================= -END OF textmate/diff.tmbundle NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +textmate/git.tmbundle 0.0.0 - MIT +https://github.com/textmate/git.tmbundle -%% textmate/git.tmbundle NOTICES AND INFORMATION BEGIN HERE -========================================= The MIT License (MIT) Copyright (c) 2008 Tim Harper @@ -2500,11 +2536,13 @@ 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 textmate/git.tmbundle NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +textmate/groovy.tmbundle 0.0.0 - TextMate Bundle License +https://github.com/textmate/groovy.tmbundle -%% textmate/groovy.tmbundle NOTICES AND INFORMATION BEGIN HERE -========================================= Copyright (c) textmate-groovy.tmbundle project authors If not otherwise specified (see below), files in this repository fall under the following license: @@ -2518,11 +2556,13 @@ An exception is made for files in readable text which contain their own license or files where an accompanying file exists (in the same directory) with a "-license" suffix added to the base-name name of the original file, and an extension of txt, html, or similar. For example "tidy" is accompanied by "tidy-license.txt". -========================================= -END OF textmate/groovy.tmbundle NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +textmate/html.tmbundle 0.0.0 - TextMate Bundle License +https://github.com/textmate/html.tmbundle -%% textmate/html.tmbundle NOTICES AND INFORMATION BEGIN HERE -========================================= Copyright (c) textmate-html.tmbundle project authors If not otherwise specified (see below), files in this repository fall under the following license: @@ -2536,11 +2576,13 @@ An exception is made for files in readable text which contain their own license or files where an accompanying file exists (in the same directory) with a "-license" suffix added to the base-name name of the original file, and an extension of txt, html, or similar. For example "tidy" is accompanied by "tidy-license.txt". -========================================= -END OF textmate/html.tmbundle NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +textmate/ini.tmbundle 0.0.0 - TextMate Bundle License +https://github.com/textmate/ini.tmbundle -%% textmate/ini.tmbundle NOTICES AND INFORMATION BEGIN HERE -========================================= Copyright (c) textmate-ini.tmbundle project authors If not otherwise specified (see below), files in this folder fall under the following license: @@ -2554,11 +2596,13 @@ An exception is made for files in readable text which contain their own license or files where an accompanying file exists (in the same directory) with a "-license" suffix added to the base-name name of the original file, and an extension of txt, html, or similar. For example "tidy" is accompanied by "tidy-license.txt". -========================================= -END OF textmate/ini.tmbundle NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +textmate/javascript.tmbundle 0.0.0 - TextMate Bundle License +https://github.com/textmate/javascript.tmbundle -%% textmate/javascript.tmbundle NOTICES AND INFORMATION BEGIN HERE -========================================= Copyright (c) textmate-javascript.tmbundle project authors If not otherwise specified (see below), files in this repository fall under the following license: @@ -2572,11 +2616,13 @@ An exception is made for files in readable text which contain their own license or files where an accompanying file exists (in the same directory) with a "-license" suffix added to the base-name name of the original file, and an extension of txt, html, or similar. For example "tidy" is accompanied by "tidy-license.txt". -========================================= -END OF textmate/javascript.tmbundle NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +textmate/markdown.tmbundle 0.0.0 - TextMate Bundle License +https://github.com/textmate/markdown.tmbundle -%% textmate/markdown.tmbundle NOTICES AND INFORMATION BEGIN HERE -========================================= Copyright (c) markdown.tmbundle authors If not otherwise specified (see below), files in this repository fall under the following license: @@ -2590,11 +2636,13 @@ An exception is made for files in readable text which contain their own license or files where an accompanying file exists (in the same directory) with a "-license" suffix added to the base-name name of the original file, and an extension of txt, html, or similar. For example "tidy" is accompanied by "tidy-license.txt". -========================================= -END OF textmate/markdown.tmbundle NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +textmate/perl.tmbundle 0.0.0 - TextMate Bundle License +https://github.com/textmate/perl.tmbundle -%% textmate/perl.tmbundle NOTICES AND INFORMATION BEGIN HERE -========================================= Copyright (c) textmate-perl.tmbundle project authors If not otherwise specified (see below), files in this repository fall under the following license: @@ -2608,11 +2656,13 @@ An exception is made for files in readable text which contain their own license or files where an accompanying file exists (in the same directory) with a "-license" suffix added to the base-name name of the original file, and an extension of txt, html, or similar. For example "tidy" is accompanied by "tidy-license.txt". -========================================= -END OF textmate/perl.tmbundle NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +textmate/ruby.tmbundle 0.0.0 - TextMate Bundle License +https://github.com/textmate/ruby.tmbundle -%% textmate/ruby.tmbundle NOTICES AND INFORMATION BEGIN HERE -========================================= Copyright (c) textmate-ruby.tmbundle project authors If not otherwise specified (see below), files in this folder fall under the following license: @@ -2626,11 +2676,13 @@ An exception is made for files in readable text which contain their own license or files where an accompanying file exists (in the same directory) with a "-license" suffix added to the base-name name of the original file, and an extension of txt, html, or similar. For example "tidy" is accompanied by "tidy-license.txt". -========================================= -END OF textmate/ruby.tmbundle NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +textmate/yaml.tmbundle 0.0.0 - TextMate Bundle License +https://github.com/textmate/yaml.tmbundle -%% textmate/yaml.tmbundle NOTICES AND INFORMATION BEGIN HERE -========================================= Copyright (c) 2015 FichteFoll Permission is hereby granted, free of charge, to any person obtaining a copy @@ -2649,11 +2701,13 @@ 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 textmate/yaml.tmbundle NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +trond-snekvik/vscode-rst 1.5.1 - MIT +https://github.com/trond-snekvik/vscode-rst -%% trond-snekvik/vscode-rst NOTICES AND INFORMATION BEGIN HERE -========================================= The MIT License (MIT) Copyright 2021 Trond Snekvik @@ -2663,11 +2717,14 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of 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 trond-snekvik/vscode-rst NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +TypeScript-TmLanguage 0.1.8 - MIT +TypeScript-TmLanguage 1.0.0 - MIT +https://github.com/microsoft/TypeScript-TmLanguage -%% TypeScript-TmLanguage NOTICES AND INFORMATION BEGIN HERE -========================================= Copyright (c) Microsoft Corporation All rights reserved. @@ -2690,11 +2747,13 @@ 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 TypeScript-TmLanguage NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +Unicode 12.0.0 - UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE +https://home.unicode.org/ -%% Unicode NOTICES AND INFORMATION BEGIN HERE -========================================= Unicode Data Files include all data files under the directories https://www.unicode.org/Public/, https://www.unicode.org/reports/, https://cldr.unicode.org, https://github.com/unicode-org/icu, and @@ -2750,11 +2809,13 @@ Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in these Data Files or Software without prior written authorization of the copyright holder. -========================================= -END OF Unicode NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +vscode-codicons 0.0.14 - MIT and Creative Commons Attribution 4.0 +https://github.com/microsoft/vscode-codicons -%% vscode-codicons NOTICES AND INFORMATION BEGIN HERE -========================================= Attribution 4.0 International ======================================================================= @@ -3150,11 +3211,13 @@ the avoidance of doubt, this paragraph does not form part of the public licenses. Creative Commons may be contacted at creativecommons.org. -========================================= -END OF vscode-codicons NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +vscode-logfile-highlighter 2.15.0 - MIT +https://github.com/emilast/vscode-logfile-highlighter -%% vscode-logfile-highlighter NOTICES AND INFORMATION BEGIN HERE -========================================= The MIT License (MIT) Copyright (c) 2015 emilast @@ -3176,11 +3239,13 @@ 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-logfile-highlighter NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +vscode-swift 0.0.1 - MIT +https://github.com/owensd/vscode-swift -%% vscode-swift NOTICES AND INFORMATION BEGIN HERE -========================================= The MIT License (MIT) Copyright (c) 2015 David Owens II @@ -3201,11 +3266,13 @@ 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-swift NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +vscode-win32-app-container-tokens +https://github.com/microsoft/vscode-win32-app-container-tokens -%% vscode-win32-app-container-tokens NOTICES AND INFORMATION BEGIN HERE -========================================= MIT License Copyright (c) Microsoft Corporation. @@ -3227,11 +3294,13 @@ 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-win32-app-container-tokens NOTICES AND INFORMATION +--------------------------------------------------------- + +--------------------------------------------------------- + +Web Background Synchronization - Apache2 +https://github.com/WICG/background-sync -%% Web Background Synchronization NOTICES AND INFORMATION BEGIN HERE -========================================= Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -3433,5 +3502,4 @@ Apache License WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -========================================= -END OF Web Background Synchronization NOTICES AND INFORMATION \ No newline at end of file +--------------------------------------------------------- \ No newline at end of file diff --git a/build/azure-pipelines/cli/cli-compile-and-publish.yml b/build/azure-pipelines/cli/cli-compile-and-publish.yml new file mode 100644 index 00000000000..e635f51df8c --- /dev/null +++ b/build/azure-pipelines/cli/cli-compile-and-publish.yml @@ -0,0 +1,50 @@ +parameters: + - name: VSCODE_CLI_TARGET + type: string + - name: VSCODE_CLI_ARTIFACT + type: string + - name: VSCODE_CLI_ENV + type: object + default: {} + +steps: + - script: cargo build --release --target ${{ parameters.VSCODE_CLI_TARGET }} --bin=code-tunnel + displayName: Compile ${{ parameters.VSCODE_CLI_TARGET }} + workingDirectory: $(Build.SourcesDirectory)/cli + env: + VSCODE_CLI_VERSION: $(VSCODE_CLI_VERSION) + VSCODE_CLI_REMOTE_LICENSE_TEXT: $(VSCODE_CLI_REMOTE_LICENSE_TEXT) + VSCODE_CLI_REMOTE_LICENSE_PROMPT: $(VSCODE_CLI_REMOTE_LICENSE_PROMPT) + VSCODE_CLI_ASSET_NAME: ${{ parameters.VSCODE_CLI_ARTIFACT }} + VSCODE_CLI_AI_KEY: $(VSCODE_CLI_AI_KEY) + VSCODE_CLI_AI_ENDPOINT: $(VSCODE_CLI_AI_ENDPOINT) + ${{ each pair in parameters.VSCODE_CLI_ENV }}: + ${{ pair.key }}: ${{ pair.value }} + + - ${{ if or(contains(parameters.VSCODE_CLI_TARGET, '-windows-'), contains(parameters.VSCODE_CLI_TARGET, '-darwin')) }}: + - task: ArchiveFiles@2 + inputs: + ${{ if contains(parameters.VSCODE_CLI_TARGET, '-windows-') }}: + rootFolderOrFile: $(Build.SourcesDirectory)/cli/target/${{ parameters.VSCODE_CLI_TARGET }}/release/code-tunnel.exe + ${{ if contains(parameters.VSCODE_CLI_TARGET, '-darwin') }}: + rootFolderOrFile: $(Build.SourcesDirectory)/cli/target/${{ parameters.VSCODE_CLI_TARGET }}/release/code-tunnel + includeRootFolder: false + archiveType: zip + archiveFile: $(Build.ArtifactStagingDirectory)/${{ parameters.VSCODE_CLI_ARTIFACT }}.zip + + - publish: $(Build.ArtifactStagingDirectory)/${{ parameters.VSCODE_CLI_ARTIFACT }}.zip + artifact: ${{ parameters.VSCODE_CLI_ARTIFACT }} + displayName: Publish ${{ parameters.VSCODE_CLI_ARTIFACT }} artifact + + - ${{ else }}: + - task: ArchiveFiles@2 + inputs: + rootFolderOrFile: $(Build.SourcesDirectory)/cli/target/${{ parameters.VSCODE_CLI_TARGET }}/release/code-tunnel + includeRootFolder: false + archiveType: tar + tarCompression: gz + archiveFile: $(Build.ArtifactStagingDirectory)/${{ parameters.VSCODE_CLI_ARTIFACT }}.tar.gz + + - publish: $(Build.ArtifactStagingDirectory)/${{ parameters.VSCODE_CLI_ARTIFACT }}.tar.gz + artifact: ${{ parameters.VSCODE_CLI_ARTIFACT }} + displayName: Publish ${{ parameters.VSCODE_CLI_ARTIFACT }} artifact diff --git a/build/azure-pipelines/cli/cli-darwin-sign.yml b/build/azure-pipelines/cli/cli-darwin-sign.yml new file mode 100644 index 00000000000..b8f9e965133 --- /dev/null +++ b/build/azure-pipelines/cli/cli-darwin-sign.yml @@ -0,0 +1,40 @@ +parameters: + - name: VSCODE_CLI_ARTIFACTS + type: object + default: [] + +steps: + - task: UseDotNet@2 + inputs: + version: 2.x + + - task: EsrpClientTool@1 + displayName: Download ESRPClient + + - ${{ each target in parameters.VSCODE_CLI_ARTIFACTS }}: + - task: DownloadPipelineArtifact@2 + displayName: Download ${{ target }} + inputs: + artifact: ${{ target }} + path: $(Build.ArtifactStagingDirectory)/pkg/${{ target }} + + - script: | + set -e + node build/azure-pipelines/common/sign "$(esrpclient.toolpath)/$(esrpclient.toolname)" darwin-sign $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(Build.ArtifactStagingDirectory)/pkg "*.zip" + displayName: Codesign + + - script: | + set -e + node build/azure-pipelines/common/sign "$(esrpclient.toolpath)/$(esrpclient.toolname)" darwin-notarize $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(Build.ArtifactStagingDirectory)/pkg "*.zip" + displayName: Notarize + + - ${{ each target in parameters.VSCODE_CLI_ARTIFACTS }}: + - script: | + set -e + ASSET_ID=$(echo "${{ target }}" | sed "s/unsigned_//") + mv $(Build.ArtifactStagingDirectory)/pkg/${{ target }}/${{ target }}.zip $(Build.ArtifactStagingDirectory)/pkg/${{ target }}/$ASSET_ID.zip + echo "##vso[task.setvariable variable=ASSET_ID]$ASSET_ID" + displayName: Set asset id variable + + - publish: $(Build.ArtifactStagingDirectory)/pkg/${{ target }}/$(ASSET_ID).zip + artifact: $(ASSET_ID) diff --git a/build/azure-pipelines/cli/cli-win32-sign.yml b/build/azure-pipelines/cli/cli-win32-sign.yml new file mode 100644 index 00000000000..91e069617d8 --- /dev/null +++ b/build/azure-pipelines/cli/cli-win32-sign.yml @@ -0,0 +1,65 @@ +parameters: + - name: VSCODE_CLI_ARTIFACTS + type: object + default: [] + +steps: + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode + SecretsFilter: "ESRP-PKI,esrp-aad-username,esrp-aad-password" + + - task: UseDotNet@2 + displayName: "Use .NET" + inputs: + version: 3.x + + - task: EsrpClientTool@1 + displayName: "Use ESRP client" + + - ${{ each target in parameters.VSCODE_CLI_ARTIFACTS }}: + - task: DownloadPipelineArtifact@2 + displayName: Download artifacts + inputs: + artifact: ${{ target }} + path: $(Build.ArtifactStagingDirectory)/pkg/${{ target }} + + - task: ExtractFiles@1 + inputs: + archiveFilePatterns: $(Build.ArtifactStagingDirectory)/pkg/${{ target }}/*.zip + destinationFolder: $(Build.ArtifactStagingDirectory)/sign/${{ target }} + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $EsrpClientTool = (gci -directory -filter EsrpClientTool_* $(Agent.RootDirectory)\_tasks | Select-Object -last 1).FullName + $EsrpCliZip = (gci -recurse -filter esrpcli.*.zip $EsrpClientTool | Select-Object -last 1).FullName + mkdir -p $(Agent.TempDirectory)\esrpcli + Expand-Archive -Path $EsrpCliZip -DestinationPath $(Agent.TempDirectory)\esrpcli + $EsrpCliDllPath = (gci -recurse -filter esrpcli.dll $(Agent.TempDirectory)\esrpcli | Select-Object -last 1).FullName + echo "##vso[task.setvariable variable=EsrpCliDllPath]$EsrpCliDllPath" + displayName: Find ESRP CLI + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { node build\azure-pipelines\common\sign $env:EsrpCliDllPath windows $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(Build.ArtifactStagingDirectory)/sign "*.exe" } + displayName: "Code sign" + + - ${{ each target in parameters.VSCODE_CLI_ARTIFACTS }}: + - powershell: | + $ASSET_ID = "${{ target }}".replace("unsigned_", ""); + echo "##vso[task.setvariable variable=ASSET_ID]$ASSET_ID" + displayName: Set asset id variable + + - task: ArchiveFiles@2 + inputs: + rootFolderOrFile: $(Build.ArtifactStagingDirectory)/sign/${{ target }}/code-tunnel.exe + includeRootFolder: false + archiveType: zip + archiveFile: $(Build.ArtifactStagingDirectory)/$(ASSET_ID).zip + + - publish: $(Build.ArtifactStagingDirectory)/$(ASSET_ID).zip + artifact: $(ASSET_ID) diff --git a/build/azure-pipelines/cli/compile-linux.yml b/build/azure-pipelines/cli/compile-linux.yml deleted file mode 100644 index 18f0700a582..00000000000 --- a/build/azure-pipelines/cli/compile-linux.yml +++ /dev/null @@ -1,52 +0,0 @@ -parameters: - - name: VSCODE_CLI_TARGETS - default: [] - type: object - - name: VSCODE_CLI_DIR - type: string - default: './' - - name: VSCODE_CLI_BINARY_NAME - type: string - - name: VSCODE_QUALITY - type: string - - name: channel - type: string - default: stable - -steps: - # inspired by: https://github.com/emk/rust-musl-builder/blob/main/Dockerfile - - bash: | - sudo apt-get update - sudo apt-get install -yq build-essential cmake curl file git graphviz musl-dev musl-tools linux-libc-dev pkgconf unzip xutils-dev - sudo ln -s "/usr/bin/g++" "/usr/bin/musl-g++" || echo "link exists" - displayName: Install build dependencies - - - template: ./install-rust.yml - parameters: - targets: ${{ parameters.VSCODE_CLI_TARGETS }} - channel: ${{ parameters.channel }} - - - template: ./cli/prepare.yml - parameters: - VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_IS_POSIX: true - - - ${{ each target in parameters.VSCODE_CLI_TARGETS }}: - - script: cargo build --release --target ${{ target.target }} --bin=${{ parameters.VSCODE_CLI_BINARY_NAME }} - displayName: Compile ${{ target.artifact }} - workingDirectory: ${{ parameters.VSCODE_CLI_DIR }} - env: - VSCODE_CLI_VERSION: $(VSCODE_CLI_VERSION) - VSCODE_CLI_REMOTE_LICENSE_TEXT: $(VSCODE_CLI_REMOTE_LICENSE_TEXT) - VSCODE_CLI_REMOTE_LICENSE_PROMPT: $(VSCODE_CLI_REMOTE_LICENSE_PROMPT) - VSCODE_CLI_ASSET_NAME: ${{ target.artifact }} - VSCODE_CLI_AI_KEY: $(VSCODE_CLI_AI_KEY) - VSCODE_CLI_AI_ENDPOINT: $(VSCODE_CLI_AI_ENDPOINT) - VSCODE_CLI_COMMIT: $(VSCODE_CLI_COMMIT) - VSCODE_CLI_QUALITY: $(VSCODE_QUALITY) - CXX_aarch64-unknown-linux-musl: musl-g++ - CC_aarch64-unknown-linux-musl: musl-gcc - - - publish: ${{ parameters.VSCODE_CLI_DIR }}/target/${{ target.target }}/release/${{ parameters.VSCODE_CLI_BINARY_NAME }} - artifact: ${{ target.artifact }} - displayName: Publish ${{ target.artifact }} artifact diff --git a/build/azure-pipelines/cli/compile-macos.yml b/build/azure-pipelines/cli/compile-macos.yml deleted file mode 100644 index 4981a53e1db..00000000000 --- a/build/azure-pipelines/cli/compile-macos.yml +++ /dev/null @@ -1,43 +0,0 @@ -parameters: - - name: VSCODE_CLI_TARGETS - default: [] - type: object - - name: VSCODE_CLI_DIR - type: string - default: './' - - name: VSCODE_CLI_BINARY_NAME - type: string - - name: VSCODE_QUALITY - type: string - - name: channel - type: string - default: stable - -steps: - - template: ./install-rust.yml - parameters: - targets: ${{ parameters.VSCODE_CLI_TARGETS }} - channel: ${{ parameters.channel }} - - - template: ./cli/prepare.yml - parameters: - VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_IS_POSIX: true - - - ${{ each target in parameters.VSCODE_CLI_TARGETS }}: - - script: cargo build --release --target ${{ target.target }} --bin=${{ parameters.VSCODE_CLI_BINARY_NAME }} - displayName: Compile ${{ target.artifact }} - workingDirectory: ${{ parameters.VSCODE_CLI_DIR }} - env: - VSCODE_CLI_VERSION: $(VSCODE_CLI_VERSION) - VSCODE_CLI_REMOTE_LICENSE_TEXT: $(VSCODE_CLI_REMOTE_LICENSE_TEXT) - VSCODE_CLI_REMOTE_LICENSE_PROMPT: $(VSCODE_CLI_REMOTE_LICENSE_PROMPT) - VSCODE_CLI_ASSET_NAME: ${{ target.artifact }} - VSCODE_CLI_AI_KEY: $(VSCODE_CLI_AI_KEY) - VSCODE_CLI_AI_ENDPOINT: $(VSCODE_CLI_AI_ENDPOINT) - VSCODE_CLI_COMMIT: $(VSCODE_CLI_COMMIT) - VSCODE_CLI_QUALITY: $(VSCODE_QUALITY) - - - publish: ${{ parameters.VSCODE_CLI_DIR }}/target/${{ target.target }}/release/${{ parameters.VSCODE_CLI_BINARY_NAME }} - artifact: ${{ target.artifact }} - displayName: Publish ${{ target.artifact }} artifact diff --git a/build/azure-pipelines/cli/compile-windows.yml b/build/azure-pipelines/cli/compile-windows.yml deleted file mode 100644 index 03c30726f11..00000000000 --- a/build/azure-pipelines/cli/compile-windows.yml +++ /dev/null @@ -1,49 +0,0 @@ -parameters: - - name: VSCODE_CLI_TARGETS - default: [] - type: object - - name: VSCODE_CLI_DIR - type: string - default: './' - - name: VSCODE_CLI_BINARY_NAME - type: string - - name: VSCODE_QUALITY - type: string - - name: channel - type: string - default: stable - -steps: - - template: ./install-rust.yml - parameters: - targets: ${{ parameters.VSCODE_CLI_TARGETS }} - channel: ${{ parameters.channel }} - - - template: ./cli/prepare.yml - parameters: - VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_IS_POSIX: false - - - ${{ each target in parameters.VSCODE_CLI_TARGETS }}: - - script: cargo build --release --target ${{ target.target }} --bin=${{ parameters.VSCODE_CLI_BINARY_NAME }} - displayName: Compile ${{ target.artifact }} - workingDirectory: ${{ parameters.VSCODE_CLI_DIR }} - env: - VSCODE_CLI_VERSION: $(VSCODE_CLI_VERSION) - VSCODE_CLI_REMOTE_LICENSE_TEXT: $(VSCODE_CLI_REMOTE_LICENSE_TEXT) - VSCODE_CLI_REMOTE_LICENSE_PROMPT: $(VSCODE_CLI_REMOTE_LICENSE_PROMPT) - VSCODE_CLI_ASSET_NAME: ${{ target.artifact }} - VSCODE_CLI_AI_KEY: $(VSCODE_CLI_AI_KEY) - VSCODE_CLI_AI_ENDPOINT: $(VSCODE_CLI_AI_ENDPOINT) - VSCODE_CLI_COMMIT: $(VSCODE_CLI_COMMIT) - VSCODE_CLI_QUALITY: $(VSCODE_QUALITY) - ${{ if eq(target, 'x86_64-pc-windows-msvc') }}: - OPENSSL_LIB_DIR: $(Build.ArtifactStagingDirectory)/deps/x64-windows-static-md/lib - OPENSSL_INCLUDE_DIR: $(Build.ArtifactStagingDirectory)/deps/x64-windows-static-md/include - ${{ if eq(target, 'aarch64-pc-windows-msvc') }}: - OPENSSL_LIB_DIR: $(Build.ArtifactStagingDirectory)/deps/arm64-windows-static-md/lib - OPENSSL_INCLUDE_DIR: $(Build.ArtifactStagingDirectory)/deps/arm64-windows-static-md/include - - - publish: ${{ parameters.VSCODE_CLI_DIR }}/target/${{ target.target }}/release/${{ parameters.VSCODE_CLI_BINARY_NAME }}.exe - artifact: ${{ target.artifact }} - displayName: Publish ${{ target.artifact }} artifact diff --git a/build/azure-pipelines/cli/install-rust.yml b/build/azure-pipelines/cli/install-rust-posix.yml similarity index 56% rename from build/azure-pipelines/cli/install-rust.yml rename to build/azure-pipelines/cli/install-rust-posix.yml index 7e70a293c6a..e32a8eedadf 100644 --- a/build/azure-pipelines/cli/install-rust.yml +++ b/build/azure-pipelines/cli/install-rust-posix.yml @@ -15,20 +15,10 @@ steps: echo "##vso[task.setvariable variable=PATH;]$PATH:$HOME/.cargo/bin" env: RUSTUP_TOOLCHAIN: ${{ parameters.channel }} - condition: not(eq(variables['Agent.OS'], 'Windows_NT')) displayName: "Install Rust" - script: | - curl -sSf -o rustup-init.exe https://win.rustup.rs - rustup-init.exe -y --profile minimal --default-toolchain %RUSTUP_TOOLCHAIN% --default-host x86_64-pc-windows-msvc - set PATH=%PATH%;%USERPROFILE%\.cargo\bin - echo "##vso[task.setvariable variable=PATH;]%PATH%;%USERPROFILE%\.cargo\bin" - env: - RUSTUP_TOOLCHAIN: ${{ parameters.channel }} - condition: eq(variables['Agent.OS'], 'Windows_NT') - displayName: "Install Rust" - - - bash: | + set -e rustup default $RUSTUP_TOOLCHAIN rustup update $RUSTUP_TOOLCHAIN env: @@ -36,10 +26,11 @@ steps: displayName: "Set Rust version" - ${{ each target in parameters.targets }}: - - script: rustup target add ${{ target.target }} - displayName: "🎯 Adding '${{ target.target }}'" + - script: rustup target add ${{ target }} + displayName: "Adding Rust target '${{ target }}'" - script: | + set -e rustc --version cargo --version rustup --version diff --git a/build/azure-pipelines/cli/install-rust-win32.yml b/build/azure-pipelines/cli/install-rust-win32.yml new file mode 100644 index 00000000000..fc23f40f1b6 --- /dev/null +++ b/build/azure-pipelines/cli/install-rust-win32.yml @@ -0,0 +1,38 @@ +parameters: + - name: channel + type: string + default: stable + - name: targets + default: [] + type: object + +# Todo: use 1ES pipeline once extension is installed in ADO + +steps: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + exec { curl -sSf -o rustup-init.exe https://win.rustup.rs } + exec { rustup-init.exe -y --profile minimal --default-toolchain %RUSTUP_TOOLCHAIN% --default-host x86_64-pc-windows-msvc } + echo "##vso[task.prependpath]$env:USERPROFILE\.cargo\bin" + env: + RUSTUP_TOOLCHAIN: ${{ parameters.channel }} + displayName: "Install Rust" + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + exec { rustup default $RUSTUP_TOOLCHAIN } + exec { rustup update $RUSTUP_TOOLCHAIN } + env: + RUSTUP_TOOLCHAIN: ${{ parameters.channel }} + displayName: "Set Rust version" + + - ${{ each target in parameters.targets }}: + - script: rustup target add ${{ target }} + displayName: "Adding Rust target '${{ target }}'" + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + exec { rustc --version } + exec { cargo --version } + exec { rustup --version } + displayName: "Check Rust versions" diff --git a/build/azure-pipelines/cli/prepare.js b/build/azure-pipelines/cli/prepare.js index fdaf2ef8753..d4c7a556f08 100644 --- a/build/azure-pipelines/cli/prepare.js +++ b/build/azure-pipelines/cli/prepare.js @@ -6,35 +6,11 @@ Object.defineProperty(exports, "__esModule", { value: true }); const getVersion_1 = require("../../lib/getVersion"); const fs = require("fs"); -const cp = require("child_process"); const path = require("path"); const packageJson = require("../../../package.json"); -const os_1 = require("os"); const root = path.dirname(path.dirname(path.dirname(__dirname))); -const cliPath = path.join(root, 'cli'); const product = JSON.parse(fs.readFileSync(path.join(root, 'product.json'), 'utf8')); const commit = (0, getVersion_1.getVersion)(root); -const getCargoLines = () => { - const fpath = path.join(cliPath, 'Cargo.toml'); - const cargo = fs.readFileSync(fpath, 'utf-8').split(/\r?\n/g); - return [fpath, cargo]; -}; -const addCargoDependency = (line) => { - const [fpath, cargo] = getCargoLines(); - const depsLine = cargo.findIndex(line => line.includes('[dependencies]')); - cargo.splice(depsLine + 1, 0, line); - fs.writeFileSync(fpath, cargo.join('\n')); -}; -const enableFeature = (feature) => { - const [fpath, cargo] = getCargoLines(); - const featuresLine = cargo.findIndex(line => line.includes('[features]')); - const prefix = 'default = '; - const defaultFeaturesLine = cargo.findIndex((line, i) => i > featuresLine && line.startsWith(prefix)); - const defaultFeatures = new Set(JSON.parse(cargo[defaultFeaturesLine].slice(prefix.length))); - defaultFeatures.add(feature); - cargo[defaultFeaturesLine] = prefix + JSON.stringify([...defaultFeatures]); - fs.writeFileSync(fpath, cargo.join('\n')); -}; /** * Sets build environment variables for the CLI for current contextual info. */ @@ -51,26 +27,6 @@ const setLauncherEnvironmentVars = () => { } } }; -/** - * Enables vscode-encrypt in the CLI if it's available in the current node_modules. - * This is not graceful since Cargo doesn't have a good way to do private or - * true-optional dependencies... - */ -const enableVscodeEncrypt = () => { - const dep = packageJson.dependencies['vscode-encrypt']; - if (!dep) { - return; - } - // If there's a vscode-encrypt in the package.json, install that (alone) in - // a temp dir for the build. This avoids having to do a full install of all - // node modules while building the CLI. - const stagingDir = path.join((0, os_1.tmpdir)(), `vscode-encrypt-staging-${Date.now()}`); - fs.mkdirSync(stagingDir); - fs.writeFileSync(path.join(stagingDir, 'package.json'), JSON.stringify({ dependencies: { 'vscode-encrypt': dep } })); - cp.execSync('yarn', { cwd: stagingDir, stdio: 'inherit' }); - const encryptPath = path.join(stagingDir, 'node_modules', 'vscode-encrypt', 'rs-pure'); - addCargoDependency(`vscode-encrypt = { "path" = "${encryptPath.replace(/\\/g, '/')}" }`); - enableFeature('vscode-encrypt'); -}; -setLauncherEnvironmentVars(); -enableVscodeEncrypt(); +if (require.main === module) { + setLauncherEnvironmentVars(); +} diff --git a/build/azure-pipelines/cli/prepare.ts b/build/azure-pipelines/cli/prepare.ts index fe08b56c84e..d30bce54b49 100644 --- a/build/azure-pipelines/cli/prepare.ts +++ b/build/azure-pipelines/cli/prepare.ts @@ -5,49 +5,24 @@ import { getVersion } from '../../lib/getVersion'; import * as fs from 'fs'; -import * as cp from 'child_process'; import * as path from 'path'; import * as packageJson from '../../../package.json'; -import { tmpdir } from 'os'; const root = path.dirname(path.dirname(path.dirname(__dirname))); -const cliPath = path.join(root, 'cli'); const product = JSON.parse(fs.readFileSync(path.join(root, 'product.json'), 'utf8')); const commit = getVersion(root); -const getCargoLines = () => { - const fpath = path.join(cliPath, 'Cargo.toml'); - const cargo = fs.readFileSync(fpath, 'utf-8').split(/\r?\n/g); - return [fpath, cargo] as const; -}; - -const addCargoDependency = (line: string) => { - const [fpath, cargo] = getCargoLines(); - const depsLine = cargo.findIndex(line => line.includes('[dependencies]')); - cargo.splice(depsLine + 1, 0, line); - fs.writeFileSync(fpath, cargo.join('\n')); -}; - -const enableFeature = (feature: string) => { - const [fpath, cargo] = getCargoLines(); - const featuresLine = cargo.findIndex(line => line.includes('[features]')); - - const prefix = 'default = '; - const defaultFeaturesLine = cargo.findIndex((line, i) => i > featuresLine && line.startsWith(prefix)); - const defaultFeatures = new Set(JSON.parse(cargo[defaultFeaturesLine].slice(prefix.length))); - defaultFeatures.add(feature); - cargo[defaultFeaturesLine] = prefix + JSON.stringify([...defaultFeatures]); - fs.writeFileSync(fpath, cargo.join('\n')); -}; - /** * Sets build environment variables for the CLI for current contextual info. */ const setLauncherEnvironmentVars = () => { const vars = new Map([ - ['VSCODE_CLI_REMOTE_LICENSE_TEXT', product.serverLicense], + ['VSCODE_CLI_REMOTE_LICENSE_TEXT', product.serverLicense?.join('\r\n')], ['VSCODE_CLI_REMOTE_LICENSE_PROMPT', product.serverLicensePrompt], + ['VSCODE_CLI_AI_KEY', product.aiConfig?.cliKey], + ['VSCODE_CLI_AI_ENDPOINT', product.aiConfig?.cliEndpoint], ['VSCODE_CLI_VERSION', packageJson.version], + ['VSCODE_CLI_QUALIY', product.quality], ['VSCODE_CLI_COMMIT', commit], ]); @@ -58,29 +33,6 @@ const setLauncherEnvironmentVars = () => { } }; -/** - * Enables vscode-encrypt in the CLI if it's available in the current node_modules. - * This is not graceful since Cargo doesn't have a good way to do private or - * true-optional dependencies... - */ -const enableVscodeEncrypt = () => { - const dep = (packageJson.dependencies as Record)['vscode-encrypt']; - if (!dep) { - return; - } - - // If there's a vscode-encrypt in the package.json, install that (alone) in - // a temp dir for the build. This avoids having to do a full install of all - // node modules while building the CLI. - const stagingDir = path.join(tmpdir(), `vscode-encrypt-staging-${Date.now()}`); - fs.mkdirSync(stagingDir); - fs.writeFileSync(path.join(stagingDir, 'package.json'), JSON.stringify({ dependencies: { 'vscode-encrypt': dep } })); - cp.execSync('yarn', { cwd: stagingDir, stdio: 'inherit' }); - const encryptPath = path.join(stagingDir, 'node_modules', 'vscode-encrypt', 'rs-pure'); - - addCargoDependency(`vscode-encrypt = { "path" = "${encryptPath.replace(/\\/g, '/')}" }`); - enableFeature('vscode-encrypt'); -}; - -setLauncherEnvironmentVars(); -enableVscodeEncrypt(); +if (require.main === module) { + setLauncherEnvironmentVars(); +} diff --git a/build/azure-pipelines/cli/prepare.yml b/build/azure-pipelines/cli/prepare.yml deleted file mode 100644 index 27bff9f2b4d..00000000000 --- a/build/azure-pipelines/cli/prepare.yml +++ /dev/null @@ -1,35 +0,0 @@ -parameters: - - name: VSCODE_IS_POSIX - type: boolean - - name: VSCODE_QUALITY - type: string - -steps: - - task: NodeTool@0 - inputs: - versionSpec: "16.x" - - - ${{ if eq(parameters.VSCODE_IS_POSIX, true) }}: - - template: ../mixin-distro-posix.yml - parameters: - VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - - - script: | - set -e - node build/azure-pipelines/cli/prepare.js - displayName: Prepare CLI build - env: - GITHUB_TOKEN: "$(github-distro-mixin-password)" - - - ${{ if ne(parameters.VSCODE_IS_POSIX, true) }}: - - template: ../mixin-distro-win32.yml - parameters: - VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { node build/azure-pipelines/cli/prepare.js } - displayName: Prepare CLI build - env: - GITHUB_TOKEN: "$(github-distro-mixin-password)" diff --git a/build/azure-pipelines/cli/test.yml b/build/azure-pipelines/cli/test.yml index 38db2f166b8..70eb714c1d9 100644 --- a/build/azure-pipelines/cli/test.yml +++ b/build/azure-pipelines/cli/test.yml @@ -2,17 +2,12 @@ parameters: - name: VSCODE_CLI_TARGETS default: [] type: object - - name: VSCODE_CLI_DIR - type: string - default: './' - - name: VSCODE_CLI_BINARY_NAME - type: string - name: VSCODE_CLI_RUST_CHANNEL type: string default: stable steps: - - template: ./install-rust.yml + - template: ./install-rust-posix.yml parameters: targets: [] channel: ${{ parameters.VSCODE_CLI_RUST_CHANNEL }} diff --git a/build/azure-pipelines/common/createAsset.js b/build/azure-pipelines/common/createAsset.js index 2117dbd27ad..10f1503f666 100644 --- a/build/azure-pipelines/common/createAsset.js +++ b/build/azure-pipelines/common/createAsset.js @@ -15,7 +15,6 @@ if (process.argv.length !== 8) { console.error('Usage: node createAsset.js PRODUCT OS ARCH TYPE NAME FILE'); process.exit(-1); } -const Ignore = Symbol('Ignore'); // Contains all of the logic for mapping details to our actual product names in CosmosDB function getPlatform(product, os, arch, type) { switch (os) { @@ -45,7 +44,7 @@ function getPlatform(product, os, arch, type) { } return arch === 'ia32' ? 'server-win32-web' : `server-win32-${arch}-web`; case 'cli': - return type === 'cli-unsigned' ? Ignore : `cli-win32-${arch}`; + return `cli-win32-${arch}`; default: throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); } @@ -79,7 +78,7 @@ function getPlatform(product, os, arch, type) { return `linux-deb-${arch}`; case 'rpm-package': return `linux-rpm-${arch}`; - case 'cli-unsigned': + case 'cli': return `cli-linux-${arch}`; default: throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); @@ -102,7 +101,7 @@ function getPlatform(product, os, arch, type) { } return `server-darwin-${arch}-web`; case 'cli': - return type === 'cli-unsigned' ? Ignore : `cli-darwin-${arch}`; + return `cli-darwin-${arch}`; default: throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); } @@ -142,9 +141,6 @@ async function main() { const [, , product, os, arch, unprocessedType, fileName, filePath] = process.argv; // getPlatform needs the unprocessedType const platform = getPlatform(product, os, arch, unprocessedType); - if (platform === Ignore) { - return; - } const type = getRealType(unprocessedType); const quality = getEnv('VSCODE_QUALITY'); const commit = process.env['VSCODE_DISTRO_COMMIT'] || getEnv('BUILD_SOURCEVERSION'); diff --git a/build/azure-pipelines/common/createAsset.ts b/build/azure-pipelines/common/createAsset.ts index c9a9449e36d..139338dd24b 100644 --- a/build/azure-pipelines/common/createAsset.ts +++ b/build/azure-pipelines/common/createAsset.ts @@ -28,10 +28,8 @@ if (process.argv.length !== 8) { process.exit(-1); } -const Ignore = Symbol('Ignore'); - // Contains all of the logic for mapping details to our actual product names in CosmosDB -function getPlatform(product: string, os: string, arch: string, type: string): string | typeof Ignore { +function getPlatform(product: string, os: string, arch: string, type: string): string { switch (os) { case 'win32': switch (product) { @@ -59,7 +57,7 @@ function getPlatform(product: string, os: string, arch: string, type: string): s } return arch === 'ia32' ? 'server-win32-web' : `server-win32-${arch}-web`; case 'cli': - return type === 'cli-unsigned' ? Ignore : `cli-win32-${arch}`; + return `cli-win32-${arch}`; default: throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); } @@ -93,7 +91,7 @@ function getPlatform(product: string, os: string, arch: string, type: string): s return `linux-deb-${arch}`; case 'rpm-package': return `linux-rpm-${arch}`; - case 'cli-unsigned': + case 'cli': return `cli-linux-${arch}`; default: throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); @@ -116,7 +114,7 @@ function getPlatform(product: string, os: string, arch: string, type: string): s } return `server-darwin-${arch}-web`; case 'cli': - return type === 'cli-unsigned' ? Ignore : `cli-darwin-${arch}`; + return `cli-darwin-${arch}`; default: throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); } @@ -163,10 +161,6 @@ async function main(): Promise { const [, , product, os, arch, unprocessedType, fileName, filePath] = process.argv; // getPlatform needs the unprocessedType const platform = getPlatform(product, os, arch, unprocessedType); - if (platform === Ignore) { - return; - } - const type = getRealType(unprocessedType); const quality = getEnv('VSCODE_QUALITY'); const commit = process.env['VSCODE_DISTRO_COMMIT'] || getEnv('BUILD_SOURCEVERSION'); diff --git a/build/azure-pipelines/common/releaseBuild.js b/build/azure-pipelines/common/releaseBuild.js index 9e96781eb67..eba9b2dc8b7 100644 --- a/build/azure-pipelines/common/releaseBuild.js +++ b/build/azure-pipelines/common/releaseBuild.js @@ -28,22 +28,25 @@ async function getConfig(client, quality) { } return res.resources[0]; } -async function main() { +async function main(force) { const commit = process.env['VSCODE_DISTRO_COMMIT'] || getEnv('BUILD_SOURCEVERSION'); const quality = getEnv('VSCODE_QUALITY'); const aadCredentials = new identity_1.ClientSecretCredential(process.env['AZURE_TENANT_ID'], process.env['AZURE_CLIENT_ID'], process.env['AZURE_CLIENT_SECRET']); const client = new cosmos_1.CosmosClient({ endpoint: process.env['AZURE_DOCUMENTDB_ENDPOINT'], aadCredentials }); - const config = await getConfig(client, quality); - console.log('Quality config:', config); - if (config.frozen) { - console.log(`Skipping release because quality ${quality} is frozen.`); - return; + if (!force) { + const config = await getConfig(client, quality); + console.log('Quality config:', config); + if (config.frozen) { + console.log(`Skipping release because quality ${quality} is frozen.`); + return; + } } console.log(`Releasing build ${commit}...`); const scripts = client.database('builds').container(quality).scripts; await (0, retry_1.retry)(() => scripts.storedProcedure('releaseBuild').execute('', [commit])); } -main().then(() => { +const [, , force] = process.argv; +main(force === 'true').then(() => { console.log('Build successfully released'); process.exit(0); }, err => { diff --git a/build/azure-pipelines/common/releaseBuild.ts b/build/azure-pipelines/common/releaseBuild.ts index 3b163b6b619..68476cc2952 100644 --- a/build/azure-pipelines/common/releaseBuild.ts +++ b/build/azure-pipelines/common/releaseBuild.ts @@ -41,19 +41,22 @@ async function getConfig(client: CosmosClient, quality: string): Promise return res.resources[0] as Config; } -async function main(): Promise { +async function main(force: boolean): Promise { const commit = process.env['VSCODE_DISTRO_COMMIT'] || getEnv('BUILD_SOURCEVERSION'); const quality = getEnv('VSCODE_QUALITY'); const aadCredentials = new ClientSecretCredential(process.env['AZURE_TENANT_ID']!, process.env['AZURE_CLIENT_ID']!, process.env['AZURE_CLIENT_SECRET']!); const client = new CosmosClient({ endpoint: process.env['AZURE_DOCUMENTDB_ENDPOINT']!, aadCredentials }); - const config = await getConfig(client, quality); - console.log('Quality config:', config); + if (!force) { + const config = await getConfig(client, quality); - if (config.frozen) { - console.log(`Skipping release because quality ${quality} is frozen.`); - return; + console.log('Quality config:', config); + + if (config.frozen) { + console.log(`Skipping release because quality ${quality} is frozen.`); + return; + } } console.log(`Releasing build ${commit}...`); @@ -62,7 +65,9 @@ async function main(): Promise { await retry(() => scripts.storedProcedure('releaseBuild').execute('', [commit])); } -main().then(() => { +const [, , force] = process.argv; + +main(force === 'true').then(() => { console.log('Build successfully released'); process.exit(0); }, err => { diff --git a/build/azure-pipelines/darwin/cli-build-darwin.yml b/build/azure-pipelines/darwin/cli-build-darwin.yml new file mode 100644 index 00000000000..42632c280e6 --- /dev/null +++ b/build/azure-pipelines/darwin/cli-build-darwin.yml @@ -0,0 +1,46 @@ +parameters: + - name: VSCODE_QUALITY + type: string + - name: VSCODE_BUILD_MACOS + type: boolean + - name: VSCODE_BUILD_MACOS_ARM64 + type: boolean + - name: channel + type: string + default: stable + +steps: + - task: NodeTool@0 + inputs: + versionSpec: "16.x" + + - template: ../mixin-distro-posix.yml + parameters: + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + + - script: | + set -e + node build/azure-pipelines/cli/prepare.js + displayName: Prepare CLI build + env: + GITHUB_TOKEN: "$(github-distro-mixin-password)" + + - template: ../cli/install-rust-posix.yml + parameters: + targets: + - ${{ if eq(parameters.VSCODE_BUILD_MACOS, true) }}: + - x86_64-apple-darwin + - ${{ if eq(parameters.VSCODE_BUILD_MACOS_ARM64, true) }}: + - aarch64-apple-darwin + + - ${{ if eq(parameters.VSCODE_BUILD_MACOS, true) }}: + - template: ../cli/cli-compile-and-publish.yml + parameters: + VSCODE_CLI_TARGET: x86_64-apple-darwin + VSCODE_CLI_ARTIFACT: unsigned_vscode_cli_darwin_x64_cli + + - ${{ if eq(parameters.VSCODE_BUILD_MACOS_ARM64, true) }}: + - template: ../cli/cli-compile-and-publish.yml + parameters: + VSCODE_CLI_TARGET: aarch64-apple-darwin + VSCODE_CLI_ARTIFACT: unsigned_vscode_cli_darwin_arm64_cli diff --git a/build/azure-pipelines/darwin/product-build-darwin-cli-sign.yml b/build/azure-pipelines/darwin/product-build-darwin-cli-sign.yml index d116ddca091..4c29271f35a 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-cli-sign.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-cli-sign.yml @@ -1,9 +1,8 @@ parameters: - - name: VSCODE_CLI_BINARY_NAME - type: string - - name: VSCODE_CLI_ARTIFACTS - default: [] - type: object + - name: VSCODE_BUILD_MACOS + type: boolean + - name: VSCODE_BUILD_MACOS_ARM64 + type: boolean steps: - task: NodeTool@0 @@ -19,7 +18,7 @@ steps: - script: | set -e - npx https://aka.ms/enablesecurefeed standAlone + npm install https://aka.ms/enablesecurefeed --global && npm exec terrapinadotask -- standAlone timeoutInMinutes: 5 retryCountOnTaskFailure: 3 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) @@ -37,43 +36,10 @@ steps: done displayName: Install build dependencies - - ${{ each target in parameters.VSCODE_CLI_ARTIFACTS }}: - - task: DownloadPipelineArtifact@2 - displayName: Download ${{ target }} - inputs: - artifact: ${{ target }} - path: $(Build.ArtifactStagingDirectory)/pkg/${{ target }} - - - script: | - set -e - mkdir -p $(agent.builddirectory)/signing - zip -j $(agent.builddirectory)/signing/${{ target }}.zip $(Build.ArtifactStagingDirectory)/pkg/${{ target }}/${{ parameters.VSCODE_CLI_BINARY_NAME }} - displayName: Prepare ${{ target }} archive - - - task: UseDotNet@2 - inputs: - version: 2.x - - - task: EsrpClientTool@1 - displayName: Download ESRPClient - - - script: | - set -e - node build/azure-pipelines/common/sign "$(esrpclient.toolpath)/$(esrpclient.toolname)" darwin-sign $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(agent.builddirectory)/signing "*.zip" - displayName: Codesign - - - script: | - set -e - node build/azure-pipelines/common/sign "$(esrpclient.toolpath)/$(esrpclient.toolname)" darwin-notarize $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(agent.builddirectory)/signing "*.zip" - displayName: Notarize - - - ${{ each target in parameters.VSCODE_CLI_ARTIFACTS }}: - - script: | - ASSET_ID=$(echo "${{ target }}" | sed "s/-unsigned//") - mkdir -p $(Build.ArtifactStagingDirectory)/final/${{ target }} - unzip $(agent.builddirectory)/signing/${{ target }}.zip -d $(Build.ArtifactStagingDirectory)/final/${{ target }} - echo "##vso[task.setvariable variable=ASSET_ID]$ASSET_ID" - displayName: Set asset id variable - - - publish: $(Build.ArtifactStagingDirectory)/final/${{ target }}/${{ parameters.VSCODE_CLI_BINARY_NAME }} - artifact: $(ASSET_ID) + - template: ../cli/cli-darwin-sign.yml + parameters: + VSCODE_CLI_ARTIFACTS: + - ${{ if eq(parameters.VSCODE_BUILD_MACOS, true) }}: + - unsigned_vscode_cli_darwin_x64_cli + - ${{ if eq(parameters.VSCODE_BUILD_MACOS_ARM64, true) }}: + - unsigned_vscode_cli_darwin_arm64_cli diff --git a/build/azure-pipelines/darwin/product-build-darwin-sign.yml b/build/azure-pipelines/darwin/product-build-darwin-sign.yml index a4ac3731104..c58b5ef1bb4 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-sign.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-sign.yml @@ -69,7 +69,7 @@ steps: - script: | set -e - npx https://aka.ms/enablesecurefeed standAlone + npm install https://aka.ms/enablesecurefeed --global && npm exec terrapinadotask -- standAlone timeoutInMinutes: 5 retryCountOnTaskFailure: 3 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) diff --git a/build/azure-pipelines/darwin/product-build-darwin-universal.yml b/build/azure-pipelines/darwin/product-build-darwin-universal.yml index ebc7104d6ce..393308f99b5 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-universal.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-universal.yml @@ -69,7 +69,7 @@ steps: - script: | set -e - npx https://aka.ms/enablesecurefeed standAlone + npm install https://aka.ms/enablesecurefeed --global && npm exec terrapinadotask -- standAlone timeoutInMinutes: 5 retryCountOnTaskFailure: 3 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index 6f9d9f6260b..7ccd0344d2c 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -9,71 +9,67 @@ parameters: type: boolean - name: VSCODE_RUN_SMOKE_TESTS type: boolean - - name: VSCODE_CLI_ARTIFACT - type: string - default: '' - - name: VSCODE_CLI_BINARY_NAME - type: string - default: '' + - name: VSCODE_BUILD_TUNNEL_CLI + type: boolean steps: - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: - - checkout: self - fetchDepth: 1 - retryCountOnTaskFailure: 3 + - checkout: self + fetchDepth: 1 + retryCountOnTaskFailure: 3 - task: NodeTool@0 inputs: versionSpec: "16.x" - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - task: AzureKeyVault@1 - displayName: "Azure Key Vault: Get Secrets" - inputs: - azureSubscription: "vscode-builds-subscription" - KeyVaultName: vscode - SecretsFilter: "github-distro-mixin-password,macos-developer-certificate,macos-developer-certificate-key" + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode + SecretsFilter: "github-distro-mixin-password,macos-developer-certificate,macos-developer-certificate-key" - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - task: DownloadPipelineArtifact@2 - inputs: - artifact: Compilation - path: $(Build.ArtifactStagingDirectory) - displayName: Download compilation output + - task: DownloadPipelineArtifact@2 + inputs: + artifact: Compilation + path: $(Build.ArtifactStagingDirectory) + displayName: Download compilation output - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - tar -xzf $(Build.ArtifactStagingDirectory)/compilation.tar.gz - displayName: Extract compilation output + - script: | + set -e + tar -xzf $(Build.ArtifactStagingDirectory)/compilation.tar.gz + displayName: Extract compilation output - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - cat << EOF > ~/.netrc - machine github.com - login vscode - password $(github-distro-mixin-password) - EOF + - 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" - displayName: Prepare tooling + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" + displayName: Prepare tooling - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - git fetch https://github.com/$(VSCODE_MIXIN_REPO).git $VSCODE_DISTRO_REF - echo "##vso[task.setvariable variable=VSCODE_DISTRO_COMMIT;]$(git rev-parse FETCH_HEAD)" - git checkout FETCH_HEAD - condition: and(succeeded(), ne(variables.VSCODE_DISTRO_REF, ' ')) - displayName: Checkout override commit + - script: | + set -e + git fetch https://github.com/$(VSCODE_MIXIN_REPO).git $VSCODE_DISTRO_REF + echo "##vso[task.setvariable variable=VSCODE_DISTRO_COMMIT;]$(git rev-parse FETCH_HEAD)" + git checkout FETCH_HEAD + condition: and(succeeded(), ne(variables.VSCODE_DISTRO_REF, ' ')) + displayName: Checkout override commit - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") - displayName: Merge distro + - script: | + set -e + git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") + displayName: Merge distro - script: | mkdir -p .build @@ -102,7 +98,7 @@ steps: - script: | set -e - npx https://aka.ms/enablesecurefeed standAlone + npm install https://aka.ms/enablesecurefeed --global && npm exec terrapinadotask -- standAlone timeoutInMinutes: 5 retryCountOnTaskFailure: 3 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) @@ -144,40 +140,40 @@ steps: displayName: Create node_modules archive - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - # This script brings in the right resources (images, icons, etc) based on the quality (insiders, stable, exploration) - - script: | - set -e - node build/azure-pipelines/mixin - displayName: Mix in quality + # This script brings in the right resources (images, icons, etc) based on the quality (insiders, stable, exploration) + - script: | + set -e + node build/azure-pipelines/mixin + displayName: Mix in quality - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-darwin-$(VSCODE_ARCH)-min-ci - displayName: Build client + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-darwin-$(VSCODE_ARCH)-min-ci + displayName: Build client - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - node build/azure-pipelines/mixin --server - displayName: Mix in server quality + - script: | + set -e + node build/azure-pipelines/mixin --server + displayName: Mix in server quality - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-reh-darwin-$(VSCODE_ARCH)-min-ci - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-reh-web-darwin-$(VSCODE_ARCH)-min-ci - displayName: Build Server + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-reh-darwin-$(VSCODE_ARCH)-min-ci + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-reh-web-darwin-$(VSCODE_ARCH)-min-ci + displayName: Build Server - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp "transpile-client-swc" "transpile-extensions" - displayName: Transpile + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp "transpile-client-swc" "transpile-extensions" + displayName: Transpile - script: | set -e @@ -186,122 +182,133 @@ steps: echo "##vso[task.setvariable variable=APP_PATH]$APP_ROOT/$APP_NAME" displayName: Find application path - - ${{ if ne(parameters.VSCODE_CLI_ARTIFACT, '') }}: - - task: DownloadPipelineArtifact@2 - inputs: - artifact: ${{ parameters.VSCODE_CLI_ARTIFACT }} - patterns: '**' - path: $(APP_PATH)/Contents/Resources/app/bin - displayName: Download VS Code CLI + - ${{ if eq(parameters.VSCODE_BUILD_TUNNEL_CLI, true) }}: + - task: DownloadPipelineArtifact@2 + inputs: + artifact: unsigned_vscode_cli_darwin_arm64_cli + patterns: "**" + path: $(Build.ArtifactStagingDirectory)/cli + displayName: Download VS Code CLI + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'arm64')) - - script: | - set -e - chmod +x "$(APP_PATH)/Contents/Resources/app/bin/$(VSCODE_CLI_BINARY_NAME)" - if [ "$(VSCODE_QUALITY)" != "stable" ]; then - mv "$(APP_PATH)/Contents/Resources/app/bin/$(VSCODE_CLI_BINARY_NAME)" "$(APP_PATH)/Contents/Resources/app/bin/$(VSCODE_CLI_BINARY_NAME)-$(VSCODE_QUALITY)" - fi - displayName: Make CLI executable + - task: DownloadPipelineArtifact@2 + inputs: + artifact: unsigned_vscode_cli_darwin_x64_cli + patterns: "**" + path: $(Build.ArtifactStagingDirectory)/cli + displayName: Download VS Code CLI + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) + + - script: | + set -e + ARCHIVE_NAME=$(ls "$(Build.ArtifactStagingDirectory)/cli" | head -n 1) + unzip "$(Build.ArtifactStagingDirectory)/cli/$ARCHIVE_NAME" -d "$(APP_PATH)/Contents/Resources/app/bin" + chmod +x "$(APP_PATH)/Contents/Resources/app/bin/code-tunnel" + if [ "$(VSCODE_QUALITY)" != "stable" ]; then + mv "$(APP_PATH)/Contents/Resources/app/bin/code-tunnel" "$(APP_PATH)/Contents/Resources/app/bin/code-tunnel-$(VSCODE_QUALITY)" + fi + displayName: Make CLI executable - ${{ if or(eq(parameters.VSCODE_RUN_UNIT_TESTS, true), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true), eq(parameters.VSCODE_RUN_SMOKE_TESTS, true)) }}: - - template: product-build-darwin-test.yml - parameters: - VSCODE_QUALITY: ${{ parameters.VSCODE_QUALITY }} - VSCODE_RUN_UNIT_TESTS: ${{ parameters.VSCODE_RUN_UNIT_TESTS }} - VSCODE_RUN_INTEGRATION_TESTS: ${{ parameters.VSCODE_RUN_INTEGRATION_TESTS }} - VSCODE_RUN_SMOKE_TESTS: ${{ parameters.VSCODE_RUN_SMOKE_TESTS }} + - template: product-build-darwin-test.yml + parameters: + VSCODE_QUALITY: ${{ parameters.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: ${{ parameters.VSCODE_RUN_UNIT_TESTS }} + VSCODE_RUN_INTEGRATION_TESTS: ${{ parameters.VSCODE_RUN_INTEGRATION_TESTS }} + VSCODE_RUN_SMOKE_TESTS: ${{ parameters.VSCODE_RUN_SMOKE_TESTS }} - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: - # Setting hardened entitlements is a requirement for: - # * Apple notarization - # * Running tests on Big Sur (because Big Sur has additional security precautions) - - script: | - set -e - security create-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain - security default-keychain -s $(agent.tempdirectory)/buildagent.keychain - security unlock-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain - echo "$(macos-developer-certificate)" | base64 -D > $(agent.tempdirectory)/cert.p12 - security import $(agent.tempdirectory)/cert.p12 -k $(agent.tempdirectory)/buildagent.keychain -P "$(macos-developer-certificate-key)" -T /usr/bin/codesign - security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k pwd $(agent.tempdirectory)/buildagent.keychain - VSCODE_ARCH=$(VSCODE_ARCH) DEBUG=electron-osx-sign* node build/darwin/sign.js - displayName: Set Hardened Entitlements + # Setting hardened entitlements is a requirement for: + # * Apple notarization + # * Running tests on Big Sur (because Big Sur has additional security precautions) + - script: | + set -e + security create-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain + security default-keychain -s $(agent.tempdirectory)/buildagent.keychain + security unlock-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain + echo "$(macos-developer-certificate)" | base64 -D > $(agent.tempdirectory)/cert.p12 + security import $(agent.tempdirectory)/cert.p12 -k $(agent.tempdirectory)/buildagent.keychain -P "$(macos-developer-certificate-key)" -T /usr/bin/codesign + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k pwd $(agent.tempdirectory)/buildagent.keychain + VSCODE_ARCH=$(VSCODE_ARCH) DEBUG=electron-osx-sign* node build/darwin/sign.js + displayName: Set Hardened Entitlements - ${{ if and(eq(parameters.VSCODE_PUBLISH, true), eq(parameters.VSCODE_RUN_UNIT_TESTS, false), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: - - script: | - set -e - pushd $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) && zip -r -X -y $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH).zip * && popd - displayName: Archive build + - script: | + set -e + pushd $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) && zip -r -X -y $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH).zip * && popd + displayName: Archive build - ${{ if and(eq(parameters.VSCODE_PUBLISH, true), eq(parameters.VSCODE_RUN_UNIT_TESTS, false), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: - - script: | - set -e + - script: | + set -e - # package Remote Extension Host - pushd .. && mv vscode-reh-darwin-$(VSCODE_ARCH) vscode-server-darwin-$(VSCODE_ARCH) && zip -Xry vscode-server-darwin-$(VSCODE_ARCH).zip vscode-server-darwin-$(VSCODE_ARCH) && popd + # package Remote Extension Host + pushd .. && mv vscode-reh-darwin-$(VSCODE_ARCH) vscode-server-darwin-$(VSCODE_ARCH) && zip -Xry vscode-server-darwin-$(VSCODE_ARCH).zip vscode-server-darwin-$(VSCODE_ARCH) && popd - # package Remote Extension Host (Web) - pushd .. && mv vscode-reh-web-darwin-$(VSCODE_ARCH) vscode-server-darwin-$(VSCODE_ARCH)-web && zip -Xry vscode-server-darwin-$(VSCODE_ARCH)-web.zip vscode-server-darwin-$(VSCODE_ARCH)-web && popd - displayName: Prepare to publish servers + # package Remote Extension Host (Web) + pushd .. && mv vscode-reh-web-darwin-$(VSCODE_ARCH) vscode-server-darwin-$(VSCODE_ARCH)-web && zip -Xry vscode-server-darwin-$(VSCODE_ARCH)-web.zip vscode-server-darwin-$(VSCODE_ARCH)-web && popd + displayName: Prepare to publish servers - ${{ if and(eq(parameters.VSCODE_PUBLISH, true), eq(parameters.VSCODE_RUN_UNIT_TESTS, false), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: - - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 - displayName: Generate SBOM (client) - inputs: - BuildDropPath: $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) - PackageName: Visual Studio Code + - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 + displayName: Generate SBOM (client) + inputs: + BuildDropPath: $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) + PackageName: Visual Studio Code - ${{ if and(eq(parameters.VSCODE_PUBLISH, true), eq(parameters.VSCODE_RUN_UNIT_TESTS, false), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: - - publish: $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH)/_manifest - displayName: Publish SBOM (client) - artifact: vscode_client_darwin_$(VSCODE_ARCH)_sbom + - publish: $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH)/_manifest + displayName: Publish SBOM (client) + artifact: vscode_client_darwin_$(VSCODE_ARCH)_sbom - ${{ if and(eq(parameters.VSCODE_PUBLISH, true), eq(parameters.VSCODE_RUN_UNIT_TESTS, false), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: - - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 - displayName: Generate SBOM (server) - inputs: - BuildDropPath: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH) - PackageName: Visual Studio Code Server + - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 + displayName: Generate SBOM (server) + inputs: + BuildDropPath: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH) + PackageName: Visual Studio Code Server - ${{ if and(eq(parameters.VSCODE_PUBLISH, true), eq(parameters.VSCODE_RUN_UNIT_TESTS, false), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: - - publish: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH)/_manifest - displayName: Publish SBOM (server) - artifact: vscode_server_darwin_$(VSCODE_ARCH)_sbom + - publish: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH)/_manifest + displayName: Publish SBOM (server) + artifact: vscode_server_darwin_$(VSCODE_ARCH)_sbom - ${{ if and(eq(parameters.VSCODE_PUBLISH, true), eq(parameters.VSCODE_RUN_UNIT_TESTS, false), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: - - publish: $(Agent.BuildDirectory)/VSCode-darwin-$(VSCODE_ARCH).zip - artifact: unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive - displayName: Publish client archive + - publish: $(Agent.BuildDirectory)/VSCode-darwin-$(VSCODE_ARCH).zip + artifact: unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive + displayName: Publish client archive - ${{ if and(eq(parameters.VSCODE_PUBLISH, true), eq(parameters.VSCODE_RUN_UNIT_TESTS, false), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: - - publish: $(Agent.BuildDirectory)/vscode-server-darwin-$(VSCODE_ARCH).zip - artifact: vscode_server_darwin_$(VSCODE_ARCH)_archive-unsigned - displayName: Publish server archive + - publish: $(Agent.BuildDirectory)/vscode-server-darwin-$(VSCODE_ARCH).zip + artifact: vscode_server_darwin_$(VSCODE_ARCH)_archive-unsigned + displayName: Publish server archive - ${{ if and(eq(parameters.VSCODE_PUBLISH, true), eq(parameters.VSCODE_RUN_UNIT_TESTS, false), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: - - publish: $(Agent.BuildDirectory)/vscode-server-darwin-$(VSCODE_ARCH)-web.zip - artifact: vscode_web_darwin_$(VSCODE_ARCH)_archive-unsigned - displayName: Publish web server archive + - publish: $(Agent.BuildDirectory)/vscode-server-darwin-$(VSCODE_ARCH)-web.zip + artifact: vscode_web_darwin_$(VSCODE_ARCH)_archive-unsigned + displayName: Publish web server archive - ${{ if and(eq(parameters.VSCODE_PUBLISH, true), eq(parameters.VSCODE_RUN_UNIT_TESTS, false), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: - - task: AzureCLI@2 - inputs: - azureSubscription: "vscode-builds-subscription" - scriptType: pscore - scriptLocation: inlineScript - addSpnToEnvironment: true - inlineScript: | - Write-Host "##vso[task.setvariable variable=AZURE_TENANT_ID]$env:tenantId" - Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_ID]$env:servicePrincipalId" - Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_SECRET;issecret=true]$env:servicePrincipalKey" + - task: AzureCLI@2 + inputs: + azureSubscription: "vscode-builds-subscription" + scriptType: pscore + scriptLocation: inlineScript + addSpnToEnvironment: true + inlineScript: | + Write-Host "##vso[task.setvariable variable=AZURE_TENANT_ID]$env:tenantId" + Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_ID]$env:servicePrincipalId" + Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_SECRET;issecret=true]$env:servicePrincipalKey" - ${{ if and(eq(parameters.VSCODE_PUBLISH, true), eq(parameters.VSCODE_RUN_UNIT_TESTS, false), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: - - script: | - set -e - AZURE_STORAGE_ACCOUNT="ticino" \ - AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \ - AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ - AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ - VSCODE_ARCH="$(VSCODE_ARCH)" \ - node build/azure-pipelines/upload-configuration - displayName: Upload configuration (for Bing settings search) - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) - continueOnError: true + - script: | + set -e + AZURE_STORAGE_ACCOUNT="ticino" \ + AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \ + AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ + AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ + VSCODE_ARCH="$(VSCODE_ARCH)" \ + node build/azure-pipelines/upload-configuration + displayName: Upload configuration (for Bing settings search) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) + continueOnError: true diff --git a/build/azure-pipelines/linux/cli-build-linux.yml b/build/azure-pipelines/linux/cli-build-linux.yml new file mode 100644 index 00000000000..0bc62e28dad --- /dev/null +++ b/build/azure-pipelines/linux/cli-build-linux.yml @@ -0,0 +1,84 @@ +parameters: + - name: VSCODE_BUILD_LINUX_ALPINE + type: boolean + default: false + - name: VSCODE_BUILD_LINUX + type: boolean + default: false + - name: VSCODE_BUILD_LINUX_ALPINE_ARM64 + type: boolean + default: false + - name: VSCODE_BUILD_LINUX_ARM64 + type: boolean + default: false + - name: VSCODE_QUALITY + type: string + - name: channel + type: string + default: stable + +steps: + # inspired by: https://github.com/emk/rust-musl-builder/blob/main/Dockerfile + - bash: | + set -e + sudo apt-get update + sudo apt-get install -yq build-essential cmake curl file git graphviz musl-dev musl-tools linux-libc-dev pkgconf unzip xutils-dev + sudo ln -s "/usr/bin/g++" "/usr/bin/musl-g++" || echo "link exists" + displayName: Install build dependencies + + - task: NodeTool@0 + inputs: + versionSpec: "16.x" + + - template: ../mixin-distro-posix.yml + parameters: + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + + - script: | + set -e + node build/azure-pipelines/cli/prepare.js + displayName: Prepare CLI build + env: + GITHUB_TOKEN: "$(github-distro-mixin-password)" + + - template: ../cli/install-rust-posix.yml + parameters: + targets: + - ${{ if eq(parameters.VSCODE_BUILD_LINUX_ALPINE_ARM64, true) }}: + - aarch64-unknown-linux-musl + - ${{ if eq(parameters.VSCODE_BUILD_LINUX_ALPINE, true) }}: + - x86_64-unknown-linux-musl + - ${{ if eq(parameters.VSCODE_BUILD_LINUX_ARM64, true) }}: + - aarch64-unknown-linux-gnu + - ${{ if eq(parameters.VSCODE_BUILD_LINUX, true) }}: + - x86_64-unknown-linux-gnu + + - ${{ if eq(parameters.VSCODE_BUILD_LINUX_ALPINE_ARM64, true) }}: + - template: ../cli/cli-compile-and-publish.yml + parameters: + VSCODE_CLI_TARGET: aarch64-unknown-linux-musl + VSCODE_CLI_ARTIFACT: vscode_cli_alpine_arm64_cli + VSCODE_CLI_ENV: + CXX_aarch64-unknown-linux-musl: musl-g++ + CC_aarch64-unknown-linux-musl: musl-gcc + + - ${{ if eq(parameters.VSCODE_BUILD_LINUX_ALPINE, true) }}: + - template: ../cli/cli-compile-and-publish.yml + parameters: + VSCODE_CLI_TARGET: x86_64-unknown-linux-musl + VSCODE_CLI_ARTIFACT: vscode_cli_alpine_x64_cli + VSCODE_CLI_ENV: + CXX_aarch64-unknown-linux-musl: musl-g++ + CC_aarch64-unknown-linux-musl: musl-gcc + + - ${{ if eq(parameters.VSCODE_BUILD_LINUX_ARM64, true) }}: + - template: ../cli/cli-compile-and-publish.yml + parameters: + VSCODE_CLI_TARGET: aarch64-unknown-linux-gnu + VSCODE_CLI_ARTIFACT: vscode_cli_linux_arm64_cli + + - ${{ if eq(parameters.VSCODE_BUILD_LINUX, true) }}: + - template: ../cli/cli-compile-and-publish.yml + parameters: + VSCODE_CLI_TARGET: x86_64-unknown-linux-gnu + VSCODE_CLI_ARTIFACT: vscode_cli_linux_x64_cli diff --git a/build/azure-pipelines/linux/product-build-alpine.yml b/build/azure-pipelines/linux/product-build-alpine.yml index c60f16d0804..34f08bd48c7 100644 --- a/build/azure-pipelines/linux/product-build-alpine.yml +++ b/build/azure-pipelines/linux/product-build-alpine.yml @@ -82,7 +82,7 @@ steps: - script: | set -e - npx https://aka.ms/enablesecurefeed standAlone + npm install https://aka.ms/enablesecurefeed --global && npm exec terrapinadotask -- standAlone timeoutInMinutes: 5 retryCountOnTaskFailure: 3 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) diff --git a/build/azure-pipelines/linux/product-build-linux-client.yml b/build/azure-pipelines/linux/product-build-linux-client.yml index ddc7a478685..9b22c4b26e6 100644 --- a/build/azure-pipelines/linux/product-build-linux-client.yml +++ b/build/azure-pipelines/linux/product-build-linux-client.yml @@ -9,90 +9,86 @@ parameters: type: boolean - name: VSCODE_RUN_SMOKE_TESTS type: boolean - - name: VSCODE_CLI_ARTIFACT - type: string - default: '' - - name: VSCODE_CLI_BINARY_NAME - type: string - default: '' + - name: VSCODE_BUILD_TUNNEL_CLI + type: boolean steps: - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: - - checkout: self - fetchDepth: 1 - retryCountOnTaskFailure: 3 + - checkout: self + fetchDepth: 1 + retryCountOnTaskFailure: 3 - task: NodeTool@0 inputs: versionSpec: "16.x" - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - task: AzureKeyVault@1 - displayName: "Azure Key Vault: Get Secrets" - inputs: - azureSubscription: "vscode-builds-subscription" - KeyVaultName: vscode - SecretsFilter: "github-distro-mixin-password,ESRP-PKI,esrp-aad-username,esrp-aad-password" + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode + SecretsFilter: "github-distro-mixin-password,ESRP-PKI,esrp-aad-username,esrp-aad-password" - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - task: DownloadPipelineArtifact@2 - inputs: - artifact: Compilation - path: $(Build.ArtifactStagingDirectory) - displayName: Download compilation output + - task: DownloadPipelineArtifact@2 + inputs: + artifact: Compilation + path: $(Build.ArtifactStagingDirectory) + displayName: Download compilation output - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - task: DownloadPipelineArtifact@2 - inputs: - artifact: reh_node_modules-$(VSCODE_ARCH) - path: $(Build.ArtifactStagingDirectory) - displayName: Download server build dependencies - condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'armhf')) + - task: DownloadPipelineArtifact@2 + inputs: + artifact: reh_node_modules-$(VSCODE_ARCH) + path: $(Build.ArtifactStagingDirectory) + displayName: Download server build dependencies + condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'armhf')) - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - # Start X server - /etc/init.d/xvfb start - # Start dbus session - DBUS_LAUNCH_RESULT=$(sudo dbus-daemon --config-file=/usr/share/dbus-1/system.conf --print-address) - echo "##vso[task.setvariable variable=DBUS_SESSION_BUS_ADDRESS]$DBUS_LAUNCH_RESULT" - displayName: Setup system services - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) + - script: | + set -e + # Start X server + /etc/init.d/xvfb start + # Start dbus session + DBUS_LAUNCH_RESULT=$(sudo dbus-daemon --config-file=/usr/share/dbus-1/system.conf --print-address) + echo "##vso[task.setvariable variable=DBUS_SESSION_BUS_ADDRESS]$DBUS_LAUNCH_RESULT" + displayName: Setup system services + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - tar -xzf $(Build.ArtifactStagingDirectory)/compilation.tar.gz - displayName: Extract compilation output + - script: | + set -e + tar -xzf $(Build.ArtifactStagingDirectory)/compilation.tar.gz + displayName: Extract compilation output - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - cat << EOF > ~/.netrc - machine github.com - login vscode - password $(github-distro-mixin-password) - EOF + - 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" - displayName: Prepare tooling + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" + displayName: Prepare tooling - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - git fetch https://github.com/$(VSCODE_MIXIN_REPO).git $VSCODE_DISTRO_REF - echo "##vso[task.setvariable variable=VSCODE_DISTRO_COMMIT;]$(git rev-parse FETCH_HEAD)" - git checkout FETCH_HEAD - condition: and(succeeded(), ne(variables.VSCODE_DISTRO_REF, ' ')) - displayName: Checkout override commit + - script: | + set -e + git fetch https://github.com/$(VSCODE_MIXIN_REPO).git $VSCODE_DISTRO_REF + echo "##vso[task.setvariable variable=VSCODE_DISTRO_COMMIT;]$(git rev-parse FETCH_HEAD)" + git checkout FETCH_HEAD + condition: and(succeeded(), ne(variables.VSCODE_DISTRO_REF, ' ')) + displayName: Checkout override commit - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") - displayName: Merge distro + - script: | + set -e + git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") + displayName: Merge distro - script: | mkdir -p .build @@ -101,20 +97,20 @@ steps: displayName: Prepare yarn cache flags - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: - - task: Cache@2 - inputs: - key: "genericNodeModules | $(Agent.OS) | .build/yarnlockhash" - path: .build/node_modules_cache - cacheHitVar: NODE_MODULES_RESTORED - displayName: Restore node_modules cache + - task: Cache@2 + inputs: + key: "genericNodeModules | $(Agent.OS) | .build/yarnlockhash" + path: .build/node_modules_cache + cacheHitVar: NODE_MODULES_RESTORED + displayName: Restore node_modules cache - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - task: Cache@2 - inputs: - key: "nodeModules | $(Agent.OS) | .build/yarnlockhash" - path: .build/node_modules_cache - cacheHitVar: NODE_MODULES_RESTORED - displayName: Restore node_modules cache + - task: Cache@2 + inputs: + key: "nodeModules | $(Agent.OS) | .build/yarnlockhash" + path: .build/node_modules_cache + cacheHitVar: NODE_MODULES_RESTORED + displayName: Restore node_modules cache - task: Cache@2 inputs: @@ -130,7 +126,7 @@ steps: - script: | set -e - npx https://aka.ms/enablesecurefeed standAlone + npm install https://aka.ms/enablesecurefeed --global && npm exec terrapinadotask -- standAlone timeoutInMinutes: 5 retryCountOnTaskFailure: 3 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) @@ -208,12 +204,12 @@ steps: displayName: Download missing built-in extensions - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - rm -rf remote/node_modules - tar -xzf $(Build.ArtifactStagingDirectory)/reh_node_modules-$(VSCODE_ARCH).tar.gz --directory $(Build.SourcesDirectory)/remote - displayName: Extract server node_modules output - condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'armhf')) + - script: | + set -e + rm -rf remote/node_modules + tar -xzf $(Build.ArtifactStagingDirectory)/reh_node_modules-$(VSCODE_ARCH).tar.gz --directory $(Build.SourcesDirectory)/remote + displayName: Extract server node_modules output + condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'armhf')) - script: | set -e @@ -224,151 +220,160 @@ steps: displayName: Create node_modules archive - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - node build/azure-pipelines/mixin - displayName: Mix in quality + - script: | + set -e + node build/azure-pipelines/mixin + displayName: Mix in quality - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-linux-$(VSCODE_ARCH)-min-ci - displayName: Build + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-linux-$(VSCODE_ARCH)-min-ci + displayName: Build - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - node build/azure-pipelines/mixin --server - displayName: Mix in server quality + - script: | + set -e + node build/azure-pipelines/mixin --server + displayName: Mix in server quality - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-reh-linux-$(VSCODE_ARCH)-min-ci - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-reh-web-linux-$(VSCODE_ARCH)-min-ci - displayName: Build Server + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-reh-linux-$(VSCODE_ARCH)-min-ci + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-reh-web-linux-$(VSCODE_ARCH)-min-ci + displayName: Build Server - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp "transpile-client-swc" "transpile-extensions" - displayName: Transpile + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp "transpile-client-swc" "transpile-extensions" + displayName: Transpile - - ${{ if ne(parameters.VSCODE_CLI_ARTIFACT, '') }}: - - task: DownloadPipelineArtifact@2 - inputs: - artifact: ${{ parameters.VSCODE_CLI_ARTIFACT }} - patterns: '**' - path: $(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)/bin - displayName: Download VS Code CLI + - ${{ if eq(parameters.VSCODE_BUILD_TUNNEL_CLI, true) }}: + - task: DownloadPipelineArtifact@2 + inputs: + artifact: vscode_cli_linux_arm64_cli + patterns: "**" + path: $(Build.ArtifactStagingDirectory)/cli + displayName: Download VS Code CLI + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'arm64')) - - script: | - set -e - chmod +x "$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)/bin/$(VSCODE_CLI_BINARY_NAME)" - if [ "$(VSCODE_QUALITY)" != "stable" ]; then - mv "$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)/bin/$(VSCODE_CLI_BINARY_NAME)" "$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)/bin/$(VSCODE_CLI_BINARY_NAME)-$(VSCODE_QUALITY)" - fi - displayName: Make CLI executable + - task: DownloadPipelineArtifact@2 + inputs: + artifact: vscode_cli_linux_x64_cli + patterns: "**" + path: $(Build.ArtifactStagingDirectory)/cli + displayName: Download VS Code CLI + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) + + - script: | + set -e + tar -xzvf $(Build.ArtifactStagingDirectory)/cli/*.tar.gz -C $(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)/bin + if [ "$(VSCODE_QUALITY)" != "stable" ]; then + mv "$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)/bin/code-tunnel" "$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)/bin/code-tunnel-$(VSCODE_QUALITY)" + fi + displayName: Make CLI executable - ${{ if or(eq(parameters.VSCODE_RUN_UNIT_TESTS, true), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true), eq(parameters.VSCODE_RUN_SMOKE_TESTS, true)) }}: - - template: product-build-linux-client-test.yml - parameters: - VSCODE_QUALITY: ${{ parameters.VSCODE_QUALITY }} - VSCODE_RUN_UNIT_TESTS: ${{ parameters.VSCODE_RUN_UNIT_TESTS }} - VSCODE_RUN_INTEGRATION_TESTS: ${{ parameters.VSCODE_RUN_INTEGRATION_TESTS }} - VSCODE_RUN_SMOKE_TESTS: ${{ parameters.VSCODE_RUN_SMOKE_TESTS }} + - template: product-build-linux-client-test.yml + parameters: + VSCODE_QUALITY: ${{ parameters.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: ${{ parameters.VSCODE_RUN_UNIT_TESTS }} + VSCODE_RUN_INTEGRATION_TESTS: ${{ parameters.VSCODE_RUN_INTEGRATION_TESTS }} + VSCODE_RUN_SMOKE_TESTS: ${{ parameters.VSCODE_RUN_SMOKE_TESTS }} - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: - - script: | - set -e - yarn gulp "vscode-linux-$(VSCODE_ARCH)-build-deb" - yarn gulp "vscode-linux-$(VSCODE_ARCH)-build-rpm" - displayName: Build deb, rpm packages + - script: | + set -e + yarn gulp "vscode-linux-$(VSCODE_ARCH)-build-deb" + yarn gulp "vscode-linux-$(VSCODE_ARCH)-build-rpm" + displayName: Build deb, rpm packages - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: - - script: | - set -e - yarn gulp "vscode-linux-$(VSCODE_ARCH)-prepare-snap" - displayName: Prepare snap package + - script: | + set -e + yarn gulp "vscode-linux-$(VSCODE_ARCH)-prepare-snap" + displayName: Prepare snap package - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: - - task: UseDotNet@2 - inputs: - version: 2.x + - task: UseDotNet@2 + inputs: + version: 2.x - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: - - task: EsrpClientTool@1 - displayName: Download ESRPClient + - task: EsrpClientTool@1 + displayName: Download ESRPClient - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: - - script: | - set -e - node build/azure-pipelines/common/sign "$(esrpclient.toolpath)/$(esrpclient.toolname)" rpm $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) .build/linux/rpm '*.rpm' - displayName: Codesign rpm + - script: | + set -e + node build/azure-pipelines/common/sign "$(esrpclient.toolpath)/$(esrpclient.toolname)" rpm $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) .build/linux/rpm '*.rpm' + displayName: Codesign rpm - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: - - script: | - set -e - VSCODE_ARCH="$(VSCODE_ARCH)" \ - ./build/azure-pipelines/linux/prepare-publish.sh - displayName: Prepare for Publish + - script: | + set -e + VSCODE_ARCH="$(VSCODE_ARCH)" \ + ./build/azure-pipelines/linux/prepare-publish.sh + displayName: Prepare for Publish - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: - - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 - displayName: Generate SBOM (client) - inputs: - BuildDropPath: $(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) - PackageName: Visual Studio Code + - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 + displayName: Generate SBOM (client) + inputs: + BuildDropPath: $(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) + PackageName: Visual Studio Code - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: - - publish: $(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)/_manifest - displayName: Publish SBOM (client) - artifact: vscode_client_linux_$(VSCODE_ARCH)_sbom + - publish: $(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)/_manifest + displayName: Publish SBOM (client) + artifact: vscode_client_linux_$(VSCODE_ARCH)_sbom - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: - - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 - displayName: Generate SBOM (server) - inputs: - BuildDropPath: $(agent.builddirectory)/vscode-server-linux-$(VSCODE_ARCH) - PackageName: Visual Studio Code Server + - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 + displayName: Generate SBOM (server) + inputs: + BuildDropPath: $(agent.builddirectory)/vscode-server-linux-$(VSCODE_ARCH) + PackageName: Visual Studio Code Server - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: - - publish: $(agent.builddirectory)/vscode-server-linux-$(VSCODE_ARCH)/_manifest - displayName: Publish SBOM (server) - artifact: vscode_server_linux_$(VSCODE_ARCH)_sbom + - publish: $(agent.builddirectory)/vscode-server-linux-$(VSCODE_ARCH)/_manifest + displayName: Publish SBOM (server) + artifact: vscode_server_linux_$(VSCODE_ARCH)_sbom - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: - - publish: $(DEB_PATH) - artifact: vscode_client_linux_$(VSCODE_ARCH)_deb-package - displayName: Publish deb package + - publish: $(DEB_PATH) + artifact: vscode_client_linux_$(VSCODE_ARCH)_deb-package + displayName: Publish deb package - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: - - publish: $(RPM_PATH) - artifact: vscode_client_linux_$(VSCODE_ARCH)_rpm-package - displayName: Publish rpm package + - publish: $(RPM_PATH) + artifact: vscode_client_linux_$(VSCODE_ARCH)_rpm-package + displayName: Publish rpm package - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: - - publish: $(TARBALL_PATH) - artifact: vscode_client_linux_$(VSCODE_ARCH)_archive-unsigned - displayName: Publish client archive + - publish: $(TARBALL_PATH) + artifact: vscode_client_linux_$(VSCODE_ARCH)_archive-unsigned + displayName: Publish client archive - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: - - publish: $(Agent.BuildDirectory)/vscode-server-linux-$(VSCODE_ARCH).tar.gz - artifact: vscode_server_linux_$(VSCODE_ARCH)_archive-unsigned - displayName: Publish server archive + - publish: $(Agent.BuildDirectory)/vscode-server-linux-$(VSCODE_ARCH).tar.gz + artifact: vscode_server_linux_$(VSCODE_ARCH)_archive-unsigned + displayName: Publish server archive - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: - - publish: $(Agent.BuildDirectory)/vscode-server-linux-$(VSCODE_ARCH)-web.tar.gz - artifact: vscode_web_linux_$(VSCODE_ARCH)_archive-unsigned - displayName: Publish web server archive + - publish: $(Agent.BuildDirectory)/vscode-server-linux-$(VSCODE_ARCH)-web.tar.gz + artifact: vscode_web_linux_$(VSCODE_ARCH)_archive-unsigned + displayName: Publish web server archive - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: - - task: PublishPipelineArtifact@0 - displayName: "Publish Pipeline Artifact" - inputs: - artifactName: "snap-$(VSCODE_ARCH)" - targetPath: .build/linux/snap-tarball + - task: PublishPipelineArtifact@0 + displayName: "Publish Pipeline Artifact" + inputs: + artifactName: "snap-$(VSCODE_ARCH)" + targetPath: .build/linux/snap-tarball diff --git a/build/azure-pipelines/linux/product-build-linux-server.yml b/build/azure-pipelines/linux/product-build-linux-server.yml index 8ab58da435c..ebec4113dc4 100644 --- a/build/azure-pipelines/linux/product-build-linux-server.yml +++ b/build/azure-pipelines/linux/product-build-linux-server.yml @@ -8,55 +8,55 @@ steps: versionSpec: "16.x" - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - task: AzureKeyVault@1 - displayName: "Azure Key Vault: Get Secrets" - inputs: - azureSubscription: "vscode-builds-subscription" - KeyVaultName: vscode - SecretsFilter: "github-distro-mixin-password,ESRP-PKI,esrp-aad-username,esrp-aad-password" + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode + SecretsFilter: "github-distro-mixin-password,ESRP-PKI,esrp-aad-username,esrp-aad-password" - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - task: Docker@1 - displayName: "Pull Docker image" - inputs: - azureSubscriptionEndpoint: "vscode-builds-subscription" - azureContainerRegistry: vscodehub.azurecr.io - command: "Run an image" - imageName: "vscode-linux-build-agent:centos7-devtoolset8-arm64" - containerCommand: uname - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'arm64')) + - task: Docker@1 + displayName: "Pull Docker image" + inputs: + azureSubscriptionEndpoint: "vscode-builds-subscription" + azureContainerRegistry: vscodehub.azurecr.io + command: "Run an image" + imageName: "vscode-linux-build-agent:centos7-devtoolset8-arm64" + containerCommand: uname + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'arm64')) - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - cat << EOF > ~/.netrc - machine github.com - login vscode - password $(github-distro-mixin-password) - EOF + - 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" - displayName: Prepare tooling + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" + displayName: Prepare tooling - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - git fetch https://github.com/$(VSCODE_MIXIN_REPO).git $VSCODE_DISTRO_REF - echo "##vso[task.setvariable variable=VSCODE_DISTRO_COMMIT;]$(git rev-parse FETCH_HEAD)" - git checkout FETCH_HEAD - condition: and(succeeded(), ne(variables.VSCODE_DISTRO_REF, ' ')) - displayName: Checkout override commit + - script: | + set -e + git fetch https://github.com/$(VSCODE_MIXIN_REPO).git $VSCODE_DISTRO_REF + echo "##vso[task.setvariable variable=VSCODE_DISTRO_COMMIT;]$(git rev-parse FETCH_HEAD)" + git checkout FETCH_HEAD + condition: and(succeeded(), ne(variables.VSCODE_DISTRO_REF, ' ')) + displayName: Checkout override commit - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") - displayName: Merge distro + - script: | + set -e + git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") + displayName: Merge distro - script: | set -e - npx https://aka.ms/enablesecurefeed standAlone + npm install https://aka.ms/enablesecurefeed --global && npm exec terrapinadotask -- standAlone timeoutInMinutes: 5 retryCountOnTaskFailure: 3 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) @@ -71,18 +71,18 @@ steps: condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes - displayName: Register Docker QEMU - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'arm64')) + - script: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + displayName: Register Docker QEMU + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'arm64')) - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - docker run -e VSCODE_QUALITY -e GITHUB_TOKEN -v $(pwd):/root/vscode -v ~/.netrc:/root/.netrc vscodehub.azurecr.io/vscode-linux-build-agent:centos7-devtoolset8-arm64 /root/vscode/build/azure-pipelines/linux/scripts/install-remote-dependencies.sh - displayName: Install dependencies via qemu - env: - GITHUB_TOKEN: "$(github-distro-mixin-password)" - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'arm64')) + - script: | + set -e + docker run -e VSCODE_QUALITY -e GITHUB_TOKEN -v $(pwd):/root/vscode -v ~/.netrc:/root/.netrc vscodehub.azurecr.io/vscode-linux-build-agent:centos7-devtoolset8-arm64 /root/vscode/build/azure-pipelines/linux/scripts/install-remote-dependencies.sh + displayName: Install dependencies via qemu + env: + GITHUB_TOKEN: "$(github-distro-mixin-password)" + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'arm64')) - script: | set -e diff --git a/build/azure-pipelines/mixin-distro-win32.yml b/build/azure-pipelines/mixin-distro-win32.yml index 2b055428bfa..5ff4eacf41c 100644 --- a/build/azure-pipelines/mixin-distro-win32.yml +++ b/build/azure-pipelines/mixin-distro-win32.yml @@ -1,4 +1,3 @@ - parameters: - name: VSCODE_QUALITY type: string diff --git a/build/azure-pipelines/product-build-pr-cache.yml b/build/azure-pipelines/product-build-pr-cache.yml index 042325394d3..7a6aaa7e559 100644 --- a/build/azure-pipelines/product-build-pr-cache.yml +++ b/build/azure-pipelines/product-build-pr-cache.yml @@ -34,7 +34,7 @@ steps: - script: | set -e - npx https://aka.ms/enablesecurefeed standAlone + npm install https://aka.ms/enablesecurefeed --global && npm exec terrapinadotask -- standAlone timeoutInMinutes: 5 retryCountOnTaskFailure: 3 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) diff --git a/build/azure-pipelines/product-build-pr.yml b/build/azure-pipelines/product-build-pr.yml index b332ec7b3f8..0ffcdcc00ca 100644 --- a/build/azure-pipelines/product-build-pr.yml +++ b/build/azure-pipelines/product-build-pr.yml @@ -26,10 +26,7 @@ jobs: - ${{ if ne(variables['VSCODE_CIBUILD'], true) }}: - job: Compile displayName: Compile & Hygiene - pool: - name: vscode-1es-vscode-linux-20.04 - demands: - - ImageVersionOverride -equals 40.0.0 + pool: vscode-1es-vscode-linux-20.04 timeoutInMinutes: 30 variables: VSCODE_ARCH: x64 @@ -40,10 +37,7 @@ jobs: - job: Linuxx64UnitTest displayName: Linux (Unit Tests) - pool: - name: vscode-1es-vscode-linux-20.04 - demands: - - ImageVersionOverride -equals 40.0.0 + pool: vscode-1es-vscode-linux-20.04 timeoutInMinutes: 30 variables: VSCODE_ARCH: x64 @@ -54,16 +48,14 @@ jobs: parameters: VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_TUNNEL_CLI: false VSCODE_RUN_UNIT_TESTS: true VSCODE_RUN_INTEGRATION_TESTS: false VSCODE_RUN_SMOKE_TESTS: false - job: Linuxx64IntegrationTest displayName: Linux (Integration Tests) - pool: - name: vscode-1es-vscode-linux-20.04 - demands: - - ImageVersionOverride -equals 40.0.0 + pool: vscode-1es-vscode-linux-20.04 timeoutInMinutes: 30 variables: VSCODE_ARCH: x64 @@ -74,16 +66,14 @@ jobs: parameters: VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_TUNNEL_CLI: false VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: true VSCODE_RUN_SMOKE_TESTS: false - job: Linuxx64SmokeTest displayName: Linux (Smoke Tests) - pool: - name: vscode-1es-vscode-linux-20.04 - demands: - - ImageVersionOverride -equals 40.0.0 + pool: vscode-1es-vscode-linux-20.04 timeoutInMinutes: 30 variables: VSCODE_ARCH: x64 @@ -94,6 +84,7 @@ jobs: parameters: VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_TUNNEL_CLI: false VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: false VSCODE_RUN_SMOKE_TESTS: true @@ -101,10 +92,7 @@ jobs: - ${{ if eq(variables['VSCODE_CIBUILD'], true) }}: - job: Linuxx64MaintainNodeModulesCache displayName: Linux (Maintain node_modules cache) - pool: - name: vscode-1es-vscode-linux-20.04 - demands: - - ImageVersionOverride -equals 40.0.0 + pool: vscode-1es-vscode-linux-20.04 timeoutInMinutes: 30 variables: VSCODE_ARCH: x64 @@ -114,10 +102,7 @@ jobs: - ${{ if eq(variables['VSCODE_CIBUILD'], true) }}: - job: LinuxCLI displayName: Linux (CLI) - pool: - name: vscode-1es-vscode-linux-20.04 - demands: - - ImageVersionOverride -equals 40.0.0 + pool: vscode-1es-vscode-linux-20.04 timeoutInMinutes: 30 steps: - template: cli/test.yml diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index 970412d7869..8110d9aaea3 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -28,7 +28,7 @@ parameters: - name: ENABLE_TERRAPIN displayName: "Enable Terrapin" type: boolean - default: false + default: true - name: VSCODE_BUILD_WIN32 displayName: "🎯 Windows x64" type: boolean @@ -111,10 +111,6 @@ variables: value: ${{ parameters.VSCODE_QUALITY }} - name: VSCODE_BUILD_STAGE_WINDOWS value: ${{ or(eq(parameters.VSCODE_BUILD_WIN32, true), eq(parameters.VSCODE_BUILD_WIN32_32BIT, true), eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }} - - name: VSCODE_BUILD_STAGE_ARM_LINUX - value: ${{ or(eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true), eq(parameters.VSCODE_BUILD_LINUX_ARM64, true), eq(parameters.VSCODE_BUILD_LINUX_ALPINE_ARM64, true)) }} - - name: VSCODE_BUILD_STAGE_X86_LINUX - value: ${{ or(eq(parameters.VSCODE_BUILD_LINUX, true), eq(parameters.VSCODE_BUILD_LINUX_ALPINE, true)) }} - name: VSCODE_BUILD_STAGE_LINUX value: ${{ or(eq(parameters.VSCODE_BUILD_LINUX, true), eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true), eq(parameters.VSCODE_BUILD_LINUX_ARM64, true), eq(parameters.VSCODE_BUILD_LINUX_ALPINE, true), eq(parameters.VSCODE_BUILD_LINUX_ALPINE_ARM64, true)) }} - name: VSCODE_BUILD_STAGE_MACOS @@ -145,8 +141,6 @@ variables: value: true - name: Codeql.SkipTaskAutoInjection value: true - - name: VSCODE_CLI_BINARY_NAME - value: code-tunnel resources: containers: @@ -185,24 +179,23 @@ stages: - stage: CompileCLI dependsOn: [] jobs: - - ${{ if eq(variables.VSCODE_BUILD_STAGE_X86_LINUX, true) }}: + - ${{ if or(eq(parameters.VSCODE_BUILD_LINUX, true), eq(parameters.VSCODE_BUILD_LINUX_ALPINE, true)) }}: - job: LinuxX86 pool: vscode-1es-linux + variables: + VSCODE_ARCH: x64 steps: - - template: ./cli/compile-linux.yml + - template: ./linux/cli-build-linux.yml parameters: - VSCODE_CLI_DIR: $(Build.SourcesDirectory)/cli - VSCODE_CLI_BINARY_NAME: ${{ variables.VSCODE_CLI_BINARY_NAME }} VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_CLI_TARGETS: - - ${{ if eq(parameters.VSCODE_BUILD_LINUX_ALPINE, true) }}: - - { target: x86_64-unknown-linux-musl, artifact: vscode_cli_alpine_x64_cli-unsigned } - - ${{ if eq(parameters.VSCODE_BUILD_LINUX, true) }}: - - { target: x86_64-unknown-linux-gnu, artifact: vscode_cli_linux_x64_cli-unsigned } + VSCODE_BUILD_LINUX_ALPINE: ${{ parameters.VSCODE_BUILD_LINUX_ALPINE }} + VSCODE_BUILD_LINUX: ${{ parameters.VSCODE_BUILD_LINUX }} - - ${{ if eq(variables.VSCODE_BUILD_STAGE_ARM_LINUX, true) }}: + - ${{ if or(eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true), eq(parameters.VSCODE_BUILD_LINUX_ARM64, true), eq(parameters.VSCODE_BUILD_LINUX_ALPINE_ARM64, true)) }}: - job: LinuxArm64 pool: vscode-1es-linux-20.04-arm64 + variables: + VSCODE_ARCH: arm64 steps: - task: NodeTool@0 displayName: Install Node.js @@ -214,56 +207,32 @@ stages: sudo apt update -y sudo apt install -y build-essential pkg-config displayName: Install build dependencies - - template: ./cli/compile-linux.yml + - template: ./linux/cli-build-linux.yml parameters: - VSCODE_CLI_DIR: $(Build.SourcesDirectory)/cli - VSCODE_CLI_BINARY_NAME: ${{ variables.VSCODE_CLI_BINARY_NAME }} VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_CLI_TARGETS: - - ${{ if eq(parameters.VSCODE_BUILD_LINUX_ALPINE_ARM64, true) }}: - - { target: aarch64-unknown-linux-musl, artifact: vscode_cli_alpine_arm64_cli-unsigned } - - ${{ if eq(parameters.VSCODE_BUILD_LINUX_ARM64, true) }}: - - { target: aarch64-unknown-linux-gnu, artifact: vscode_cli_linux_arm64_cli-unsigned } + VSCODE_BUILD_LINUX_ALPINE_ARM64: ${{ parameters.VSCODE_BUILD_LINUX_ALPINE_ARM64 }} + VSCODE_BUILD_LINUX_ARM64: ${{ parameters.VSCODE_BUILD_LINUX_ARM64 }} - ${{ if eq(variables.VSCODE_BUILD_STAGE_MACOS, true) }}: - job: MacOS pool: vmImage: macOS-latest steps: - - template: ./cli/compile-macos.yml + - template: ./darwin/cli-build-darwin.yml parameters: - VSCODE_CLI_DIR: $(Build.SourcesDirectory)/cli - VSCODE_CLI_BINARY_NAME: ${{ variables.VSCODE_CLI_BINARY_NAME }} VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_CLI_TARGETS: - - ${{ if eq(parameters.VSCODE_BUILD_MACOS, true) }}: - - { target: x86_64-apple-darwin, artifact: vscode_cli_darwin_x64_cli-unsigned } - - ${{ if eq(parameters.VSCODE_BUILD_MACOS_ARM64, true) }}: - - { target: aarch64-apple-darwin, artifact: vscode_cli_darwin_arm64_cli-unsigned } + VSCODE_BUILD_MACOS: ${{ parameters.VSCODE_BUILD_MACOS }} + VSCODE_BUILD_MACOS_ARM64: ${{ parameters.VSCODE_BUILD_MACOS_ARM64 }} - ${{ if eq(variables.VSCODE_BUILD_STAGE_WINDOWS, true) }}: - job: Windows pool: vscode-1es-windows steps: - - template: ./cli/vcpkg-deps.yml + - template: ./win32/cli-build-win32.yml parameters: - targets: - - ${{ if eq(parameters.VSCODE_BUILD_WIN32, true) }}: - - x64-windows-static-md - - ${{ if eq(parameters.VSCODE_BUILD_WIN32_ARM64, true) }}: - - arm64-windows-static-md - vcpkgDir: $(Build.SourcesDirectory)/build/azure-pipelines/cli/vcpkg - targetDirectory: $(Build.ArtifactStagingDirectory)/deps - - template: ./cli/compile-windows.yml - parameters: - VSCODE_CLI_DIR: $(Build.SourcesDirectory)/cli - VSCODE_CLI_BINARY_NAME: ${{ variables.VSCODE_CLI_BINARY_NAME }} VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_CLI_TARGETS: - - ${{ if eq(parameters.VSCODE_BUILD_WIN32, true) }}: - - { target: x86_64-pc-windows-msvc, artifact: vscode_cli_win32_x64_cli-unsigned } - - ${{ if eq(parameters.VSCODE_BUILD_WIN32_ARM64, true) }}: - - { target: aarch64-pc-windows-msvc, artifact: vscode_cli_win32_arm64_cli-unsigned } + VSCODE_BUILD_WIN32: ${{ parameters.VSCODE_BUILD_WIN32 }} + VSCODE_BUILD_WIN32_ARM64: ${{ parameters.VSCODE_BUILD_WIN32_ARM64 }} - ${{ if and(eq(parameters.VSCODE_COMPILE_ONLY, false), eq(variables['VSCODE_BUILD_STAGE_WINDOWS'], true)) }}: - stage: Windows @@ -284,6 +253,7 @@ stages: parameters: VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_TUNNEL_CLI: false VSCODE_RUN_UNIT_TESTS: true VSCODE_RUN_INTEGRATION_TESTS: false VSCODE_RUN_SMOKE_TESTS: false @@ -297,6 +267,7 @@ stages: parameters: VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_TUNNEL_CLI: false VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: true VSCODE_RUN_SMOKE_TESTS: false @@ -310,6 +281,7 @@ stages: parameters: VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_TUNNEL_CLI: false VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: false VSCODE_RUN_SMOKE_TESTS: true @@ -324,12 +296,10 @@ stages: parameters: VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_TUNNEL_CLI: ${{ parameters.VSCODE_BUILD_TUNNEL_CLI }} VSCODE_RUN_UNIT_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} VSCODE_RUN_INTEGRATION_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} VSCODE_RUN_SMOKE_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} - ${{ if eq(parameters.VSCODE_BUILD_TUNNEL_CLI, true) }}: - VSCODE_CLI_ARTIFACT: vscode_cli_win32_x64_cli-unsigned - VSCODE_CLI_BINARY_NAME: ${{ variables.VSCODE_CLI_BINARY_NAME }} - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_WIN32_32BIT, true)) }}: - job: Windows32 @@ -341,6 +311,7 @@ stages: parameters: VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_TUNNEL_CLI: false # todo until 32 bit CLI is available VSCODE_RUN_UNIT_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} VSCODE_RUN_INTEGRATION_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} VSCODE_RUN_SMOKE_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} @@ -355,12 +326,10 @@ stages: parameters: VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_TUNNEL_CLI: ${{ parameters.VSCODE_BUILD_TUNNEL_CLI }} VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: false VSCODE_RUN_SMOKE_TESTS: false - ${{ if eq(parameters.VSCODE_BUILD_TUNNEL_CLI, true) }}: - VSCODE_CLI_ARTIFACT: vscode_cli_win32_arm64_cli-unsigned - VSCODE_CLI_BINARY_NAME: ${{ variables.VSCODE_CLI_BINARY_NAME }} - ${{ if and(eq(variables['VSCODE_PUBLISH'], true), eq(parameters.VSCODE_BUILD_TUNNEL_CLI, true)) }}: - job: windowsCLISign @@ -368,12 +337,8 @@ stages: steps: - template: win32/product-build-win32-cli-sign.yml parameters: - VSCODE_CLI_BINARY_NAME: ${{ variables.VSCODE_CLI_BINARY_NAME }} - VSCODE_CLI_ARTIFACTS: - - ${{ if eq(parameters.VSCODE_BUILD_WIN32, true) }}: - - vscode_cli_win32_x64_cli-unsigned - - ${{ if eq(parameters.VSCODE_BUILD_WIN32_ARM64, true) }}: - - vscode_cli_win32_arm64_cli-unsigned + VSCODE_BUILD_WIN32: ${{ parameters.VSCODE_BUILD_WIN32 }} + VSCODE_BUILD_WIN32_ARM64: ${{ parameters.VSCODE_BUILD_WIN32_ARM64 }} - ${{ if and(eq(parameters.VSCODE_COMPILE_ONLY, false), eq(variables['VSCODE_BUILD_STAGE_LINUX'], true)) }}: - stage: LinuxServerDependencies @@ -422,6 +387,7 @@ stages: parameters: VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_TUNNEL_CLI: false VSCODE_RUN_UNIT_TESTS: true VSCODE_RUN_INTEGRATION_TESTS: false VSCODE_RUN_SMOKE_TESTS: false @@ -437,6 +403,7 @@ stages: parameters: VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_TUNNEL_CLI: false VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: true VSCODE_RUN_SMOKE_TESTS: false @@ -452,6 +419,7 @@ stages: parameters: VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_TUNNEL_CLI: false VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: false VSCODE_RUN_SMOKE_TESTS: true @@ -468,12 +436,10 @@ stages: parameters: VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_TUNNEL_CLI: ${{ parameters.VSCODE_BUILD_TUNNEL_CLI }} VSCODE_RUN_UNIT_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} VSCODE_RUN_INTEGRATION_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} VSCODE_RUN_SMOKE_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} - ${{ if eq(parameters.VSCODE_BUILD_TUNNEL_CLI, true) }}: - VSCODE_CLI_ARTIFACT: vscode_cli_linux_x64_cli-unsigned - VSCODE_CLI_BINARY_NAME: ${{ variables.VSCODE_CLI_BINARY_NAME }} - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX, true), ne(variables['VSCODE_PUBLISH'], 'false')) }}: - job: LinuxSnap @@ -496,6 +462,7 @@ stages: parameters: VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_TUNNEL_CLI: false # todo: not built yet VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: false VSCODE_RUN_SMOKE_TESTS: false @@ -522,12 +489,10 @@ stages: parameters: VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_TUNNEL_CLI: ${{ parameters.VSCODE_BUILD_TUNNEL_CLI }} VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: false VSCODE_RUN_SMOKE_TESTS: false - ${{ if eq(parameters.VSCODE_BUILD_TUNNEL_CLI, true) }}: - VSCODE_CLI_ARTIFACT: vscode_cli_linux_arm64_cli-unsigned - VSCODE_CLI_BINARY_NAME: ${{ variables.VSCODE_CLI_BINARY_NAME }} # TODO@joaomoreno: We don't ship ARM snaps for now - ${{ if and(false, eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX_ARM64, true)) }}: @@ -577,6 +542,7 @@ stages: parameters: VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_TUNNEL_CLI: false VSCODE_RUN_UNIT_TESTS: true VSCODE_RUN_INTEGRATION_TESTS: false VSCODE_RUN_SMOKE_TESTS: false @@ -590,6 +556,7 @@ stages: parameters: VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_TUNNEL_CLI: false VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: true VSCODE_RUN_SMOKE_TESTS: false @@ -603,6 +570,7 @@ stages: parameters: VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_TUNNEL_CLI: false VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: false VSCODE_RUN_SMOKE_TESTS: true @@ -617,12 +585,10 @@ stages: parameters: VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_TUNNEL_CLI: ${{ parameters.VSCODE_BUILD_TUNNEL_CLI }} VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: false VSCODE_RUN_SMOKE_TESTS: false - ${{ if eq(parameters.VSCODE_BUILD_TUNNEL_CLI, true) }}: - VSCODE_CLI_ARTIFACT: vscode_cli_darwin_x64_cli-unsigned - VSCODE_CLI_BINARY_NAME: ${{ variables.VSCODE_CLI_BINARY_NAME }} - ${{ if eq(parameters.VSCODE_STEP_ON_IT, false) }}: - job: macOSTest @@ -634,6 +600,7 @@ stages: parameters: VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_TUNNEL_CLI: false VSCODE_RUN_UNIT_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} VSCODE_RUN_INTEGRATION_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} VSCODE_RUN_SMOKE_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} @@ -654,12 +621,8 @@ stages: steps: - template: darwin/product-build-darwin-cli-sign.yml parameters: - VSCODE_CLI_BINARY_NAME: ${{ variables.VSCODE_CLI_BINARY_NAME }} - VSCODE_CLI_ARTIFACTS: - - ${{ if eq(parameters.VSCODE_BUILD_MACOS, true) }}: - - vscode_cli_darwin_x64_cli-unsigned - - ${{ if eq(parameters.VSCODE_BUILD_MACOS_ARM64, true) }}: - - vscode_cli_darwin_arm64_cli-unsigned + VSCODE_BUILD_MACOS: ${{ parameters.VSCODE_BUILD_MACOS }} + VSCODE_BUILD_MACOS_ARM64: ${{ parameters.VSCODE_BUILD_MACOS_ARM64 }} - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_MACOS_ARM64, true)) }}: - job: macOSARM64 @@ -671,12 +634,10 @@ stages: parameters: VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_TUNNEL_CLI: ${{ parameters.VSCODE_BUILD_TUNNEL_CLI }} VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: false VSCODE_RUN_SMOKE_TESTS: false - ${{ if eq(parameters.VSCODE_BUILD_TUNNEL_CLI, true) }}: - VSCODE_CLI_ARTIFACT: vscode_cli_darwin_arm64_cli-unsigned - VSCODE_CLI_BINARY_NAME: ${{ variables.VSCODE_CLI_BINARY_NAME }} - ${{ if eq(variables['VSCODE_PUBLISH'], true) }}: - job: macOSARM64Sign @@ -737,13 +698,33 @@ stages: steps: - template: product-publish.yml + - ${{ if and(parameters.VSCODE_RELEASE, eq(parameters.VSCODE_DISTRO_REF, ' ')) }}: + - stage: ApproveRelease + dependsOn: [] # run in parallel to compile stage + pool: vscode-1es-linux + jobs: + - deployment: ApproveRelease + displayName: "Approve Release" + environment: "vscode" + variables: + skipComponentGovernanceDetection: true + strategy: + runOnce: + deploy: + steps: + - checkout: none + - ${{ if or(and(parameters.VSCODE_RELEASE, eq(parameters.VSCODE_DISTRO_REF, ' ')), and(in(parameters.VSCODE_QUALITY, 'insider', 'exploration'), eq(variables['VSCODE_SCHEDULEDBUILD'], true))) }}: - stage: Release dependsOn: - Publish + - ${{ if and(parameters.VSCODE_RELEASE, eq(parameters.VSCODE_DISTRO_REF, ' ')) }}: + - ApproveRelease pool: vscode-1es-linux jobs: - job: ReleaseBuild displayName: Release Build steps: - template: product-release.yml + parameters: + VSCODE_RELEASE: ${{ parameters.VSCODE_RELEASE }} diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index b0631e607dc..7e1837a8ae4 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -40,7 +40,7 @@ steps: - script: | set -e - npx https://aka.ms/enablesecurefeed standAlone + npm install https://aka.ms/enablesecurefeed --global && npm exec terrapinadotask -- standAlone timeoutInMinutes: 5 retryCountOnTaskFailure: 3 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) @@ -86,11 +86,11 @@ steps: displayName: Create node_modules archive - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - # Mixin must run before optimize, because the CSS loader will inline small SVGs - - script: | - set -e - node build/azure-pipelines/mixin - displayName: Mix in quality + # Mixin must run before optimize, because the CSS loader will inline small SVGs + - script: | + set -e + node build/azure-pipelines/mixin + displayName: Mix in quality - script: | set -e @@ -100,71 +100,71 @@ steps: displayName: Compile & Hygiene - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - yarn --cwd build compile - ./.github/workflows/check-clean-git-state.sh - displayName: Check /build/ folder + - script: | + set -e + yarn --cwd build compile + ./.github/workflows/check-clean-git-state.sh + displayName: Check /build/ folder - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - yarn --cwd test/smoke compile - yarn --cwd test/integration/browser compile - displayName: Compile test suites - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - script: | + set -e + yarn --cwd test/smoke compile + yarn --cwd test/integration/browser compile + displayName: Compile test suites + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - task: AzureCLI@2 - inputs: - azureSubscription: "vscode-builds-subscription" - scriptType: pscore - scriptLocation: inlineScript - addSpnToEnvironment: true - inlineScript: | - Write-Host "##vso[task.setvariable variable=AZURE_TENANT_ID]$env:tenantId" - Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_ID]$env:servicePrincipalId" - Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_SECRET;issecret=true]$env:servicePrincipalKey" + - task: AzureCLI@2 + inputs: + azureSubscription: "vscode-builds-subscription" + scriptType: pscore + scriptLocation: inlineScript + addSpnToEnvironment: true + inlineScript: | + Write-Host "##vso[task.setvariable variable=AZURE_TENANT_ID]$env:tenantId" + Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_ID]$env:servicePrincipalId" + Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_SECRET;issecret=true]$env:servicePrincipalKey" - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - AZURE_STORAGE_ACCOUNT="ticino" \ - AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \ - AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ - AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ - node build/azure-pipelines/upload-sourcemaps - displayName: Upload sourcemaps + - script: | + set -e + AZURE_STORAGE_ACCOUNT="ticino" \ + AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \ + AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ + AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ + node build/azure-pipelines/upload-sourcemaps + displayName: Upload sourcemaps - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set - - ./build/azure-pipelines/common/extract-telemetry.sh - displayName: Extract Telemetry + - script: | + set - + ./build/azure-pipelines/common/extract-telemetry.sh + displayName: Extract Telemetry - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - tar -cz --ignore-failed-read -f $(Build.ArtifactStagingDirectory)/compilation.tar.gz .build out-* test/integration/browser/out test/smoke/out test/automation/out - displayName: Compress compilation artifact + - script: | + set -e + tar -cz --ignore-failed-read -f $(Build.ArtifactStagingDirectory)/compilation.tar.gz .build out-* test/integration/browser/out test/smoke/out test/automation/out + displayName: Compress compilation artifact - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - task: PublishPipelineArtifact@1 - inputs: - targetPath: $(Build.ArtifactStagingDirectory)/compilation.tar.gz - artifactName: Compilation - displayName: Publish compilation artifact + - task: PublishPipelineArtifact@1 + inputs: + targetPath: $(Build.ArtifactStagingDirectory)/compilation.tar.gz + artifactName: Compilation + displayName: Publish compilation artifact - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn download-builtin-extensions-cg - displayName: Built-in extensions component details + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn download-builtin-extensions-cg + displayName: Built-in extensions component details - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 - displayName: "Component Detection" - inputs: - sourceScanPath: $(Build.SourcesDirectory) - continueOnError: true + - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: "Component Detection" + inputs: + sourceScanPath: $(Build.SourcesDirectory) + continueOnError: true diff --git a/build/azure-pipelines/product-publish.ps1 b/build/azure-pipelines/product-publish.ps1 index 5006ec61a30..4e47d7d6cb2 100644 --- a/build/azure-pipelines/product-publish.ps1 +++ b/build/azure-pipelines/product-publish.ps1 @@ -62,12 +62,15 @@ do { if($set.Add($artifactName)) { Write-Host "Processing artifact: '$artifactName. Downloading from: $($_.resource.downloadUrl)" + $extractPath = "$env:AGENT_TEMPDIRECTORY/$artifactName.zip" try { - Invoke-RestMethod $_.resource.downloadUrl -OutFile "$env:AGENT_TEMPDIRECTORY/$artifactName.zip" -Headers @{ + Invoke-RestMethod $_.resource.downloadUrl -OutFile $extractPath -Headers @{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" - } -MaximumRetryCount 5 -RetryIntervalSec 1 | Out-Null + } -MaximumRetryCount 5 -RetryIntervalSec 1 -TimeoutSec 300 | Out-Null - Expand-Archive -Path "$env:AGENT_TEMPDIRECTORY/$artifactName.zip" -DestinationPath $env:AGENT_TEMPDIRECTORY | Out-Null + Write-Host "Extracting artifact: '$extractPath'" + + Expand-Archive -Path $extractPath -DestinationPath $env:AGENT_TEMPDIRECTORY | Out-Null } catch { Write-Warning $_ $set.Remove($artifactName) | Out-Null @@ -76,6 +79,13 @@ do { $null,$product,$os,$arch,$type = $artifactName -split '_' $asset = Get-ChildItem -rec "$env:AGENT_TEMPDIRECTORY/$artifactName" + + if ($asset.Size -ne $_.resource.properties.artifactsize) { + Write-Warning "Artifact size mismatch for '$artifactName'. Expected: $($_.resource.properties.artifactsize). Actual: $($asset.Size)" + $set.Remove($artifactName) | Out-Null + continue + } + Write-Host "Processing artifact with the following values:" # turning in into an object just to log nicely @{ diff --git a/build/azure-pipelines/product-release.yml b/build/azure-pipelines/product-release.yml index a1086945595..8be900a9b41 100644 --- a/build/azure-pipelines/product-release.yml +++ b/build/azure-pipelines/product-release.yml @@ -1,3 +1,7 @@ +parameters: + - name: VSCODE_RELEASE + type: boolean + steps: - task: NodeTool@0 inputs: @@ -20,4 +24,4 @@ steps: AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \ AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ - node build/azure-pipelines/common/releaseBuild.js + node build/azure-pipelines/common/releaseBuild.js ${{ parameters.VSCODE_RELEASE }} diff --git a/build/azure-pipelines/sdl-scan.yml b/build/azure-pipelines/sdl-scan.yml index f6a44d4862b..1df26feebbd 100644 --- a/build/azure-pipelines/sdl-scan.yml +++ b/build/azure-pipelines/sdl-scan.yml @@ -83,7 +83,8 @@ stages: - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" - exec { npx https://aka.ms/enablesecurefeed standAlone } + exec { npm install https://aka.ms/enablesecurefeed --global } + exec { npm exec terrapinadotask -- standAlone } timeoutInMinutes: 5 retryCountOnTaskFailure: 3 condition: and(succeeded(), eq(variables['ENABLE_TERRAPIN'], 'true')) @@ -184,7 +185,7 @@ stages: - script: | set -e - npx https://aka.ms/enablesecurefeed standAlone + npm install https://aka.ms/enablesecurefeed --global && npm exec terrapinadotask -- standAlone timeoutInMinutes: 5 retryCountOnTaskFailure: 3 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) diff --git a/build/azure-pipelines/cli/vcpkg-deps.yml b/build/azure-pipelines/vcpkg-install.yml similarity index 69% rename from build/azure-pipelines/cli/vcpkg-deps.yml rename to build/azure-pipelines/vcpkg-install.yml index 8c2928b0f70..d11d5c5d5c3 100644 --- a/build/azure-pipelines/cli/vcpkg-deps.yml +++ b/build/azure-pipelines/vcpkg-install.yml @@ -8,11 +8,14 @@ parameters: type: string steps: - - script: git clone https://github.com/microsoft/vcpkg.git $(Build.ArtifactStagingDirectory)/vcpkg - displayName: Checkout vcpkg - - - script: $(Build.ArtifactStagingDirectory)/vcpkg/bootstrap-vcpkg.bat + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + exec { git clone https://github.com/microsoft/vcpkg.git $(Build.ArtifactStagingDirectory)/vcpkg } + exec { cd $(Build.ArtifactStagingDirectory)/vcpkg; git checkout 779ce74ef67d3e12d904da1b15f9ed5626d5f677 } + exec { $(Build.ArtifactStagingDirectory)/vcpkg/bootstrap-vcpkg.bat } + Write-Output "##vso[task.setvariable variable=VSCODE_DID_BOOTSTRAP_VCPKG]true" displayName: Bootstrap vcpkg + condition: not(eq(variables.VSCODE_DID_BOOTSTRAP_VCPKG, true)) - ${{ each target in parameters.targets }}: - task: Cache@2 diff --git a/build/azure-pipelines/web/product-build-web.yml b/build/azure-pipelines/web/product-build-web.yml index 61e409d4859..735b26cb0e7 100644 --- a/build/azure-pipelines/web/product-build-web.yml +++ b/build/azure-pipelines/web/product-build-web.yml @@ -73,7 +73,7 @@ steps: - script: | set -e - npx https://aka.ms/enablesecurefeed standAlone + npm install https://aka.ms/enablesecurefeed --global && npm exec terrapinadotask -- standAlone timeoutInMinutes: 5 retryCountOnTaskFailure: 3 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) diff --git a/build/azure-pipelines/win32/cli-build-win32.yml b/build/azure-pipelines/win32/cli-build-win32.yml new file mode 100644 index 00000000000..2b77bccfd2a --- /dev/null +++ b/build/azure-pipelines/win32/cli-build-win32.yml @@ -0,0 +1,63 @@ +parameters: + - name: VSCODE_BUILD_WIN32 + type: boolean + - name: VSCODE_BUILD_WIN32_ARM64 + type: boolean + - name: VSCODE_QUALITY + type: string + - name: channel + type: string + default: stable + +steps: + - task: NodeTool@0 + inputs: + versionSpec: "16.x" + + - template: ../mixin-distro-win32.yml + parameters: + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { node build/azure-pipelines/cli/prepare.js } + displayName: Prepare CLI build + env: + GITHUB_TOKEN: "$(github-distro-mixin-password)" + + - template: ../cli/install-rust-win32.yml + parameters: + targets: + - ${{ if eq(parameters.VSCODE_BUILD_WIN32, true) }}: + - x86_64-pc-windows-msvc + - ${{ if eq(parameters.VSCODE_BUILD_WIN32_ARM64, true) }}: + - aarch64-pc-windows-msvc + + - template: ../vcpkg-install.yml + parameters: + targets: + - ${{ if eq(parameters.VSCODE_BUILD_WIN32, true) }}: + - x64-windows-static-md + - ${{ if eq(parameters.VSCODE_BUILD_WIN32_ARM64, true) }}: + - arm64-windows-static-md + vcpkgDir: $(Build.SourcesDirectory)/build/azure-pipelines/cli/vcpkg + targetDirectory: $(Build.ArtifactStagingDirectory)/deps + + - ${{ if eq(parameters.VSCODE_BUILD_WIN32, true) }}: + - template: ../cli/cli-compile-and-publish.yml + parameters: + VSCODE_CLI_TARGET: x86_64-pc-windows-msvc + VSCODE_CLI_ARTIFACT: unsigned_vscode_cli_win32_x64_cli + VSCODE_CLI_ENV: + OPENSSL_LIB_DIR: $(Build.ArtifactStagingDirectory)/deps/x64-windows-static-md/lib + OPENSSL_INCLUDE_DIR: $(Build.ArtifactStagingDirectory)/deps/x64-windows-static-md/include + + - ${{ if eq(parameters.VSCODE_BUILD_WIN32_ARM64, true) }}: + - template: ../cli/cli-compile-and-publish.yml + parameters: + VSCODE_CLI_TARGET: aarch64-pc-windows-msvc + VSCODE_CLI_ARTIFACT: unsigned_vscode_cli_win32_arm64_cli + VSCODE_CLI_ENV: + OPENSSL_LIB_DIR: $(Build.ArtifactStagingDirectory)/deps/arm64-windows-static-md/lib + OPENSSL_INCLUDE_DIR: $(Build.ArtifactStagingDirectory)/deps/arm64-windows-static-md/include diff --git a/build/azure-pipelines/win32/product-build-win32-cli-sign.yml b/build/azure-pipelines/win32/product-build-win32-cli-sign.yml index ca45ef2f864..73526fbafcb 100644 --- a/build/azure-pipelines/win32/product-build-win32-cli-sign.yml +++ b/build/azure-pipelines/win32/product-build-win32-cli-sign.yml @@ -1,10 +1,16 @@ parameters: - - name: VSCODE_CLI_BINARY_NAME - type: string - - name: VSCODE_CLI_ARTIFACTS - default: [] - type: object + - name: VSCODE_BUILD_WIN32 + type: boolean + - name: VSCODE_BUILD_WIN32_ARM64 + type: boolean +variables: + - name: VSCODE_CLI_ARTIFACTS + value: + - ${{ if eq(parameters.VSCODE_BUILD_WIN32, true) }}: + - unsigned_vscode_cli_win32_x64_cli + - ${{ if eq(parameters.VSCODE_BUILD_WIN32_ARM64, true) }}: + - unsigned_vscode_cli_win32_arm64_cli steps: - task: NodeTool@0 displayName: "Use Node.js" @@ -17,50 +23,10 @@ steps: exec { yarn } displayName: Install build dependencies - - task: AzureKeyVault@1 - displayName: "Azure Key Vault: Get Secrets" - inputs: - azureSubscription: "vscode-builds-subscription" - KeyVaultName: vscode - SecretsFilter: "ESRP-PKI,esrp-aad-username,esrp-aad-password" - - - ${{ each target in parameters.VSCODE_CLI_ARTIFACTS }}: - - task: DownloadPipelineArtifact@2 - displayName: Download artifacts - inputs: - artifact: ${{ target }} - path: $(Build.ArtifactStagingDirectory)/pkg/${{ target }} - - - task: UseDotNet@2 - displayName: "Use .NET" - inputs: - version: 3.x - - - task: EsrpClientTool@1 - displayName: "Use ESRP client" - - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $EsrpClientTool = (gci -directory -filter EsrpClientTool_* $(Agent.RootDirectory)\_tasks | Select-Object -last 1).FullName - $EsrpCliZip = (gci -recurse -filter esrpcli.*.zip $EsrpClientTool | Select-Object -last 1).FullName - mkdir -p $(Agent.TempDirectory)\esrpcli - Expand-Archive -Path $EsrpCliZip -DestinationPath $(Agent.TempDirectory)\esrpcli - $EsrpCliDllPath = (gci -recurse -filter esrpcli.dll $(Agent.TempDirectory)\esrpcli | Select-Object -last 1).FullName - echo "##vso[task.setvariable variable=EsrpCliDllPath]$EsrpCliDllPath" - displayName: Find ESRP CLI - - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { node build\azure-pipelines\common\sign $env:EsrpCliDllPath windows $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(Build.ArtifactStagingDirectory)/pkg "*.exe" } - displayName: "Code sign" - - - ${{ each target in parameters.VSCODE_CLI_ARTIFACTS }}: - - powershell: | - $ASSET_ID = "${{ target }}".replace("-unsigned", ""); - echo "##vso[task.setvariable variable=ASSET_ID]$ASSET_ID" - displayName: Set asset id variable - - - publish: $(Build.ArtifactStagingDirectory)/pkg/${{ target }}/${{ parameters.VSCODE_CLI_BINARY_NAME }}.exe - artifact: $(ASSET_ID) + - template: ../cli/cli-win32-sign.yml + parameters: + VSCODE_CLI_ARTIFACTS: + - ${{ if eq(parameters.VSCODE_BUILD_WIN32, true) }}: + - unsigned_vscode_cli_win32_x64_cli + - ${{ if eq(parameters.VSCODE_BUILD_WIN32_ARM64, true) }}: + - unsigned_vscode_cli_win32_arm64_cli diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index 4a402d478f3..0eb1ab16978 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -9,12 +9,8 @@ parameters: type: boolean - name: VSCODE_RUN_SMOKE_TESTS type: boolean - - name: VSCODE_CLI_ARTIFACT - type: string - default: '' - - name: VSCODE_CLI_BINARY_NAME - type: string - default: '' + - name: VSCODE_BUILD_TUNNEL_CLI + type: boolean steps: - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: @@ -112,7 +108,8 @@ steps: - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" - exec { npx https://aka.ms/enablesecurefeed standAlone } + exec { npm install https://aka.ms/enablesecurefeed --global } + exec { npm exec terrapinadotask -- standAlone } timeoutInMinutes: 5 retryCountOnTaskFailure: 3 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) @@ -181,19 +178,35 @@ steps: echo "##vso[task.setvariable variable=CodeSigningFolderPath]$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)" displayName: Build - - ${{ if ne(parameters.VSCODE_CLI_ARTIFACT, '') }}: + - ${{ if eq(parameters.VSCODE_BUILD_TUNNEL_CLI, true) }}: - task: DownloadPipelineArtifact@2 inputs: - artifact: ${{ parameters.VSCODE_CLI_ARTIFACT }} + artifact: unsigned_vscode_cli_win32_arm64_cli patterns: '**' - path: $(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)/bin + path: $(Build.ArtifactStagingDirectory)/cli displayName: Download VS Code CLI + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'arm64')) - - ${{ if ne(parameters.VSCODE_QUALITY, 'stable') }}: - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - Move-Item -Path "$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)/bin/$(VSCODE_CLI_BINARY_NAME).exe" -Destination "$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)/bin/$(VSCODE_CLI_BINARY_NAME)-$(VSCODE_QUALITY).exe" - displayName: Move VS Code CLI + - task: DownloadPipelineArtifact@2 + inputs: + artifact: unsigned_vscode_cli_win32_x64_cli + patterns: '**' + path: $(Build.ArtifactStagingDirectory)/cli + displayName: Download VS Code CLI + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $ArtifactName = (gci -Path "$(Build.ArtifactStagingDirectory)/cli" | Select-Object -last 1).FullName + Expand-Archive -Path $ArtifactName -DestinationPath "$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)/bin" + + if ("$(VSCODE_QUALITY)" -ne "stable") + { + Move-Item -Path "$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)/bin/code-tunnel.exe" -Destination "$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)/bin/code-tunnel-$(VSCODE_QUALITY).exe" + } + + displayName: Move VS Code CLI - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: - powershell: | diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 94724bb410d..fb3a148f9a8 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 { getVersion } = require('./lib/getVersion'); const task = require('./lib/task'); const optimize = require('./lib/optimize'); const es = require('event-stream'); @@ -18,7 +19,7 @@ const monacoapi = require('./lib/monaco-api'); const fs = require('fs'); const root = path.dirname(__dirname); -const sha1 = util.getVersion(root); +const sha1 = getVersion(root); const semver = require('./monaco/package.json').version; const headerVersion = semver + '(' + sha1 + ')'; diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index 4023b3860e9..c0b79bc0f9d 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -12,12 +12,13 @@ const nodeUtil = require('util'); const es = require('event-stream'); const filter = require('gulp-filter'); const util = require('./lib/util'); +const { getVersion } = require('./lib/getVersion'); const task = require('./lib/task'); const watcher = require('./lib/watch'); const createReporter = require('./lib/reporter').createReporter; const glob = require('glob'); const root = path.dirname(__dirname); -const commit = util.getVersion(root); +const commit = getVersion(root); const plumber = require('gulp-plumber'); const ext = require('./lib/extensions'); @@ -46,7 +47,6 @@ const compilations = [ 'gulp/tsconfig.json', 'html-language-features/client/tsconfig.json', 'html-language-features/server/tsconfig.json', - 'image-preview/tsconfig.json', 'ipynb/tsconfig.json', 'jake/tsconfig.json', 'json-language-features/client/tsconfig.json', @@ -55,6 +55,7 @@ const compilations = [ 'markdown-language-features/server/tsconfig.json', 'markdown-language-features/tsconfig.json', 'markdown-math/tsconfig.json', + 'media-preview/tsconfig.json', 'merge-conflict/tsconfig.json', 'microsoft-authentication/tsconfig.json', 'npm/tsconfig.json', diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index 980f647c854..7475e04d6e5 100644 --- a/build/gulpfile.reh.js +++ b/build/gulpfile.reh.js @@ -9,6 +9,7 @@ const gulp = require('gulp'); const path = require('path'); const es = require('event-stream'); const util = require('./lib/util'); +const { getVersion } = require('./lib/getVersion'); const task = require('./lib/task'); const optimize = require('./lib/optimize'); const product = require('../product.json'); @@ -30,7 +31,7 @@ const { vscodeWebEntryPoints, vscodeWebResourceIncludes, createVSCodeWebFileCont const cp = require('child_process'); const REPO_ROOT = path.dirname(__dirname); -const commit = util.getVersion(REPO_ROOT); +const commit = getVersion(REPO_ROOT); const BUILD_ROOT = path.dirname(REPO_ROOT); const REMOTE_FOLDER = path.join(REPO_ROOT, 'remote'); diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 6947d1ee1ed..b6d3523d8b5 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -18,11 +18,12 @@ const replace = require('gulp-replace'); const filter = require('gulp-filter'); const _ = require('underscore'); const util = require('./lib/util'); +const { getVersion } = require('./lib/getVersion'); const task = require('./lib/task'); const buildfile = require('../src/buildfile'); const optimize = require('./lib/optimize'); const root = path.dirname(__dirname); -const commit = util.getVersion(root); +const commit = getVersion(root); const packageJson = require('../package.json'); const product = require('../product.json'); const crypto = require('crypto'); diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index 4a25ca5ab04..cdc887013d8 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -12,6 +12,7 @@ const shell = require('gulp-shell'); const es = require('event-stream'); const vfs = require('vinyl-fs'); const util = require('./lib/util'); +const { getVersion } = require('./lib/getVersion'); const task = require('./lib/task'); const packageJson = require('../package.json'); const product = require('../product.json'); @@ -20,7 +21,7 @@ const sysrootInstaller = require('./linux/debian/install-sysroot'); const debianRecommendedDependencies = require('./linux/debian/dep-lists').recommendedDeps; const path = require('path'); const root = path.dirname(__dirname); -const commit = util.getVersion(root); +const commit = getVersion(root); const linuxPackageRevision = Math.floor(new Date().getTime() / 1000); diff --git a/build/gulpfile.vscode.web.js b/build/gulpfile.vscode.web.js index c5b09393728..115f3ce499f 100644 --- a/build/gulpfile.vscode.web.js +++ b/build/gulpfile.vscode.web.js @@ -9,6 +9,7 @@ const gulp = require('gulp'); const path = require('path'); const es = require('event-stream'); const util = require('./lib/util'); +const { getVersion } = require('./lib/getVersion'); const task = require('./lib/task'); const optimize = require('./lib/optimize'); const product = require('../product.json'); @@ -26,7 +27,7 @@ const REPO_ROOT = path.dirname(__dirname); const BUILD_ROOT = path.dirname(REPO_ROOT); const WEB_FOLDER = path.join(REPO_ROOT, 'remote', 'web'); -const commit = util.getVersion(REPO_ROOT); +const commit = getVersion(REPO_ROOT); const quality = product.quality; const version = (quality && quality !== 'stable') ? `${packageJson.version}-${quality}` : packageJson.version; diff --git a/build/lib/electron.js b/build/lib/electron.js index 610d5bb6377..38f13d0ff08 100644 --- a/build/lib/electron.js +++ b/build/lib/electron.js @@ -11,12 +11,13 @@ const vfs = require("vinyl-fs"); const filter = require("gulp-filter"); const _ = require("underscore"); const util = require("./util"); +const getVersion_1 = require("./getVersion"); function isDocumentSuffix(str) { return str === 'document' || str === 'script' || str === 'file' || str === 'source code'; } const root = path.dirname(path.dirname(__dirname)); const product = JSON.parse(fs.readFileSync(path.join(root, 'product.json'), 'utf8')); -const commit = util.getVersion(root); +const commit = (0, getVersion_1.getVersion)(root); const darwinCreditsTemplate = product.darwinCredits && _.template(fs.readFileSync(path.join(root, product.darwinCredits), 'utf8')); /** * Generate a `DarwinDocumentType` given a list of file extensions, an icon name, and an optional suffix or file type name. diff --git a/build/lib/electron.ts b/build/lib/electron.ts index c37a0f6eae3..247de01c563 100644 --- a/build/lib/electron.ts +++ b/build/lib/electron.ts @@ -9,6 +9,7 @@ import * as vfs from 'vinyl-fs'; import * as filter from 'gulp-filter'; import * as _ from 'underscore'; import * as util from './util'; +import { getVersion } from './getVersion'; type DarwinDocumentSuffix = 'document' | 'script' | 'file' | 'source code'; type DarwinDocumentType = { @@ -26,7 +27,7 @@ function isDocumentSuffix(str?: string): str is DarwinDocumentSuffix { const root = path.dirname(path.dirname(__dirname)); const product = JSON.parse(fs.readFileSync(path.join(root, 'product.json'), 'utf8')); -const commit = util.getVersion(root); +const commit = getVersion(root); const darwinCreditsTemplate = product.darwinCredits && _.template(fs.readFileSync(path.join(root, product.darwinCredits), 'utf8')); diff --git a/build/lib/extensions.js b/build/lib/extensions.js index f22d9749fb0..f69b3322018 100644 --- a/build/lib/extensions.js +++ b/build/lib/extensions.js @@ -26,9 +26,9 @@ const jsoncParser = require("jsonc-parser"); const dependencies_1 = require("./dependencies"); const _ = require("underscore"); const builtInExtensions_1 = require("./builtInExtensions"); -const util = require('./util'); +const getVersion_1 = require("./getVersion"); const root = path.dirname(path.dirname(__dirname)); -const commit = util.getVersion(root); +const commit = (0, getVersion_1.getVersion)(root); const sourceMappingURLBase = `https://ticino.blob.core.windows.net/sourcemaps/${commit}`; function minifyExtensionResources(input) { const jsonFilter = filter(['**/*.json', '**/*.code-snippets'], { restore: true }); diff --git a/build/lib/extensions.ts b/build/lib/extensions.ts index 35447b284ad..36f29fcf3a1 100644 --- a/build/lib/extensions.ts +++ b/build/lib/extensions.ts @@ -26,9 +26,10 @@ import webpack = require('webpack'); import { getProductionDependencies } from './dependencies'; import _ = require('underscore'); import { getExtensionStream } from './builtInExtensions'; -const util = require('./util'); +import { getVersion } from './getVersion'; + const root = path.dirname(path.dirname(__dirname)); -const commit = util.getVersion(root); +const commit = getVersion(root); const sourceMappingURLBase = `https://ticino.blob.core.windows.net/sourcemaps/${commit}`; function minifyExtensionResources(input: Stream): Stream { diff --git a/build/lib/i18n.js b/build/lib/i18n.js index b97da8b9fbe..24e622bc7a8 100644 --- a/build/lib/i18n.js +++ b/build/lib/i18n.js @@ -4,19 +4,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ Object.defineProperty(exports, "__esModule", { value: true }); -exports.prepareIslFiles = exports.prepareI18nPackFiles = exports.prepareI18nFiles = exports.pullSetupXlfFiles = exports.findObsoleteResources = exports.pushXlfFiles = exports.createXlfFilesForIsl = exports.createXlfFilesForExtensions = exports.createXlfFilesForCoreBundle = exports.getResource = exports.processNlsFiles = exports.Limiter = exports.XLF = exports.Line = exports.externalExtensionsWithTranslations = exports.extraLanguages = exports.defaultLanguages = void 0; +exports.prepareIslFiles = exports.prepareI18nPackFiles = exports.createXlfFilesForIsl = exports.createXlfFilesForExtensions = exports.createXlfFilesForCoreBundle = exports.getResource = exports.processNlsFiles = exports.XLF = exports.Line = exports.extraLanguages = exports.defaultLanguages = void 0; const path = require("path"); const fs = require("fs"); const event_stream_1 = require("event-stream"); const File = require("vinyl"); const Is = require("is"); const xml2js = require("xml2js"); -const https = require("https"); const gulp = require("gulp"); const fancyLog = require("fancy-log"); const ansiColors = require("ansi-colors"); const iconv = require("@vscode/iconv-lite-umd"); -const NUMBER_OF_CONCURRENT_DOWNLOADS = 4; +const l10n_dev_1 = require("@vscode/l10n-dev"); function log(message, ...rest) { fancyLog(ansiColors.green('[i18n]'), message, ...rest); } @@ -38,7 +37,7 @@ exports.extraLanguages = [ { id: 'tr', folderName: 'trk' } ]; // non built-in extensions also that are transifex and need to be part of the language packs -exports.externalExtensionsWithTranslations = { +const externalExtensionsWithTranslations = { 'vscode-chrome-debug': 'msjsdiag.debugger-for-chrome', 'vscode-node-debug': 'ms-vscode.node-debug', 'vscode-node-debug2': 'ms-vscode.node-debug2' @@ -63,19 +62,6 @@ var BundledFormat; } BundledFormat.is = is; })(BundledFormat || (BundledFormat = {})); -var PackageJsonFormat; -(function (PackageJsonFormat) { - function is(value) { - if (Is.undef(value) || !Is.object(value)) { - return false; - } - return Object.keys(value).every(key => { - const element = value[key]; - return Is.string(element) || (Is.object(element) && Is.defined(element.message) && Is.defined(element.comment)); - }); - } - PackageJsonFormat.is = is; -})(PackageJsonFormat || (PackageJsonFormat = {})); class Line { constructor(indent = 0) { this.buffer = []; @@ -184,31 +170,6 @@ class XLF { } } exports.XLF = XLF; -XLF.parsePseudo = function (xlfString) { - return new Promise((resolve) => { - const parser = new xml2js.Parser(); - const files = []; - parser.parseString(xlfString, function (_err, result) { - const fileNodes = result['xliff']['file']; - fileNodes.forEach(file => { - const originalFilePath = file.$.original; - const messages = {}; - const transUnits = file.body[0]['trans-unit']; - if (transUnits) { - transUnits.forEach((unit) => { - const key = unit.$.id; - const val = pseudify(unit.source[0]['_'].toString()); - if (key && val) { - messages[key] = decodeEntities(val); - } - }); - files.push({ messages: messages, originalFilePath: originalFilePath, language: 'ps' }); - } - }); - resolve(files); - }); - }); -}; XLF.parse = function (xlfString) { return new Promise((resolve, reject) => { const parser = new xml2js.Parser(); @@ -222,8 +183,8 @@ XLF.parse = function (xlfString) { reject(new Error(`XLF parsing error: XLIFF file does not contain "xliff" or "file" node(s) required for parsing.`)); } fileNodes.forEach((file) => { - const originalFilePath = file.$.original; - if (!originalFilePath) { + const name = file.$.original; + if (!name) { reject(new Error(`XLF parsing error: XLIFF file node does not contain original attribute to determine the original location of the resource file.`)); } const language = file.$['target-language']; @@ -244,45 +205,18 @@ XLF.parse = function (xlfString) { val = val._ ? val._ : ''; } if (!key) { - reject(new Error(`XLF parsing error: trans-unit ${JSON.stringify(unit, undefined, 0)} defined in file ${originalFilePath} is missing the ID attribute.`)); + reject(new Error(`XLF parsing error: trans-unit ${JSON.stringify(unit, undefined, 0)} defined in file ${name} is missing the ID attribute.`)); return; } messages[key] = decodeEntities(val); }); - files.push({ messages: messages, originalFilePath: originalFilePath, language: language.toLowerCase() }); + files.push({ messages, name, language: language.toLowerCase() }); } }); resolve(files); }); }); }; -class Limiter { - constructor(maxDegreeOfParalellism) { - this.maxDegreeOfParalellism = maxDegreeOfParalellism; - this.outstandingPromises = []; - this.runningPromises = 0; - } - queue(factory) { - return new Promise((c, e) => { - this.outstandingPromises.push({ factory, c, e }); - this.consume(); - }); - } - consume() { - while (this.outstandingPromises.length && this.runningPromises < this.maxDegreeOfParalellism) { - const iLimitedTask = this.outstandingPromises.shift(); - this.runningPromises++; - const promise = iLimitedTask.factory(); - promise.then(iLimitedTask.c).catch(iLimitedTask.e); - promise.then(() => this.consumed()).catch(() => this.consumed()); - } - } - consumed() { - this.runningPromises--; - this.consume(); - } -} -exports.Limiter = Limiter; function sortLanguages(languages) { return languages.sort((a, b) => { return a.id < b.id ? -1 : (a.id > b.id ? 1 : 0); @@ -574,6 +508,28 @@ function createXlfFilesForCoreBundle() { }); } exports.createXlfFilesForCoreBundle = createXlfFilesForCoreBundle; +function createL10nBundleForExtension(extensionName) { + const result = (0, event_stream_1.through)(); + gulp.src([ + `extensions/${extensionName}/src/**/*.ts`, + ]).pipe((0, event_stream_1.writeArray)((err, files) => { + if (err) { + result.emit('error', err); + return; + } + const json = (0, l10n_dev_1.getL10nJson)(files.map(file => { + return file.contents.toString('utf8'); + })); + if (Object.keys(json).length > 0) { + result.emit('data', new File({ + path: `${extensionName}/bundle.l10n.json`, + contents: Buffer.from(JSON.stringify(json), 'utf8') + })); + } + result.emit('end'); + })); + return result; +} function createXlfFilesForExtensions() { let counter = 0; let folderStreamEnded = false; @@ -589,59 +545,51 @@ function createXlfFilesForExtensions() { return; } counter++; - let _xlf; - function getXlf() { - if (!_xlf) { - _xlf = new XLF(extensionsProject); + let _l10nMap; + function getL10nMap() { + if (!_l10nMap) { + _l10nMap = new Map(); } - return _xlf; + return _l10nMap; } - gulp.src([`.build/extensions/${extensionName}/package.nls.json`, `.build/extensions/${extensionName}/**/nls.metadata.json`], { allowEmpty: true }).pipe((0, event_stream_1.through)(function (file) { + (0, event_stream_1.merge)(gulp.src([`.build/extensions/${extensionName}/package.nls.json`, `.build/extensions/${extensionName}/**/nls.metadata.json`], { allowEmpty: true }), createL10nBundleForExtension(extensionName)).pipe((0, event_stream_1.through)(function (file) { if (file.isBuffer()) { const buffer = file.contents; const basename = path.basename(file.path); if (basename === 'package.nls.json') { const json = JSON.parse(buffer.toString('utf8')); - const keys = []; - const messages = []; - Object.keys(json).forEach((key) => { - const value = json[key]; - if (Is.string(value)) { - keys.push(key); - messages.push(value); - } - else if (value) { - keys.push({ - key, - comment: value.comment - }); - messages.push(value.message); - } - else { - keys.push(key); - messages.push(`Unknown message for key: ${key}`); - } - }); - getXlf().addFile(`extensions/${extensionName}/package`, keys, messages); + getL10nMap().set(`extensions/${extensionName}/package`, json); } else if (basename === 'nls.metadata.json') { const json = JSON.parse(buffer.toString('utf8')); const relPath = path.relative(`.build/extensions/${extensionName}`, path.dirname(file.path)); for (const file in json) { const fileContent = json[file]; - getXlf().addFile(`extensions/${extensionName}/${relPath}/${file}`, fileContent.keys, fileContent.messages); + const info = Object.create(null); + for (let i = 0; i < fileContent.messages.length; i++) { + const message = fileContent.messages[i]; + const { key, comment } = LocalizeInfo.is(fileContent.keys[i]) + ? fileContent.keys[i] + : { key: fileContent.keys[i], comment: undefined }; + info[key] = comment ? { message, comment } : message; + } + getL10nMap().set(`extensions/${extensionName}/${relPath}/${file}`, info); } } + else if (basename === 'bundle.l10n.json') { + const json = JSON.parse(buffer.toString('utf8')); + getL10nMap().set(`extensions/${extensionName}/bundle`, json); + } else { this.emit('error', new Error(`${file.path} is not a valid extension nls file`)); return; } } }, function () { - if (_xlf) { + if (_l10nMap?.size > 0) { const xlfFile = new File({ path: path.join(extensionsProject, extensionName + '.xlf'), - contents: Buffer.from(_xlf.toString(), 'utf8') + contents: Buffer.from((0, l10n_dev_1.getL10nXlf)(_l10nMap), 'utf8') }); folderStream.queue(xlfFile); } @@ -712,299 +660,7 @@ function createXlfFilesForIsl() { }); } exports.createXlfFilesForIsl = createXlfFilesForIsl; -function pushXlfFiles(apiHostname, username, password) { - const tryGetPromises = []; - const updateCreatePromises = []; - return (0, event_stream_1.through)(function (file) { - const project = path.dirname(file.relative); - const fileName = path.basename(file.path); - const slug = fileName.substr(0, fileName.length - '.xlf'.length); - const credentials = `${username}:${password}`; - // Check if resource already exists, if not, then create it. - let promise = tryGetResource(project, slug, apiHostname, credentials); - tryGetPromises.push(promise); - promise.then(exists => { - if (exists) { - promise = updateResource(project, slug, file, apiHostname, credentials); - } - else { - promise = createResource(project, slug, file, apiHostname, credentials); - } - updateCreatePromises.push(promise); - }); - }, function () { - // End the pipe only after all the communication with Transifex API happened - Promise.all(tryGetPromises).then(() => { - Promise.all(updateCreatePromises).then(() => { - this.queue(null); - }).catch((reason) => { throw new Error(reason); }); - }).catch((reason) => { throw new Error(reason); }); - }); -} -exports.pushXlfFiles = pushXlfFiles; -function getAllResources(project, apiHostname, username, password) { - return new Promise((resolve, reject) => { - const credentials = `${username}:${password}`; - const options = { - hostname: apiHostname, - path: `/api/2/project/${project}/resources`, - auth: credentials, - method: 'GET' - }; - const request = https.request(options, (res) => { - const buffer = []; - res.on('data', (chunk) => buffer.push(chunk)); - res.on('end', () => { - if (res.statusCode === 200) { - const json = JSON.parse(Buffer.concat(buffer).toString()); - if (Array.isArray(json)) { - resolve(json.map(o => o.slug)); - return; - } - reject(`Unexpected data format. Response code: ${res.statusCode}.`); - } - else { - reject(`No resources in ${project} returned no data. Response code: ${res.statusCode}.`); - } - }); - }); - request.on('error', (err) => { - reject(`Failed to query resources in ${project} with the following error: ${err}. ${options.path}`); - }); - request.end(); - }); -} -function findObsoleteResources(apiHostname, username, password) { - const resourcesByProject = Object.create(null); - resourcesByProject[extensionsProject] = [].concat(exports.externalExtensionsWithTranslations); // clone - return (0, event_stream_1.through)(function (file) { - const project = path.dirname(file.relative); - const fileName = path.basename(file.path); - const slug = fileName.substr(0, fileName.length - '.xlf'.length); - let slugs = resourcesByProject[project]; - if (!slugs) { - resourcesByProject[project] = slugs = []; - } - slugs.push(slug); - this.push(file); - }, function () { - const json = JSON.parse(fs.readFileSync('./build/lib/i18n.resources.json', 'utf8')); - const i18Resources = [...json.editor, ...json.workbench].map((r) => r.project + '/' + r.name.replace(/\//g, '_')); - const extractedResources = []; - for (const project of [workbenchProject, editorProject]) { - for (const resource of resourcesByProject[project]) { - if (resource !== 'setup_messages') { - extractedResources.push(project + '/' + resource); - } - } - } - if (i18Resources.length !== extractedResources.length) { - console.log(`[i18n] Obsolete resources in file 'build/lib/i18n.resources.json': JSON.stringify(${i18Resources.filter(p => extractedResources.indexOf(p) === -1)})`); - console.log(`[i18n] Missing resources in file 'build/lib/i18n.resources.json': JSON.stringify(${extractedResources.filter(p => i18Resources.indexOf(p) === -1)})`); - } - const promises = []; - for (const project in resourcesByProject) { - promises.push(getAllResources(project, apiHostname, username, password).then(resources => { - const expectedResources = resourcesByProject[project]; - const unusedResources = resources.filter(resource => resource && expectedResources.indexOf(resource) === -1); - if (unusedResources.length) { - console.log(`[transifex] Obsolete resources in project '${project}': ${unusedResources.join(', ')}`); - } - })); - } - return Promise.all(promises).then(_ => { - this.push(null); - }).catch((reason) => { throw new Error(reason); }); - }); -} -exports.findObsoleteResources = findObsoleteResources; -function tryGetResource(project, slug, apiHostname, credentials) { - return new Promise((resolve, reject) => { - const options = { - hostname: apiHostname, - path: `/api/2/project/${project}/resource/${slug}/?details`, - auth: credentials, - method: 'GET' - }; - const request = https.request(options, (response) => { - if (response.statusCode === 404) { - resolve(false); - } - else if (response.statusCode === 200) { - resolve(true); - } - else { - reject(`Failed to query resource ${project}/${slug}. Response: ${response.statusCode} ${response.statusMessage}`); - } - }); - request.on('error', (err) => { - reject(`Failed to get ${project}/${slug} on Transifex: ${err}`); - }); - request.end(); - }); -} -function createResource(project, slug, xlfFile, apiHostname, credentials) { - return new Promise((_resolve, reject) => { - const data = JSON.stringify({ - 'content': xlfFile.contents.toString(), - 'name': slug, - 'slug': slug, - 'i18n_type': 'XLIFF' - }); - const options = { - hostname: apiHostname, - path: `/api/2/project/${project}/resources`, - headers: { - 'Content-Type': 'application/json', - 'Content-Length': Buffer.byteLength(data) - }, - auth: credentials, - method: 'POST' - }; - const request = https.request(options, (res) => { - if (res.statusCode === 201) { - log(`Resource ${project}/${slug} successfully created on Transifex.`); - } - else { - reject(`Something went wrong in the request creating ${slug} in ${project}. ${res.statusCode}`); - } - }); - request.on('error', (err) => { - reject(`Failed to create ${project}/${slug} on Transifex: ${err}`); - }); - request.write(data); - request.end(); - }); -} -/** - * The following link provides information about how Transifex handles updates of a resource file: - * https://dev.befoolish.co/tx-docs/public/projects/updating-content#what-happens-when-you-update-files - */ -function updateResource(project, slug, xlfFile, apiHostname, credentials) { - return new Promise((resolve, reject) => { - const data = JSON.stringify({ content: xlfFile.contents.toString() }); - const options = { - hostname: apiHostname, - path: `/api/2/project/${project}/resource/${slug}/content`, - headers: { - 'Content-Type': 'application/json', - 'Content-Length': Buffer.byteLength(data) - }, - auth: credentials, - method: 'PUT' - }; - const request = https.request(options, (res) => { - if (res.statusCode === 200) { - res.setEncoding('utf8'); - let responseBuffer = ''; - res.on('data', function (chunk) { - responseBuffer += chunk; - }); - res.on('end', () => { - const response = JSON.parse(responseBuffer); - log(`Resource ${project}/${slug} successfully updated on Transifex. Strings added: ${response.strings_added}, updated: ${response.strings_added}, deleted: ${response.strings_added}`); - resolve(); - }); - } - else { - reject(`Something went wrong in the request updating ${slug} in ${project}. ${res.statusCode}`); - } - }); - request.on('error', (err) => { - reject(`Failed to update ${project}/${slug} on Transifex: ${err}`); - }); - request.write(data); - request.end(); - }); -} -function pullSetupXlfFiles(apiHostname, username, password, language, includeDefault) { - const setupResources = [{ name: 'setup_messages', project: workbenchProject }]; - if (includeDefault) { - setupResources.push({ name: 'setup_default', project: setupProject }); - } - return pullXlfFiles(apiHostname, username, password, language, setupResources); -} -exports.pullSetupXlfFiles = pullSetupXlfFiles; -function pullXlfFiles(apiHostname, username, password, language, resources) { - const credentials = `${username}:${password}`; - const expectedTranslationsCount = resources.length; - let translationsRetrieved = 0, called = false; - return (0, event_stream_1.readable)(function (_count, callback) { - // Mark end of stream when all resources were retrieved - if (translationsRetrieved === expectedTranslationsCount) { - return this.emit('end'); - } - if (!called) { - called = true; - const stream = this; - resources.map(function (resource) { - retrieveResource(language, resource, apiHostname, credentials).then((file) => { - if (file) { - stream.emit('data', file); - } - translationsRetrieved++; - }).catch(error => { throw new Error(error); }); - }); - } - callback(); - }); -} -const limiter = new Limiter(NUMBER_OF_CONCURRENT_DOWNLOADS); -function retrieveResource(language, resource, apiHostname, credentials) { - return limiter.queue(() => new Promise((resolve, reject) => { - const slug = resource.name.replace(/\//g, '_'); - const project = resource.project; - const 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`, - auth: credentials, - port: 443, - method: 'GET' - }; - console.log('[transifex] Fetching ' + options.path); - const request = https.request(options, (res) => { - const xlfBuffer = []; - res.on('data', (chunk) => xlfBuffer.push(chunk)); - res.on('end', () => { - if (res.statusCode === 200) { - resolve(new File({ contents: Buffer.concat(xlfBuffer), path: `${project}/${slug}.xlf` })); - } - else if (res.statusCode === 404) { - console.log(`[transifex] ${slug} in ${project} returned no data.`); - resolve(null); - } - else { - reject(`${slug} in ${project} returned no data. Response code: ${res.statusCode}.`); - } - }); - }); - request.on('error', (err) => { - reject(`Failed to query resource ${slug} with the following error: ${err}. ${options.path}`); - }); - request.end(); - })); -} -function prepareI18nFiles() { - const parsePromises = []; - return (0, event_stream_1.through)(function (xlf) { - const stream = this; - const parsePromise = XLF.parse(xlf.contents.toString()); - parsePromises.push(parsePromise); - parsePromise.then(resolvedFiles => { - resolvedFiles.forEach(file => { - const translatedFile = createI18nFile(file.originalFilePath, file.messages); - stream.queue(translatedFile); - }); - }); - }, function () { - Promise.all(parsePromises) - .then(() => { this.queue(null); }) - .catch(reason => { throw new Error(reason); }); - }); -} -exports.prepareI18nFiles = prepareI18nFiles; -function createI18nFile(originalFilePath, messages) { +function createI18nFile(name, messages) { const result = Object.create(null); result[''] = [ '--------------------------------------------------------------------------------------------', @@ -1021,12 +677,20 @@ function createI18nFile(originalFilePath, messages) { content = content.replace(/\n/g, '\r\n'); } return new File({ - path: path.join(originalFilePath + '.i18n.json'), + path: path.join(name + '.i18n.json'), contents: Buffer.from(content, 'utf8') }); } const i18nPackVersion = '1.0.0'; -function prepareI18nPackFiles(externalExtensions, resultingTranslationPaths, pseudo = false) { +function getRecordFromL10nJsonFormat(l10nJsonFormat) { + const record = {}; + for (const key of Object.keys(l10nJsonFormat)) { + const value = l10nJsonFormat[key]; + record[key] = typeof value === 'string' ? value : value.message; + } + return record; +} +function prepareI18nPackFiles(resultingTranslationPaths) { const parsePromises = []; const mainPack = { version: i18nPackVersion, contents: {} }; const extensionsPacks = {}; @@ -1036,28 +700,28 @@ function prepareI18nPackFiles(externalExtensions, resultingTranslationPaths, pse const resource = path.basename(xlf.relative, '.xlf'); const contents = xlf.contents.toString(); log(`Found ${project}: ${resource}`); - const parsePromise = pseudo ? XLF.parsePseudo(contents) : XLF.parse(contents); + const parsePromise = (0, l10n_dev_1.getL10nFilesFromXlf)(contents); parsePromises.push(parsePromise); parsePromise.then(resolvedFiles => { resolvedFiles.forEach(file => { - const path = file.originalFilePath; + const path = file.name; const firstSlash = path.indexOf('/'); if (project === extensionsProject) { let extPack = extensionsPacks[resource]; if (!extPack) { extPack = extensionsPacks[resource] = { version: i18nPackVersion, contents: {} }; } - const externalId = externalExtensions[resource]; + const externalId = externalExtensionsWithTranslations[resource]; if (!externalId) { // internal extension: remove 'extensions/extensionId/' segnent const secondSlash = path.indexOf('/', firstSlash + 1); - extPack.contents[path.substr(secondSlash + 1)] = file.messages; + extPack.contents[path.substring(secondSlash + 1)] = getRecordFromL10nJsonFormat(file.messages); } else { - extPack.contents[path] = file.messages; + extPack.contents[path] = getRecordFromL10nJsonFormat(file.messages); } } else { - mainPack.contents[path.substr(firstSlash + 1)] = file.messages; + mainPack.contents[path.substring(firstSlash + 1)] = getRecordFromL10nJsonFormat(file.messages); } }); }).catch(reason => { @@ -1075,7 +739,7 @@ function prepareI18nPackFiles(externalExtensions, resultingTranslationPaths, pse for (const extension in extensionsPacks) { const translatedExtFile = createI18nFile(`extensions/${extension}`, extensionsPacks[extension]); this.queue(translatedExtFile); - const externalExtensionId = externalExtensions[extension]; + const externalExtensionId = externalExtensionsWithTranslations[extension]; if (externalExtensionId) { resultingTranslationPaths.push({ id: externalExtensionId, resourceName: `extensions/${extension}.i18n.json` }); } @@ -1099,7 +763,7 @@ function prepareIslFiles(language, innoSetupConfig) { parsePromises.push(parsePromise); parsePromise.then(resolvedFiles => { resolvedFiles.forEach(file => { - const translatedFile = createIslFile(file.originalFilePath, file.messages, language, innoSetupConfig); + const translatedFile = createIslFile(file.name, file.messages, language, innoSetupConfig); stream.queue(translatedFile); }); }).catch(reason => { @@ -1114,14 +778,14 @@ function prepareIslFiles(language, innoSetupConfig) { }); } exports.prepareIslFiles = prepareIslFiles; -function createIslFile(originalFilePath, messages, language, innoSetup) { +function createIslFile(name, messages, language, innoSetup) { const content = []; let originalContent; - if (path.basename(originalFilePath) === 'Default') { - originalContent = new TextModel(fs.readFileSync(originalFilePath + '.isl', 'utf8')); + if (path.basename(name) === 'Default') { + originalContent = new TextModel(fs.readFileSync(name + '.isl', 'utf8')); } else { - originalContent = new TextModel(fs.readFileSync(originalFilePath + '.en.isl', 'utf8')); + originalContent = new TextModel(fs.readFileSync(name + '.en.isl', 'utf8')); } originalContent.lines.forEach(line => { if (line.length > 0) { @@ -1143,7 +807,7 @@ function createIslFile(originalFilePath, messages, language, innoSetup) { } } }); - const basename = path.basename(originalFilePath); + const basename = path.basename(name); const filePath = `${basename}.${language.id}.isl`; const encoded = iconv.encode(Buffer.from(content.join('\r\n'), 'utf8').toString(), innoSetup.codePage); return new File({ @@ -1174,6 +838,3 @@ function encodeEntities(value) { function decodeEntities(value) { return value.replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&'); } -function pseudify(message) { - return '\uFF3B' + message.replace(/[aouei]/g, '$&$&') + '\uFF3D'; -} diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index dd949e618f2..5b51df074b5 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -306,6 +306,10 @@ "name": "vs/workbench/contrib/offline", "project": "vscode-workbench" }, + { + "name": "vs/workbench/contrib/remoteTunnel", + "project": "vscode-workbench" + }, { "name": "vs/workbench/services/actions", "project": "vscode-workbench" diff --git a/build/lib/i18n.ts b/build/lib/i18n.ts index 20c1dc93f99..1962ba98926 100644 --- a/build/lib/i18n.ts +++ b/build/lib/i18n.ts @@ -6,17 +6,15 @@ import * as path from 'path'; import * as fs from 'fs'; -import { through, readable, ThroughStream } from 'event-stream'; +import { merge, through, ThroughStream, writeArray } from 'event-stream'; import * as File from 'vinyl'; import * as Is from 'is'; import * as xml2js from 'xml2js'; -import * as https from 'https'; import * as gulp from 'gulp'; import * as fancyLog from 'fancy-log'; import * as ansiColors from 'ansi-colors'; import * as iconv from '@vscode/iconv-lite-umd'; - -const NUMBER_OF_CONCURRENT_DOWNLOADS = 4; +import { l10nJsonFormat, getL10nXlf, l10nJsonDetails, getL10nFilesFromXlf, getL10nJson } from '@vscode/l10n-dev'; function log(message: any, ...rest: any[]): void { fancyLog(ansiColors.green('[i18n]'), message, ...rest); @@ -52,17 +50,12 @@ export const extraLanguages: Language[] = [ ]; // non built-in extensions also that are transifex and need to be part of the language packs -export const externalExtensionsWithTranslations = { +const externalExtensionsWithTranslations: Record = { 'vscode-chrome-debug': 'msjsdiag.debugger-for-chrome', 'vscode-node-debug': 'ms-vscode.node-debug', 'vscode-node-debug2': 'ms-vscode.node-debug2' }; - -interface Map { - [key: string]: V; -} - interface Item { id: string; message: string; @@ -74,12 +67,6 @@ export interface Resource { project: string; } -interface ParsedXLF { - messages: Map; - originalFilePath: string; - language: string; -} - interface LocalizeInfo { key: string; comment: string[]; @@ -93,9 +80,9 @@ module LocalizeInfo { } interface BundledFormat { - keys: Map<(string | LocalizeInfo)[]>; - messages: Map; - bundles: Map; + keys: Record; + messages: Record; + bundles: Record; } module BundledFormat { @@ -111,27 +98,6 @@ module BundledFormat { } } -interface ValueFormat { - message: string; - comment: string[]; -} - -interface PackageJsonFormat { - [key: string]: string | ValueFormat; -} - -module PackageJsonFormat { - export function is(value: any): value is PackageJsonFormat { - if (Is.undef(value) || !Is.object(value)) { - return false; - } - return Object.keys(value).every(key => { - const element = value[key]; - return Is.string(element) || (Is.object(element) && Is.defined(element.message) && Is.defined(element.comment)); - }); - } -} - interface BundledExtensionFormat { [key: string]: { messages: string[]; @@ -181,7 +147,7 @@ class TextModel { export class XLF { private buffer: string[]; - private files: Map; + private files: Record; public numberOfMessages: number; constructor(public project: string) { @@ -274,37 +240,11 @@ export class XLF { this.buffer.push(line.toString()); } - static parsePseudo = function (xlfString: string): Promise { - return new Promise((resolve) => { - const parser = new xml2js.Parser(); - const files: { messages: Map; originalFilePath: string; language: string }[] = []; - parser.parseString(xlfString, function (_err: any, result: any) { - const fileNodes: any[] = result['xliff']['file']; - fileNodes.forEach(file => { - const originalFilePath = file.$.original; - const messages: Map = {}; - const transUnits = file.body[0]['trans-unit']; - if (transUnits) { - transUnits.forEach((unit: any) => { - const key = unit.$.id; - const val = pseudify(unit.source[0]['_'].toString()); - if (key && val) { - messages[key] = decodeEntities(val); - } - }); - files.push({ messages: messages, originalFilePath: originalFilePath, language: 'ps' }); - } - }); - resolve(files); - }); - }); - }; - - static parse = function (xlfString: string): Promise { + static parse = function (xlfString: string): Promise { return new Promise((resolve, reject) => { const parser = new xml2js.Parser(); - const files: { messages: Map; originalFilePath: string; language: string }[] = []; + const files: { messages: Record; name: string; language: string }[] = []; parser.parseString(xlfString, function (err: any, result: any) { if (err) { @@ -317,15 +257,15 @@ export class XLF { } fileNodes.forEach((file) => { - const originalFilePath = file.$.original; - if (!originalFilePath) { + const name = file.$.original; + if (!name) { reject(new Error(`XLF parsing error: XLIFF file node does not contain original attribute to determine the original location of the resource file.`)); } const language = file.$['target-language']; if (!language) { reject(new Error(`XLF parsing error: XLIFF file node does not contain target-language attribute to determine translated language.`)); } - const messages: Map = {}; + const messages: Record = {}; const transUnits = file.body[0]['trans-unit']; if (transUnits) { @@ -341,12 +281,12 @@ export class XLF { val = val._ ? val._ : ''; } if (!key) { - reject(new Error(`XLF parsing error: trans-unit ${JSON.stringify(unit, undefined, 0)} defined in file ${originalFilePath} is missing the ID attribute.`)); + reject(new Error(`XLF parsing error: trans-unit ${JSON.stringify(unit, undefined, 0)} defined in file ${name} is missing the ID attribute.`)); return; } messages[key] = decodeEntities(val); }); - files.push({ messages: messages, originalFilePath: originalFilePath, language: language.toLowerCase() }); + files.push({ messages, name, language: language.toLowerCase() }); } }); @@ -356,49 +296,6 @@ export class XLF { }; } -export interface ITask { - (): T; -} - -interface ILimitedTaskFactory { - factory: ITask>; - c: (value?: T | Promise) => void; - e: (error?: any) => void; -} - -export class Limiter { - private runningPromises: number; - private outstandingPromises: ILimitedTaskFactory[]; - - constructor(private maxDegreeOfParalellism: number) { - this.outstandingPromises = []; - this.runningPromises = 0; - } - - queue(factory: ITask>): Promise { - return new Promise((c, e) => { - this.outstandingPromises.push({ factory, c, e }); - this.consume(); - }); - } - - private consume(): void { - while (this.outstandingPromises.length && this.runningPromises < this.maxDegreeOfParalellism) { - const iLimitedTask = this.outstandingPromises.shift()!; - this.runningPromises++; - - const promise = iLimitedTask.factory(); - promise.then(iLimitedTask.c).catch(iLimitedTask.e); - promise.then(() => this.consumed()).catch(() => this.consumed()); - } - } - - private consumed(): void { - this.runningPromises--; - this.consume(); - } -} - function sortLanguages(languages: Language[]): Language[] { return languages.sort((a: Language, b: Language): number => { return a.id < b.id ? -1 : (a.id > b.id ? 1 : 0); @@ -480,9 +377,9 @@ function processCoreBundleFormat(fileHeader: string, languages: Language[], json const messageSection = json.messages; const bundleSection = json.bundles; - const statistics: Map = Object.create(null); + const statistics: Record = Object.create(null); - const defaultMessages: Map> = Object.create(null); + const defaultMessages: Record> = Object.create(null); const modules = Object.keys(keysSection); modules.forEach((module) => { const keys = keysSection[module]; @@ -491,7 +388,7 @@ function processCoreBundleFormat(fileHeader: string, languages: Language[], json emitter.emit('error', `Message for module ${module} corrupted. Mismatch in number of keys and messages.`); return; } - const messageMap: Map = Object.create(null); + const messageMap: Record = Object.create(null); defaultMessages[module] = messageMap; keys.map((key, i) => { if (typeof key === 'string') { @@ -514,7 +411,7 @@ function processCoreBundleFormat(fileHeader: string, languages: Language[], json } statistics[language.id] = 0; - const localizedModules: Map = Object.create(null); + const localizedModules: Record = Object.create(null); const languageFolderName = language.translationId || language.id; const i18nFile = path.join(languageDirectory, `vscode-language-pack-${languageFolderName}`, 'translations', 'main.i18n.json'); let allMessages: I18nFormat | undefined; @@ -648,7 +545,7 @@ export function createXlfFilesForCoreBundle(): ThroughStream { const basename = path.basename(file.path); if (basename === 'nls.metadata.json') { if (file.isBuffer()) { - const xlfs: Map = Object.create(null); + const xlfs: Record = Object.create(null); const json: BundledFormat = JSON.parse((file.contents as Buffer).toString('utf8')); for (const coreModule in json.keys) { const projectResource = getResource(coreModule); @@ -689,6 +586,32 @@ export function createXlfFilesForCoreBundle(): ThroughStream { }); } +function createL10nBundleForExtension(extensionName: string): ThroughStream { + const result = through(); + gulp.src([ + `extensions/${extensionName}/src/**/*.ts`, + ]).pipe(writeArray((err, files: File[]) => { + if (err) { + result.emit('error', err); + return; + } + + const json = getL10nJson(files.map(file => { + return file.contents.toString('utf8'); + })); + + if (Object.keys(json).length > 0) { + result.emit('data', new File({ + path: `${extensionName}/bundle.l10n.json`, + contents: Buffer.from(JSON.stringify(json), 'utf8') + })); + } + result.emit('end'); + })); + + return result; +} + export function createXlfFilesForExtensions(): ThroughStream { let counter: number = 0; let folderStreamEnded: boolean = false; @@ -704,55 +627,52 @@ export function createXlfFilesForExtensions(): ThroughStream { return; } counter++; - let _xlf: XLF; - function getXlf() { - if (!_xlf) { - _xlf = new XLF(extensionsProject); + let _l10nMap: Map; + function getL10nMap() { + if (!_l10nMap) { + _l10nMap = new Map(); } - return _xlf; + return _l10nMap; } - gulp.src([`.build/extensions/${extensionName}/package.nls.json`, `.build/extensions/${extensionName}/**/nls.metadata.json`], { allowEmpty: true }).pipe(through(function (file: File) { + merge( + gulp.src([`.build/extensions/${extensionName}/package.nls.json`, `.build/extensions/${extensionName}/**/nls.metadata.json`], { allowEmpty: true }), + createL10nBundleForExtension(extensionName) + ).pipe(through(function (file: File) { if (file.isBuffer()) { const buffer: Buffer = file.contents as Buffer; const basename = path.basename(file.path); if (basename === 'package.nls.json') { - const json: PackageJsonFormat = JSON.parse(buffer.toString('utf8')); - const keys: Array = []; - const messages: string[] = []; - Object.keys(json).forEach((key) => { - const value = json[key]; - if (Is.string(value)) { - keys.push(key); - messages.push(value); - } else if (value) { - keys.push({ - key, - comment: value.comment - }); - messages.push(value.message); - } else { - keys.push(key); - messages.push(`Unknown message for key: ${key}`); - } - }); - getXlf().addFile(`extensions/${extensionName}/package`, keys, messages); + const json: l10nJsonFormat = JSON.parse(buffer.toString('utf8')); + getL10nMap().set(`extensions/${extensionName}/package`, json); } else if (basename === 'nls.metadata.json') { const json: BundledExtensionFormat = JSON.parse(buffer.toString('utf8')); const relPath = path.relative(`.build/extensions/${extensionName}`, path.dirname(file.path)); for (const file in json) { const fileContent = json[file]; - getXlf().addFile(`extensions/${extensionName}/${relPath}/${file}`, fileContent.keys, fileContent.messages); + const info: l10nJsonFormat = Object.create(null); + for (let i = 0; i < fileContent.messages.length; i++) { + const message = fileContent.messages[i]; + const { key, comment } = LocalizeInfo.is(fileContent.keys[i]) + ? fileContent.keys[i] as LocalizeInfo + : { key: fileContent.keys[i] as string, comment: undefined }; + + info[key] = comment ? { message, comment } : message; + } + getL10nMap().set(`extensions/${extensionName}/${relPath}/${file}`, info); } + } else if (basename === 'bundle.l10n.json') { + const json: l10nJsonFormat = JSON.parse(buffer.toString('utf8')); + getL10nMap().set(`extensions/${extensionName}/bundle`, json); } else { this.emit('error', new Error(`${file.path} is not a valid extension nls file`)); return; } } }, function () { - if (_xlf) { + if (_l10nMap?.size > 0) { const xlfFile = new File({ path: path.join(extensionsProject, extensionName + '.xlf'), - contents: Buffer.from(_xlf.toString(), 'utf8') + contents: Buffer.from(getL10nXlf(_l10nMap), 'utf8') }); folderStream.queue(xlfFile); } @@ -828,321 +748,7 @@ export function createXlfFilesForIsl(): ThroughStream { }); } -export function pushXlfFiles(apiHostname: string, username: string, password: string): ThroughStream { - const tryGetPromises: Array> = []; - const updateCreatePromises: Array> = []; - - return through(function (this: ThroughStream, file: File) { - const project = path.dirname(file.relative); - const fileName = path.basename(file.path); - const slug = fileName.substr(0, fileName.length - '.xlf'.length); - const credentials = `${username}:${password}`; - - // Check if resource already exists, if not, then create it. - let promise = tryGetResource(project, slug, apiHostname, credentials); - tryGetPromises.push(promise); - promise.then(exists => { - if (exists) { - promise = updateResource(project, slug, file, apiHostname, credentials); - } else { - promise = createResource(project, slug, file, apiHostname, credentials); - } - updateCreatePromises.push(promise); - }); - - }, function () { - // End the pipe only after all the communication with Transifex API happened - Promise.all(tryGetPromises).then(() => { - Promise.all(updateCreatePromises).then(() => { - this.queue(null); - }).catch((reason) => { throw new Error(reason); }); - }).catch((reason) => { throw new Error(reason); }); - }); -} - -function getAllResources(project: string, apiHostname: string, username: string, password: string): Promise { - return new Promise((resolve, reject) => { - const credentials = `${username}:${password}`; - const options = { - hostname: apiHostname, - path: `/api/2/project/${project}/resources`, - auth: credentials, - method: 'GET' - }; - - const request = https.request(options, (res) => { - const buffer: Buffer[] = []; - res.on('data', (chunk: Buffer) => buffer.push(chunk)); - res.on('end', () => { - if (res.statusCode === 200) { - const json = JSON.parse(Buffer.concat(buffer).toString()); - if (Array.isArray(json)) { - resolve(json.map(o => o.slug)); - return; - } - reject(`Unexpected data format. Response code: ${res.statusCode}.`); - } else { - reject(`No resources in ${project} returned no data. Response code: ${res.statusCode}.`); - } - }); - }); - request.on('error', (err) => { - reject(`Failed to query resources in ${project} with the following error: ${err}. ${options.path}`); - }); - request.end(); - }); -} - -export function findObsoleteResources(apiHostname: string, username: string, password: string): ThroughStream { - const resourcesByProject: Map = Object.create(null); - resourcesByProject[extensionsProject] = ([] as any[]).concat(externalExtensionsWithTranslations); // clone - - return through(function (this: ThroughStream, file: File) { - const project = path.dirname(file.relative); - const fileName = path.basename(file.path); - const slug = fileName.substr(0, fileName.length - '.xlf'.length); - - let slugs = resourcesByProject[project]; - if (!slugs) { - resourcesByProject[project] = slugs = []; - } - slugs.push(slug); - this.push(file); - }, function () { - - const json = JSON.parse(fs.readFileSync('./build/lib/i18n.resources.json', 'utf8')); - const i18Resources = [...json.editor, ...json.workbench].map((r: Resource) => r.project + '/' + r.name.replace(/\//g, '_')); - const extractedResources: string[] = []; - for (const project of [workbenchProject, editorProject]) { - for (const resource of resourcesByProject[project]) { - if (resource !== 'setup_messages') { - extractedResources.push(project + '/' + resource); - } - } - } - if (i18Resources.length !== extractedResources.length) { - console.log(`[i18n] Obsolete resources in file 'build/lib/i18n.resources.json': JSON.stringify(${i18Resources.filter(p => extractedResources.indexOf(p) === -1)})`); - console.log(`[i18n] Missing resources in file 'build/lib/i18n.resources.json': JSON.stringify(${extractedResources.filter(p => i18Resources.indexOf(p) === -1)})`); - } - - const promises: Array> = []; - for (const project in resourcesByProject) { - promises.push( - getAllResources(project, apiHostname, username, password).then(resources => { - const expectedResources = resourcesByProject[project]; - const unusedResources = resources.filter(resource => resource && expectedResources.indexOf(resource) === -1); - if (unusedResources.length) { - console.log(`[transifex] Obsolete resources in project '${project}': ${unusedResources.join(', ')}`); - } - }) - ); - } - return Promise.all(promises).then(_ => { - this.push(null); - }).catch((reason) => { throw new Error(reason); }); - }); -} - -function tryGetResource(project: string, slug: string, apiHostname: string, credentials: string): Promise { - return new Promise((resolve, reject) => { - const options = { - hostname: apiHostname, - path: `/api/2/project/${project}/resource/${slug}/?details`, - auth: credentials, - method: 'GET' - }; - - const request = https.request(options, (response) => { - if (response.statusCode === 404) { - resolve(false); - } else if (response.statusCode === 200) { - resolve(true); - } else { - reject(`Failed to query resource ${project}/${slug}. Response: ${response.statusCode} ${response.statusMessage}`); - } - }); - request.on('error', (err) => { - reject(`Failed to get ${project}/${slug} on Transifex: ${err}`); - }); - - request.end(); - }); -} - -function createResource(project: string, slug: string, xlfFile: File, apiHostname: string, credentials: any): Promise { - return new Promise((_resolve, reject) => { - const data = JSON.stringify({ - 'content': xlfFile.contents.toString(), - 'name': slug, - 'slug': slug, - 'i18n_type': 'XLIFF' - }); - const options = { - hostname: apiHostname, - path: `/api/2/project/${project}/resources`, - headers: { - 'Content-Type': 'application/json', - 'Content-Length': Buffer.byteLength(data) - }, - auth: credentials, - method: 'POST' - }; - - const request = https.request(options, (res) => { - if (res.statusCode === 201) { - log(`Resource ${project}/${slug} successfully created on Transifex.`); - } else { - reject(`Something went wrong in the request creating ${slug} in ${project}. ${res.statusCode}`); - } - }); - request.on('error', (err) => { - reject(`Failed to create ${project}/${slug} on Transifex: ${err}`); - }); - - request.write(data); - request.end(); - }); -} - -/** - * The following link provides information about how Transifex handles updates of a resource file: - * https://dev.befoolish.co/tx-docs/public/projects/updating-content#what-happens-when-you-update-files - */ -function updateResource(project: string, slug: string, xlfFile: File, apiHostname: string, credentials: string): Promise { - return new Promise((resolve, reject) => { - const data = JSON.stringify({ content: xlfFile.contents.toString() }); - const options = { - hostname: apiHostname, - path: `/api/2/project/${project}/resource/${slug}/content`, - headers: { - 'Content-Type': 'application/json', - 'Content-Length': Buffer.byteLength(data) - }, - auth: credentials, - method: 'PUT' - }; - - const request = https.request(options, (res) => { - if (res.statusCode === 200) { - res.setEncoding('utf8'); - - let responseBuffer: string = ''; - res.on('data', function (chunk) { - responseBuffer += chunk; - }); - res.on('end', () => { - const response = JSON.parse(responseBuffer); - log(`Resource ${project}/${slug} successfully updated on Transifex. Strings added: ${response.strings_added}, updated: ${response.strings_added}, deleted: ${response.strings_added}`); - resolve(); - }); - } else { - reject(`Something went wrong in the request updating ${slug} in ${project}. ${res.statusCode}`); - } - }); - request.on('error', (err) => { - reject(`Failed to update ${project}/${slug} on Transifex: ${err}`); - }); - - request.write(data); - request.end(); - }); -} - -export function pullSetupXlfFiles(apiHostname: string, username: string, password: string, language: Language, includeDefault: boolean): NodeJS.ReadableStream { - const setupResources = [{ name: 'setup_messages', project: workbenchProject }]; - if (includeDefault) { - setupResources.push({ name: 'setup_default', project: setupProject }); - } - return pullXlfFiles(apiHostname, username, password, language, setupResources); -} - -function pullXlfFiles(apiHostname: string, username: string, password: string, language: Language, resources: Resource[]): NodeJS.ReadableStream { - const credentials = `${username}:${password}`; - const expectedTranslationsCount = resources.length; - let translationsRetrieved = 0, called = false; - - return readable(function (_count: any, callback: any) { - // Mark end of stream when all resources were retrieved - if (translationsRetrieved === expectedTranslationsCount) { - return this.emit('end'); - } - - if (!called) { - called = true; - const stream = this; - resources.map(function (resource) { - retrieveResource(language, resource, apiHostname, credentials).then((file: File | null) => { - if (file) { - stream.emit('data', file); - } - translationsRetrieved++; - }).catch(error => { throw new Error(error); }); - }); - } - - callback(); - }); -} -const limiter = new Limiter(NUMBER_OF_CONCURRENT_DOWNLOADS); - -function retrieveResource(language: Language, resource: Resource, apiHostname: string, credentials: string): Promise { - return limiter.queue(() => new Promise((resolve, reject) => { - const slug = resource.name.replace(/\//g, '_'); - const project = resource.project; - const 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`, - auth: credentials, - port: 443, - method: 'GET' - }; - console.log('[transifex] Fetching ' + options.path); - - const request = https.request(options, (res) => { - const xlfBuffer: Buffer[] = []; - res.on('data', (chunk: Buffer) => xlfBuffer.push(chunk)); - res.on('end', () => { - if (res.statusCode === 200) { - resolve(new File({ contents: Buffer.concat(xlfBuffer), path: `${project}/${slug}.xlf` })); - } else if (res.statusCode === 404) { - console.log(`[transifex] ${slug} in ${project} returned no data.`); - resolve(null); - } else { - reject(`${slug} in ${project} returned no data. Response code: ${res.statusCode}.`); - } - }); - }); - request.on('error', (err) => { - reject(`Failed to query resource ${slug} with the following error: ${err}. ${options.path}`); - }); - request.end(); - })); -} - -export function prepareI18nFiles(): ThroughStream { - const parsePromises: Promise[] = []; - - return through(function (this: ThroughStream, xlf: File) { - const stream = this; - const parsePromise = XLF.parse(xlf.contents.toString()); - parsePromises.push(parsePromise); - parsePromise.then( - resolvedFiles => { - resolvedFiles.forEach(file => { - const translatedFile = createI18nFile(file.originalFilePath, file.messages); - stream.queue(translatedFile); - }); - } - ); - }, function () { - Promise.all(parsePromises) - .then(() => { this.queue(null); }) - .catch(reason => { throw new Error(reason); }); - }); -} - -function createI18nFile(originalFilePath: string, messages: any): File { +function createI18nFile(name: string, messages: any): File { const result = Object.create(null); result[''] = [ '--------------------------------------------------------------------------------------------', @@ -1160,7 +766,7 @@ function createI18nFile(originalFilePath: string, messages: any): File { content = content.replace(/\n/g, '\r\n'); } return new File({ - path: path.join(originalFilePath + '.i18n.json'), + path: path.join(name + '.i18n.json'), contents: Buffer.from(content, 'utf8') }); } @@ -1168,7 +774,7 @@ function createI18nFile(originalFilePath: string, messages: any): File { interface I18nPack { version: string; contents: { - [path: string]: Map; + [path: string]: Record; }; } @@ -1179,22 +785,31 @@ export interface TranslationPath { resourceName: string; } -export function prepareI18nPackFiles(externalExtensions: Map, resultingTranslationPaths: TranslationPath[], pseudo = false): NodeJS.ReadWriteStream { - const parsePromises: Promise[] = []; +function getRecordFromL10nJsonFormat(l10nJsonFormat: l10nJsonFormat): Record { + const record: Record = {}; + for (const key of Object.keys(l10nJsonFormat)) { + const value = l10nJsonFormat[key]; + record[key] = typeof value === 'string' ? value : value.message; + } + return record; +} + +export function prepareI18nPackFiles(resultingTranslationPaths: TranslationPath[]): NodeJS.ReadWriteStream { + const parsePromises: Promise[] = []; const mainPack: I18nPack = { version: i18nPackVersion, contents: {} }; - const extensionsPacks: Map = {}; + const extensionsPacks: Record = {}; const errors: any[] = []; return through(function (this: ThroughStream, xlf: File) { const project = path.basename(path.dirname(path.dirname(xlf.relative))); const resource = path.basename(xlf.relative, '.xlf'); const contents = xlf.contents.toString(); log(`Found ${project}: ${resource}`); - const parsePromise = pseudo ? XLF.parsePseudo(contents) : XLF.parse(contents); + const parsePromise = getL10nFilesFromXlf(contents); parsePromises.push(parsePromise); parsePromise.then( resolvedFiles => { resolvedFiles.forEach(file => { - const path = file.originalFilePath; + const path = file.name; const firstSlash = path.indexOf('/'); if (project === extensionsProject) { @@ -1202,15 +817,15 @@ export function prepareI18nPackFiles(externalExtensions: Map, resultingT if (!extPack) { extPack = extensionsPacks[resource] = { version: i18nPackVersion, contents: {} }; } - const externalId = externalExtensions[resource]; + const externalId = externalExtensionsWithTranslations[resource]; if (!externalId) { // internal extension: remove 'extensions/extensionId/' segnent const secondSlash = path.indexOf('/', firstSlash + 1); - extPack.contents[path.substr(secondSlash + 1)] = file.messages; + extPack.contents[path.substring(secondSlash + 1)] = getRecordFromL10nJsonFormat(file.messages); } else { - extPack.contents[path] = file.messages; + extPack.contents[path] = getRecordFromL10nJsonFormat(file.messages); } } else { - mainPack.contents[path.substr(firstSlash + 1)] = file.messages; + mainPack.contents[path.substring(firstSlash + 1)] = getRecordFromL10nJsonFormat(file.messages); } }); } @@ -1231,7 +846,7 @@ export function prepareI18nPackFiles(externalExtensions: Map, resultingT const translatedExtFile = createI18nFile(`extensions/${extension}`, extensionsPacks[extension]); this.queue(translatedExtFile); - const externalExtensionId = externalExtensions[extension]; + const externalExtensionId = externalExtensionsWithTranslations[extension]; if (externalExtensionId) { resultingTranslationPaths.push({ id: externalExtensionId, resourceName: `extensions/${extension}.i18n.json` }); } else { @@ -1248,7 +863,7 @@ export function prepareI18nPackFiles(externalExtensions: Map, resultingT } export function prepareIslFiles(language: Language, innoSetupConfig: InnoSetup): ThroughStream { - const parsePromises: Promise[] = []; + const parsePromises: Promise[] = []; return through(function (this: ThroughStream, xlf: File) { const stream = this; @@ -1257,7 +872,7 @@ export function prepareIslFiles(language: Language, innoSetupConfig: InnoSetup): parsePromise.then( resolvedFiles => { resolvedFiles.forEach(file => { - const translatedFile = createIslFile(file.originalFilePath, file.messages, language, innoSetupConfig); + const translatedFile = createIslFile(file.name, file.messages, language, innoSetupConfig); stream.queue(translatedFile); }); } @@ -1273,13 +888,13 @@ export function prepareIslFiles(language: Language, innoSetupConfig: InnoSetup): }); } -function createIslFile(originalFilePath: string, messages: Map, language: Language, innoSetup: InnoSetup): File { +function createIslFile(name: string, messages: l10nJsonFormat, language: Language, innoSetup: InnoSetup): File { const content: string[] = []; let originalContent: TextModel; - if (path.basename(originalFilePath) === 'Default') { - originalContent = new TextModel(fs.readFileSync(originalFilePath + '.isl', 'utf8')); + if (path.basename(name) === 'Default') { + originalContent = new TextModel(fs.readFileSync(name + '.isl', 'utf8')); } else { - originalContent = new TextModel(fs.readFileSync(originalFilePath + '.en.isl', 'utf8')); + originalContent = new TextModel(fs.readFileSync(name + '.en.isl', 'utf8')); } originalContent.lines.forEach(line => { if (line.length > 0) { @@ -1302,7 +917,7 @@ function createIslFile(originalFilePath: string, messages: Map, language } }); - const basename = path.basename(originalFilePath); + const basename = path.basename(name); const filePath = `${basename}.${language.id}.isl`; const encoded = iconv.encode(Buffer.from(content.join('\r\n'), 'utf8').toString(), innoSetup.codePage); @@ -1336,7 +951,3 @@ function encodeEntities(value: string): string { function decodeEntities(value: string): string { return value.replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&'); } - -function pseudify(message: string) { - return '\uFF3B' + message.replace(/[aouei]/g, '$&$&') + '\uFF3D'; -} diff --git a/build/lib/layersChecker.js b/build/lib/layersChecker.js index cbc1f95b979..e766756d9f3 100644 --- a/build/lib/layersChecker.js +++ b/build/lib/layersChecker.js @@ -56,7 +56,8 @@ const CORE_TYPES = [ 'MessageChannel', 'MessagePort', 'URL', - 'URLSearchParams' + 'URLSearchParams', + 'ReadonlyArray', ]; // Types that are defined in a common layer but are known to be only // available in native environments should not be allowed in browser diff --git a/build/lib/layersChecker.ts b/build/lib/layersChecker.ts index 12b900162aa..44490b6f1f9 100644 --- a/build/lib/layersChecker.ts +++ b/build/lib/layersChecker.ts @@ -57,7 +57,8 @@ const CORE_TYPES = [ 'MessageChannel', 'MessagePort', 'URL', - 'URLSearchParams' + 'URLSearchParams', + 'ReadonlyArray', ]; // Types that are defined in a common layer but are known to be only diff --git a/build/lib/standalone.js b/build/lib/standalone.js index 1121e56ff94..80accea5f4e 100644 --- a/build/lib/standalone.js +++ b/build/lib/standalone.js @@ -180,7 +180,7 @@ function createESMSourcesAndResources2(options) { + relativePath + fileContents.substring(end + 1)); } - fileContents = fileContents.replace(/import ([a-zA-z0-9]+) = require\(('[^']+')\);/g, function (_, m1, m2) { + fileContents = fileContents.replace(/import ([a-zA-Z0-9]+) = require\(('[^']+')\);/g, function (_, m1, m2) { return `import * as ${m1} from ${m2};`; }); write(getDestAbsoluteFilePath(file), fileContents); diff --git a/build/lib/standalone.ts b/build/lib/standalone.ts index f2181e6f274..775a1be5996 100644 --- a/build/lib/standalone.ts +++ b/build/lib/standalone.ts @@ -212,7 +212,7 @@ export function createESMSourcesAndResources2(options: IOptions2): void { ); } - fileContents = fileContents.replace(/import ([a-zA-z0-9]+) = require\(('[^']+')\);/g, function (_, m1, m2) { + fileContents = fileContents.replace(/import ([a-zA-Z0-9]+) = require\(('[^']+')\);/g, function (_, m1, m2) { return `import * as ${m1} from ${m2};`; }); diff --git a/build/lib/test/i18n.test.js b/build/lib/test/i18n.test.js index 4d339c120a9..3f8015e8c90 100644 --- a/build/lib/test/i18n.test.js +++ b/build/lib/test/i18n.test.js @@ -9,20 +9,20 @@ const i18n = require("../i18n"); suite('XLF Parser Tests', () => { const sampleXlf = 'Key #1Key #2 &'; const sampleTranslatedXlf = 'Key #1Кнопка #1Key #2 &Кнопка #2 &'; - const originalFilePath = 'vs/base/common/keybinding'; + const name = 'vs/base/common/keybinding'; const keys = ['key1', 'key2']; const messages = ['Key #1', 'Key #2 &']; const translatedMessages = { key1: 'Кнопка #1', key2: 'Кнопка #2 &' }; test('Keys & messages to XLF conversion', () => { const xlf = new i18n.XLF('vscode-workbench'); - xlf.addFile(originalFilePath, keys, messages); + xlf.addFile(name, keys, messages); const xlfString = xlf.toString(); assert.strictEqual(xlfString.replace(/\s{2,}/g, ''), sampleXlf); }); test('XLF to keys & messages conversion', () => { i18n.XLF.parse(sampleTranslatedXlf).then(function (resolvedFiles) { assert.deepStrictEqual(resolvedFiles[0].messages, translatedMessages); - assert.strictEqual(resolvedFiles[0].originalFilePath, originalFilePath); + assert.strictEqual(resolvedFiles[0].name, name); }); }); test('JSON file source path to Transifex resource match', () => { diff --git a/build/lib/test/i18n.test.ts b/build/lib/test/i18n.test.ts index ab0924c4350..b8a68323dd7 100644 --- a/build/lib/test/i18n.test.ts +++ b/build/lib/test/i18n.test.ts @@ -9,14 +9,14 @@ import i18n = require('../i18n'); suite('XLF Parser Tests', () => { const sampleXlf = 'Key #1Key #2 &'; const sampleTranslatedXlf = 'Key #1Кнопка #1Key #2 &Кнопка #2 &'; - const originalFilePath = 'vs/base/common/keybinding'; + const name = 'vs/base/common/keybinding'; const keys = ['key1', 'key2']; const messages = ['Key #1', 'Key #2 &']; const translatedMessages = { key1: 'Кнопка #1', key2: 'Кнопка #2 &' }; test('Keys & messages to XLF conversion', () => { const xlf = new i18n.XLF('vscode-workbench'); - xlf.addFile(originalFilePath, keys, messages); + xlf.addFile(name, keys, messages); const xlfString = xlf.toString(); assert.strictEqual(xlfString.replace(/\s{2,}/g, ''), sampleXlf); @@ -25,7 +25,7 @@ suite('XLF Parser Tests', () => { test('XLF to keys & messages conversion', () => { i18n.XLF.parse(sampleTranslatedXlf).then(function (resolvedFiles) { assert.deepStrictEqual(resolvedFiles[0].messages, translatedMessages); - assert.strictEqual(resolvedFiles[0].originalFilePath, originalFilePath); + assert.strictEqual(resolvedFiles[0].name, name); }); }); diff --git a/build/lib/treeshaking.js b/build/lib/treeshaking.js index ff18694436d..029235f7709 100644 --- a/build/lib/treeshaking.js +++ b/build/lib/treeshaking.js @@ -490,54 +490,56 @@ function markNodes(ts, languageService, options) { } const nodeSourceFile = node.getSourceFile(); const loop = (node) => { - const [symbol, symbolImportNode] = getRealNodeSymbol(ts, checker, node); - if (symbolImportNode) { - setColor(symbolImportNode, 2 /* NodeColor.Black */); - const importDeclarationNode = findParentImportDeclaration(symbolImportNode); - if (importDeclarationNode && ts.isStringLiteral(importDeclarationNode.moduleSpecifier)) { - enqueueImport(importDeclarationNode, importDeclarationNode.moduleSpecifier.text); + const symbols = getRealNodeSymbol(ts, checker, node); + for (const { symbol, symbolImportNode } of symbols) { + if (symbolImportNode) { + setColor(symbolImportNode, 2 /* NodeColor.Black */); + const importDeclarationNode = findParentImportDeclaration(symbolImportNode); + if (importDeclarationNode && ts.isStringLiteral(importDeclarationNode.moduleSpecifier)) { + enqueueImport(importDeclarationNode, importDeclarationNode.moduleSpecifier.text); + } } - } - if (isSymbolWithDeclarations(symbol) && !nodeIsInItsOwnDeclaration(nodeSourceFile, node, symbol)) { - for (let i = 0, len = symbol.declarations.length; i < len; i++) { - const declaration = symbol.declarations[i]; - if (ts.isSourceFile(declaration)) { - // Do not enqueue full source files - // (they can be the declaration of a module import) - continue; - } - if (options.shakeLevel === 2 /* ShakeLevel.ClassMembers */ && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration)) && !isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(ts, program, checker, declaration)) { - enqueue_black(declaration.name); - for (let j = 0; j < declaration.members.length; j++) { - const member = declaration.members[j]; - const memberName = member.name ? member.name.getText() : null; - if (ts.isConstructorDeclaration(member) - || ts.isConstructSignatureDeclaration(member) - || ts.isIndexSignatureDeclaration(member) - || ts.isCallSignatureDeclaration(member) - || memberName === '[Symbol.iterator]' - || memberName === '[Symbol.toStringTag]' - || memberName === 'toJSON' - || memberName === 'toString' - || memberName === 'dispose' // TODO: keeping all `dispose` methods - || /^_(.*)Brand$/.test(memberName || '') // TODO: keeping all members ending with `Brand`... - ) { - enqueue_black(member); + if (isSymbolWithDeclarations(symbol) && !nodeIsInItsOwnDeclaration(nodeSourceFile, node, symbol)) { + for (let i = 0, len = symbol.declarations.length; i < len; i++) { + const declaration = symbol.declarations[i]; + if (ts.isSourceFile(declaration)) { + // Do not enqueue full source files + // (they can be the declaration of a module import) + continue; + } + if (options.shakeLevel === 2 /* ShakeLevel.ClassMembers */ && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration)) && !isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(ts, program, checker, declaration)) { + enqueue_black(declaration.name); + for (let j = 0; j < declaration.members.length; j++) { + const member = declaration.members[j]; + const memberName = member.name ? member.name.getText() : null; + if (ts.isConstructorDeclaration(member) + || ts.isConstructSignatureDeclaration(member) + || ts.isIndexSignatureDeclaration(member) + || ts.isCallSignatureDeclaration(member) + || memberName === '[Symbol.iterator]' + || memberName === '[Symbol.toStringTag]' + || memberName === 'toJSON' + || memberName === 'toString' + || memberName === 'dispose' // TODO: keeping all `dispose` methods + || /^_(.*)Brand$/.test(memberName || '') // TODO: keeping all members ending with `Brand`... + ) { + enqueue_black(member); + } + if (isStaticMemberWithSideEffects(ts, member)) { + enqueue_black(member); + } } - if (isStaticMemberWithSideEffects(ts, member)) { - enqueue_black(member); + // queue the heritage clauses + if (declaration.heritageClauses) { + for (const heritageClause of declaration.heritageClauses) { + enqueue_black(heritageClause); + } } } - // queue the heritage clauses - if (declaration.heritageClauses) { - for (const heritageClause of declaration.heritageClauses) { - enqueue_black(heritageClause); - } + else { + enqueue_black(declaration); } } - else { - enqueue_black(declaration); - } } } node.forEachChild(loop); @@ -736,13 +738,20 @@ function findSymbolFromHeritageType(ts, checker, type) { return findSymbolFromHeritageType(ts, checker, type.expression); } if (ts.isIdentifier(type)) { - return getRealNodeSymbol(ts, checker, type)[0]; + const tmp = getRealNodeSymbol(ts, checker, type); + return (tmp.length > 0 ? tmp[0].symbol : null); } if (ts.isPropertyAccessExpression(type)) { return findSymbolFromHeritageType(ts, checker, type.name); } return null; } +class SymbolImportTuple { + constructor(symbol, symbolImportNode) { + this.symbol = symbol; + this.symbolImportNode = symbolImportNode; + } +} /** * Returns the node's symbol and the `import` node (if the symbol resolved from a different module) */ @@ -774,7 +783,7 @@ function getRealNodeSymbol(ts, checker, node) { } if (!ts.isShorthandPropertyAssignment(node)) { if (node.getChildCount() !== 0) { - return [null, null]; + return []; } } const { parent } = node; @@ -820,10 +829,7 @@ function getRealNodeSymbol(ts, checker, node) { const type = checker.getTypeAtLocation(parent.parent); if (name && type) { if (type.isUnion()) { - const prop = type.types[0].getProperty(name); - if (prop) { - symbol = prop; - } + return generateMultipleSymbols(type, name, importNode); } else { const prop = type.getProperty(name); @@ -854,9 +860,19 @@ function getRealNodeSymbol(ts, checker, node) { } } if (symbol && symbol.declarations) { - return [symbol, importNode]; + return [new SymbolImportTuple(symbol, importNode)]; + } + return []; + function generateMultipleSymbols(type, name, importNode) { + const result = []; + for (const t of type.types) { + const prop = t.getProperty(name); + if (prop && prop.declarations) { + result.push(new SymbolImportTuple(prop, importNode)); + } + } + return result; } - return [null, null]; } /** Get the token whose text contains the position */ function getTokenAtPosition(ts, sourceFile, position, allowPositionInLeadingTrivia, includeEndPosition) { diff --git a/build/lib/treeshaking.ts b/build/lib/treeshaking.ts index ef829426d94..020e567eb72 100644 --- a/build/lib/treeshaking.ts +++ b/build/lib/treeshaking.ts @@ -609,58 +609,60 @@ function markNodes(ts: typeof import('typescript'), languageService: ts.Language const nodeSourceFile = node.getSourceFile(); const loop = (node: ts.Node) => { - const [symbol, symbolImportNode] = getRealNodeSymbol(ts, checker, node); - if (symbolImportNode) { - setColor(symbolImportNode, NodeColor.Black); - const importDeclarationNode = findParentImportDeclaration(symbolImportNode); - if (importDeclarationNode && ts.isStringLiteral(importDeclarationNode.moduleSpecifier)) { - enqueueImport(importDeclarationNode, importDeclarationNode.moduleSpecifier.text); - } - } - - if (isSymbolWithDeclarations(symbol) && !nodeIsInItsOwnDeclaration(nodeSourceFile, node, symbol)) { - for (let i = 0, len = symbol.declarations.length; i < len; i++) { - const declaration = symbol.declarations[i]; - if (ts.isSourceFile(declaration)) { - // Do not enqueue full source files - // (they can be the declaration of a module import) - continue; + const symbols = getRealNodeSymbol(ts, checker, node); + for (const { symbol, symbolImportNode } of symbols) { + if (symbolImportNode) { + setColor(symbolImportNode, NodeColor.Black); + const importDeclarationNode = findParentImportDeclaration(symbolImportNode); + if (importDeclarationNode && ts.isStringLiteral(importDeclarationNode.moduleSpecifier)) { + enqueueImport(importDeclarationNode, importDeclarationNode.moduleSpecifier.text); } + } - if (options.shakeLevel === ShakeLevel.ClassMembers && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration)) && !isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(ts, program, checker, declaration)) { - enqueue_black(declaration.name!); - - for (let j = 0; j < declaration.members.length; j++) { - const member = declaration.members[j]; - const memberName = member.name ? member.name.getText() : null; - if ( - ts.isConstructorDeclaration(member) - || ts.isConstructSignatureDeclaration(member) - || ts.isIndexSignatureDeclaration(member) - || ts.isCallSignatureDeclaration(member) - || memberName === '[Symbol.iterator]' - || memberName === '[Symbol.toStringTag]' - || memberName === 'toJSON' - || memberName === 'toString' - || memberName === 'dispose'// TODO: keeping all `dispose` methods - || /^_(.*)Brand$/.test(memberName || '') // TODO: keeping all members ending with `Brand`... - ) { - enqueue_black(member); - } - - if (isStaticMemberWithSideEffects(ts, member)) { - enqueue_black(member); - } + if (isSymbolWithDeclarations(symbol) && !nodeIsInItsOwnDeclaration(nodeSourceFile, node, symbol)) { + for (let i = 0, len = symbol.declarations.length; i < len; i++) { + const declaration = symbol.declarations[i]; + if (ts.isSourceFile(declaration)) { + // Do not enqueue full source files + // (they can be the declaration of a module import) + continue; } - // queue the heritage clauses - if (declaration.heritageClauses) { - for (const heritageClause of declaration.heritageClauses) { - enqueue_black(heritageClause); + if (options.shakeLevel === ShakeLevel.ClassMembers && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration)) && !isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(ts, program, checker, declaration)) { + enqueue_black(declaration.name!); + + for (let j = 0; j < declaration.members.length; j++) { + const member = declaration.members[j]; + const memberName = member.name ? member.name.getText() : null; + if ( + ts.isConstructorDeclaration(member) + || ts.isConstructSignatureDeclaration(member) + || ts.isIndexSignatureDeclaration(member) + || ts.isCallSignatureDeclaration(member) + || memberName === '[Symbol.iterator]' + || memberName === '[Symbol.toStringTag]' + || memberName === 'toJSON' + || memberName === 'toString' + || memberName === 'dispose'// TODO: keeping all `dispose` methods + || /^_(.*)Brand$/.test(memberName || '') // TODO: keeping all members ending with `Brand`... + ) { + enqueue_black(member); + } + + if (isStaticMemberWithSideEffects(ts, member)) { + enqueue_black(member); + } } + + // queue the heritage clauses + if (declaration.heritageClauses) { + for (const heritageClause of declaration.heritageClauses) { + enqueue_black(heritageClause); + } + } + } else { + enqueue_black(declaration); } - } else { - enqueue_black(declaration); } } } @@ -879,7 +881,8 @@ function findSymbolFromHeritageType(ts: typeof import('typescript'), checker: ts return findSymbolFromHeritageType(ts, checker, type.expression); } if (ts.isIdentifier(type)) { - return getRealNodeSymbol(ts, checker, type)[0]; + const tmp = getRealNodeSymbol(ts, checker, type); + return (tmp.length > 0 ? tmp[0].symbol : null); } if (ts.isPropertyAccessExpression(type)) { return findSymbolFromHeritageType(ts, checker, type.name); @@ -887,10 +890,17 @@ function findSymbolFromHeritageType(ts: typeof import('typescript'), checker: ts return null; } +class SymbolImportTuple { + constructor( + public readonly symbol: ts.Symbol | null, + public readonly symbolImportNode: ts.Declaration | null + ) { } +} + /** * Returns the node's symbol and the `import` node (if the symbol resolved from a different module) */ -function getRealNodeSymbol(ts: typeof import('typescript'), checker: ts.TypeChecker, node: ts.Node): [ts.Symbol | null, ts.Declaration | null] { +function getRealNodeSymbol(ts: typeof import('typescript'), checker: ts.TypeChecker, node: ts.Node): SymbolImportTuple[] { // Use some TypeScript internals to avoid code duplication type ObjectLiteralElementWithName = ts.ObjectLiteralElement & { name: ts.PropertyName; parent: ts.ObjectLiteralExpression | ts.JsxAttributes }; @@ -923,7 +933,7 @@ function getRealNodeSymbol(ts: typeof import('typescript'), checker: ts.TypeChec if (!ts.isShorthandPropertyAssignment(node)) { if (node.getChildCount() !== 0) { - return [null, null]; + return []; } } @@ -976,10 +986,7 @@ function getRealNodeSymbol(ts: typeof import('typescript'), checker: ts.TypeChec const type = checker.getTypeAtLocation(parent.parent); if (name && type) { if (type.isUnion()) { - const prop = type.types[0].getProperty(name); - if (prop) { - symbol = prop; - } + return generateMultipleSymbols(type, name, importNode); } else { const prop = type.getProperty(name); if (prop) { @@ -1011,10 +1018,21 @@ function getRealNodeSymbol(ts: typeof import('typescript'), checker: ts.TypeChec } if (symbol && symbol.declarations) { - return [symbol, importNode]; + return [new SymbolImportTuple(symbol, importNode)]; } - return [null, null]; + return []; + + function generateMultipleSymbols(type: ts.UnionType, name: string, importNode: ts.Declaration | null): SymbolImportTuple[] { + const result: SymbolImportTuple[] = []; + for (const t of type.types) { + const prop = t.getProperty(name); + if (prop && prop.declarations) { + result.push(new SymbolImportTuple(prop, importNode)); + } + } + return result; + } } /** Get the token whose text contains the position */ diff --git a/build/lib/util.js b/build/lib/util.js index fc62c844b6a..3eae325612a 100644 --- a/build/lib/util.js +++ b/build/lib/util.js @@ -4,7 +4,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ Object.defineProperty(exports, "__esModule", { value: true }); -exports.buildWebNodePaths = exports.createExternalLoaderConfig = exports.acquireWebNodePaths = exports.getElectronVersion = exports.streamToPromise = exports.versionStringToNumber = exports.filter = exports.rebase = exports.ensureDir = exports.rreddir = exports.rimraf = exports.rewriteSourceMappingURL = exports.stripSourceMappingURL = exports.loadSourcemaps = exports.cleanNodeModules = exports.skipDirectories = exports.toFileUri = exports.setExecutableBit = exports.fixWin32DirectoryPermissions = exports.debounce = exports.incremental = exports.getVersion = void 0; +exports.buildWebNodePaths = exports.createExternalLoaderConfig = exports.acquireWebNodePaths = exports.getElectronVersion = exports.streamToPromise = exports.versionStringToNumber = exports.filter = exports.rebase = exports.ensureDir = exports.rreddir = exports.rimraf = exports.rewriteSourceMappingURL = exports.stripSourceMappingURL = exports.loadSourcemaps = exports.cleanNodeModules = exports.skipDirectories = exports.toFileUri = exports.setExecutableBit = exports.fixWin32DirectoryPermissions = exports.debounce = exports.incremental = void 0; const es = require("event-stream"); const _debounce = require("debounce"); const _filter = require("gulp-filter"); @@ -13,8 +13,6 @@ const path = require("path"); const fs = require("fs"); const _rimraf = require("rimraf"); const VinylFile = require("vinyl"); -var getVersion_1 = require("./getVersion"); -Object.defineProperty(exports, "getVersion", { enumerable: true, get: function () { return getVersion_1.getVersion; } }); const root = path.dirname(path.dirname(__dirname)); const NoCancellationToken = { isCancellationRequested: () => false }; function incremental(streamProvider, initial, supportsCancellation) { diff --git a/build/lib/util.ts b/build/lib/util.ts index 6fb799f2699..bbe6c52ef41 100644 --- a/build/lib/util.ts +++ b/build/lib/util.ts @@ -15,8 +15,6 @@ import * as VinylFile from 'vinyl'; import { ThroughStream } from 'through'; import * as sm from 'source-map'; -export { getVersion } from './getVersion'; - const root = path.dirname(path.dirname(__dirname)); export interface ICancellationToken { diff --git a/build/npm/dirs.js b/build/npm/dirs.js index 6e5ba3ca9a4..f820f39e222 100644 --- a/build/npm/dirs.js +++ b/build/npm/dirs.js @@ -23,7 +23,6 @@ exports.dirs = [ 'extensions/gulp', 'extensions/html-language-features', 'extensions/html-language-features/server', - 'extensions/image-preview', 'extensions/ipynb', 'extensions/jake', 'extensions/json-language-features', @@ -31,6 +30,7 @@ exports.dirs = [ 'extensions/markdown-language-features/server', 'extensions/markdown-language-features', 'extensions/markdown-math', + 'extensions/media-preview', 'extensions/merge-conflict', 'extensions/microsoft-authentication', 'extensions/notebook-renderers', diff --git a/build/npm/preinstall.js b/build/npm/preinstall.js index a839653f75c..afefc404267 100644 --- a/build/npm/preinstall.js +++ b/build/npm/preinstall.js @@ -21,12 +21,19 @@ const path = require('path'); const fs = require('fs'); const cp = require('child_process'); const yarnVersion = cp.execSync('yarn -v', { encoding: 'utf8' }).trim(); -const parsedYarnVersion = /^(\d+)\.(\d+)\./.exec(yarnVersion); +const parsedYarnVersion = /^(\d+)\.(\d+)\.(\d+)/.exec(yarnVersion); const majorYarnVersion = parseInt(parsedYarnVersion[1]); const minorYarnVersion = parseInt(parsedYarnVersion[2]); +const patchYarnVersion = parseInt(parsedYarnVersion[3]); -if (majorYarnVersion < 1 || minorYarnVersion < 10) { - console.error('\033[1;31m*** Please use yarn >=1.10.1.\033[0;0m'); +if ( + majorYarnVersion < 1 || + majorYarnVersion === 1 && ( + minorYarnVersion < 10 || (minorYarnVersion === 10 && patchYarnVersion < 1) + ) || + majorYarnVersion >= 2 +) { + console.error('\033[1;31m*** Please use yarn >=1.10.1 and <2.\033[0;0m'); err = true; } diff --git a/build/npm/update-localization-extension.js b/build/npm/update-localization-extension.js index 1a689a189ea..b78674a7099 100644 --- a/build/npm/update-localization-extension.js +++ b/build/npm/update-localization-extension.js @@ -68,7 +68,7 @@ function update(options) { console.log(`Importing translations for ${languageId} form '${location}' to '${translationDataFolder}' ...`); let translationPaths = []; gulp.src(path.join(location, '**', languageId, '*.xlf'), { silent: false }) - .pipe(i18n.prepareI18nPackFiles(i18n.externalExtensionsWithTranslations, translationPaths, languageId === 'ps')) + .pipe(i18n.prepareI18nPackFiles(translationPaths)) .on('error', (error) => { console.log(`Error occurred while importing translations:`); translationPaths = undefined; diff --git a/build/win32/Cargo.lock b/build/win32/Cargo.lock index e27f58bf455..0601c70fb9e 100644 --- a/build/win32/Cargo.lock +++ b/build/win32/Cargo.lock @@ -1,258 +1,331 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -[[package]] -name = "build_const" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +version = 3 [[package]] -name = "byteorder" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "chrono" -version = "0.4.0" +name = "atty" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "num 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "hermit-abi", + "libc", + "winapi", ] [[package]] -name = "crc" -version = "1.7.0" +name = "bitflags" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "build_const" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crc" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" dependencies = [ - "build_const 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "build_const", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "getrandom" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", ] [[package]] name = "inno_updater" -version = "0.8.2" +version = "0.9.0" dependencies = [ - "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slog 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "slog-async 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slog-term 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "crc", + "slog", + "slog-async", + "slog-term", + "winapi", ] [[package]] -name = "isatty" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "lazy_static" -version = "1.0.0" +name = "itoa" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" [[package]] name = "libc" -version = "0.2.36" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" [[package]] -name = "num" -version = "0.1.41" +name = "num_threads" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" dependencies = [ - "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] -name = "num-integer" -version = "0.1.35" +name = "once_cell" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" + +[[package]] +name = "proc-macro2" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" dependencies = [ - "num-traits 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-ident", ] [[package]] -name = "num-iter" -version = "0.1.34" +name = "quote" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" dependencies = [ - "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", ] -[[package]] -name = "num-traits" -version = "0.1.42" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "redox_syscall" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "redox_termios" -version = "0.1.1" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" dependencies = [ - "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", ] [[package]] -name = "slog" -version = "2.1.1" +name = "redox_users" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall", + "thiserror", +] + +[[package]] +name = "rustversion" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0a5f7c728f5d284929a1cccb5bc19884422bfe6ef4d6c409da2c41838983fcf" + +[[package]] +name = "slog" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8347046d4ebd943127157b94d63abb990fcf729dc4e9978927fdf4ac3c998d06" [[package]] name = "slog-async" -version = "2.2.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "766c59b252e62a34651412870ff55d8c4e6d04df19b43eecb2703e417b097ffe" dependencies = [ - "slog 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "take_mut 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-channel", + "slog", + "take_mut", + "thread_local", ] [[package]] name = "slog-term" -version = "2.3.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87d29185c55b7b258b4f120eab00f48557d4d9bc814f41713f449d35b0f8977c" dependencies = [ - "chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "isatty 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "slog 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "atty", + "slog", + "term", + "thread_local", + "time", +] + +[[package]] +name = "syn" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] name = "take_mut" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" [[package]] name = "term" -version = "0.4.6" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "dirs-next", + "rustversion", + "winapi", ] [[package]] -name = "termion" -version = "1.5.1" +name = "thiserror" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] name = "thread_local" -version = "0.3.5" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" dependencies = [ - "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "once_cell", ] [[package]] name = "time" -version = "0.1.39" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217" dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa", + "libc", + "num_threads", + "time-macros", ] [[package]] -name = "unreachable" -version = "1.0.0" +name = "time-macros" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" [[package]] -name = "void" -version = "1.0.2" +name = "unicode-ident" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi" -version = "0.3.4" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", ] -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum build_const 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e90dc84f5e62d2ebe7676b83c22d33b6db8bd27340fb6ffbff0a364efa0cb9c9" -"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23" -"checksum chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c20ebe0b2b08b0aeddba49c609fe7957ba2e33449882cb186a180bc60682fa9" -"checksum crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd5d02c0aac6bd68393ed69e00bbc2457f3e89075c6349db7189618dc4ddc1d7" -"checksum isatty 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8f2a233726c7bb76995cec749d59582e5664823b7245d4970354408f1d79a7a2" -"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d" -"checksum libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1e5d97d6708edaa407429faa671b942dc0f2727222fb6b6539bf1db936e4b121" -"checksum num 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "cc4083e14b542ea3eb9b5f33ff48bd373a92d78687e74f4cc0a30caeb754f0ca" -"checksum num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "d1452e8b06e448a07f0e6ebb0bb1d92b8890eea63288c0b627331d53514d0fba" -"checksum num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "7485fcc84f85b4ecd0ea527b14189281cf27d60e583ae65ebc9c088b13dffe01" -"checksum num-traits 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "9936036cc70fe4a8b2d338ab665900323290efb03983c86cbe235ae800ad8017" -"checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" -"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" -"checksum slog 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0a6b13b17f4225771f7f15cece704a4e68d3a5f31278ed26367f497133398a18" -"checksum slog-async 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5e319a30c08b004618d5f7ca2f2b1dad7b4623ba7fcb1a12846fc3b01e9eaa10" -"checksum slog-term 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5bb5d9360b2b279b326824b3b4ca2402ead8a8138f0e5ec1900605c861bb6671" -"checksum take_mut 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50b910a1174df4aeb5738e8a0e7253883cf7801de40d094175a5a557e487f4c5" -"checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1" -"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" -"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" -"checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098" -"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" -"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" -"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" -"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/build/win32/Cargo.toml b/build/win32/Cargo.toml new file mode 100644 index 00000000000..decae65f9e6 --- /dev/null +++ b/build/win32/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "inno_updater" +version = "0.9.0" +authors = ["Microsoft "] +build = "build.rs" + +[dependencies] +byteorder = "1" +crc = "^1.0.0" +slog = "2.1.1" +slog-async = "2.2.0" +slog-term = "2.3.0" + +[target.'cfg(windows)'.dependencies] +winapi = { version = "^0.3.9", features = ["winuser", "libloaderapi", "commctrl", "processthreadsapi", "tlhelp32", "handleapi", "psapi", "errhandlingapi", "winbase", "shellapi"] } + +[profile.release] +lto = true +panic = 'abort' diff --git a/build/win32/inno_updater.exe b/build/win32/inno_updater.exe index 8c532b9eb5e..fea47a59c9d 100644 Binary files a/build/win32/inno_updater.exe and b/build/win32/inno_updater.exe differ diff --git a/build/yarn.lock b/build/yarn.lock index bd5fcfac717..27bc4dd9a10 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -502,9 +502,9 @@ integrity sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ== "@types/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== + version "16.11.64" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.64.tgz#9171f327298b619e2c52238b120c19056415d820" + integrity sha512-z5hPTlVFzNwtJ2LNozTpJcD1Cu44c4LNuzaq1mwxmiHWQh2ULdR6Vjwo1UGldzRpzL0yUEdZddnfqGW2G70z6Q== "@types/p-limit@^2.2.0": version "2.2.0" diff --git a/cglicenses.json b/cglicenses.json index f80a3fac7fc..eee2d66c8ef 100644 --- a/cglicenses.json +++ b/cglicenses.json @@ -32,20 +32,6 @@ "Copyright (c) tunnel-agent authors" ] }, - { - // Reason: Waiting for https://github.com/segmentio/noop-logger/issues/2 - "name": "noop-logger", - "fullLicenseText": [ - "This project is licensed under the MIT license.", - "Copyrights are respective of each contributor listed at the beginning of each definition file.", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." - ] - }, { // Reason: The license at https://github.com/rbuckton/reflect-metadata/blob/master/LICENSE // does not include a clear Copyright statement (it's in https://github.com/rbuckton/reflect-metadata/blob/master/CopyrightNotice.txt). @@ -102,36 +88,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." ] }, - { - // Reason: Repository lacks license text. - // https://github.com/othiym23/emitter-listener/blob/master/package.json declares BSD-2-Clause. - // https://github.com/othiym23/emitter-listener/issues/3 - "name": "emitter-listener", - "fullLicenseText": [ - "BSD 2-Clause \"Simplified\" License", - "Copyright (c) 2018, Forrest L Norvell ", - "", - "Redistribution and use in source and binary forms, with or without", - "modification, are permitted provided that the following conditions are met:", - "", - "1. Redistributions of source code must retain the above copyright notice, this", - " list of conditions and the following disclaimer.", - "2. Redistributions in binary form must reproduce the above copyright notice,", - " this list of conditions and the following disclaimer in the documentation", - " and/or other materials provided with the distribution.", - "", - "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND", - "ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED", - "WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE", - "DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR", - "ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES", - "(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;", - "LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND", - "ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT", - "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS", - "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." - ] - }, { // Reason: Repository lacks license text. // https://github.com/tjwebb/fnv-plus/blob/master/package.json declares MIT. @@ -174,23 +130,6 @@ "SOFTWARE" ] }, - { - // Reason: This product includes AVC coding technology. MPEG LA LLC requires this notice. - "name": "H.264/AVC Video Standard", - "fullLicenseText": [ - "This product is licensed under the AVC patent portfolio license for the personal", - "and non-commercial use of a consumer to (i) encode video in compliance with the AVC standard (\"AVC VIDEO\")", - "and/or (ii) decode AVC video that was encoded by a consumer", - "engaged in a personal and non-commercial activity and/or was obtained from a video provider", - "licensed to provide AVC video. No license is granted or shall be implied for any other use.", - "Additional information may be obtained from MPEG LA LLC. See http://www.MPEGLA.COM.", - "", - "For clarification purposes, this notice does not limit or inhibit the use of the product", - "for normal business uses that are personal to that business which do not include", - "(i) redistribution of the product to third parties, or", - "(ii) creation of content with AVC Standard compliant technologies for distribution to third parties." - ] - }, { // Reason: Missing license file "name": "@tokenizer/token", @@ -220,5 +159,104 @@ "", "THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." ] + }, + { + // Reason: The substack org has been deleted on GH + "name": "concat-map", + "fullLicenseText": [ + "This software is released under the MIT license:", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy of", + "this software and associated documentation files (the \"Software\"), to deal in", + "the Software without restriction, including without limitation the rights to", + "use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of", + "the Software, and to permit persons to whom the Software is furnished to do so,", + "subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all", + "copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS", + "FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR", + "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER", + "IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", + "CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ] + }, + { + // Reason: The substack org has been deleted on GH + "name": "github-from-package", + "fullLicenseText": [ + "This software is released under the MIT license:", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy of", + "this software and associated documentation files (the \"Software\"), to deal in", + "the Software without restriction, including without limitation the rights to", + "use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of", + "the Software, and to permit persons to whom the Software is furnished to do so,", + "subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all", + "copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS", + "FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR", + "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER", + "IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", + "CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ] + }, + { + // Reason: The substack org has been deleted on GH + "name": "minimist", + "fullLicenseText": [ + "This software is released under the MIT license:", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy of", + "this software and associated documentation files (the \"Software\"), to deal in", + "the Software without restriction, including without limitation the rights to", + "use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of", + "the Software, and to permit persons to whom the Software is furnished to do so,", + "subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all", + "copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS", + "FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR", + "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER", + "IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", + "CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ] + }, + { + // Reason: The substack org has been deleted on GH + "name": "mkdirp", + "fullLicenseText": [ + "Copyright 2010 James Halliday (mail@substack.net)", + "", + "This project is free software released under the MIT/X11 license:", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN", + "THE SOFTWARE." + ] } ] diff --git a/cgmanifest.json b/cgmanifest.json index df82bc993e4..726864e87f6 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -484,6 +484,32 @@ "That's all there is to it!" ] }, + { + "component": { + "type": "other", + "other": { + "name": "H.264/AVC Video Standard", + "downloadUrl": "https://chromium.googlesource.com/chromium/third_party/ffmpeg", + "version": "4.4.git" + } + }, + "licenseDetail": [ + "This product is licensed under the AVC patent portfolio license for the personal", + "and non-commercial use of a consumer to (i) encode video in compliance with the AVC standard (\"AVC VIDEO\")", + "and/or (ii) decode AVC video that was encoded by a consumer", + "engaged in a personal and non-commercial activity and/or was obtained from a video provider", + "licensed to provide AVC video. No license is granted or shall be implied for any other use.", + "Additional information may be obtained from MPEG LA LLC. See http://www.MPEGLA.COM.", + "", + "For clarification purposes, this notice does not limit or inhibit the use of the product", + "for normal business uses that are personal to that business which do not include", + "(i) redistribution of the product to third parties, or", + "(ii) creation of content with AVC Standard compliant technologies for distribution to third parties." + ], + "version": "H.264 (08/21)", + "isOnlyProductionDependency": true, + "license": "OTHER" + }, { "component": { "type": "git", diff --git a/cli/CONTRIBUTING.md b/cli/CONTRIBUTING.md index 88effa62a32..d119f1ac98a 100644 --- a/cli/CONTRIBUTING.md +++ b/cli/CONTRIBUTING.md @@ -1,7 +1,7 @@ # Setup 0. Clone, and then run `git submodule update --init --recursive` -1. Get the extensions: [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer) and [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb) +1. Get the extensions: [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) and [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb) 2. Ensure your workspace is set to the `launcher` folder being the root. ## Building the CLI on Windows diff --git a/cli/Cargo.lock b/cli/Cargo.lock index cc6b94be2f4..389b31b5d01 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -8,87 +8,67 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "aead" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c192eb8f11fc081b0fe4259ba5af04217d4e0faddd02417310a927911abd7c8" -dependencies = [ - "crypto-common", - "generic-array", -] - -[[package]] -name = "aes" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" -dependencies = [ - "aes-soft", - "aesni", - "cipher 0.2.5", -] - -[[package]] -name = "aes" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe0133578c0986e1fe3dfcd4af1cc5b2dd6c3dbf534d69916ce16a2701d40ba" -dependencies = [ - "cfg-if", - "cipher 0.4.3", - "cpufeatures", -] - -[[package]] -name = "aes-gcm" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e1366e0c69c9f927b1fa5ce2c7bf9eafc8f9268c0b9800729e8b267612447c" -dependencies = [ - "aead", - "aes 0.8.1", - "cipher 0.4.3", - "ctr", - "ghash", - "subtle", -] - -[[package]] -name = "aes-soft" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" -dependencies = [ - "cipher 0.2.5", - "opaque-debug", -] - -[[package]] -name = "aesni" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" -dependencies = [ - "cipher 0.2.5", - "opaque-debug", -] - [[package]] name = "aho-corasick" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" dependencies = [ "memchr", ] [[package]] -name = "async-io" -version = "1.7.0" +name = "android_system_properties" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5e18f61464ae81cde0a23e713ae8fd299580c54d697a35820cfd0625b8b0e07" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ + "libc", +] + +[[package]] +name = "async-broadcast" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d26004fe83b2d1cd3a97609b21e39f9a31535822210fe83205d2ce48866ea61" +dependencies = [ + "event-listener", + "futures-core", + "parking_lot", +] + +[[package]] +name = "async-channel" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-executor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "once_cell", + "slab", +] + +[[package]] +name = "async-io" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e21f3a490c72b3b0cf44962180e60045de2925d8dff97918f7ee43c8f637c7" +dependencies = [ + "autocfg", "concurrent-queue", "futures-lite", "libc", @@ -103,10 +83,36 @@ dependencies = [ ] [[package]] -name = "async-trait" -version = "0.1.53" +name = "async-lock" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600" +checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-recursion" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-task" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" + +[[package]] +name = "async-trait" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" dependencies = [ "proc-macro2", "quote", @@ -136,21 +142,15 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" -[[package]] -name = "base64ct" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdca834647821e0b13d9539a8634eb62d3501b6b6c2cec1722786ee6671b851" - [[package]] name = "bcrypt-pbkdf" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12621b8e87feb183a6e5dbb315e49026b2229c4398797ee0ae2d1bc00aef41b9" +checksum = "7c38c03b9506bd92bf1ef50665a81eda156f615438f7654bffba58907e6149d7" dependencies = [ "blowfish", - "crypto-mac 0.11.1", - "pbkdf2 0.8.0", + "crypto-mac", + "pbkdf2", "sha2 0.9.9", "zeroize", ] @@ -178,29 +178,13 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" dependencies = [ "generic-array", ] -[[package]] -name = "block-modes" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a0e8073e8baa88212fb5823574c02ebccb395136ba9a164ab89379ec6072f0" -dependencies = [ - "block-padding 0.2.1", - "cipher 0.2.5", -] - -[[package]] -name = "block-padding" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" - [[package]] name = "block-padding" version = "0.3.2" @@ -217,15 +201,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe3ff3fc1de48c1ac2e3341c4df38b0d1bfb8fdf04632a187c8b75aaa319a7ab" dependencies = [ "byteorder", - "cipher 0.3.0", + "cipher", "opaque-debug", ] [[package]] name = "bumpalo" -version = "3.9.1" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" +checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" [[package]] name = "byteorder" @@ -235,9 +219,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" [[package]] name = "cache-padded" @@ -245,15 +229,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" -[[package]] -name = "cbc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" -dependencies = [ - "cipher 0.4.3", -] - [[package]] name = "cc" version = "1.0.73" @@ -266,40 +241,22 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chacha20" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fc89c7c5b9e7a02dfe45cd2367bae382f9ed31c61ca8debe5f827c420a2f08" -dependencies = [ - "cfg-if", - "cipher 0.4.3", - "cpufeatures", -] - [[package]] name = "chrono" -version = "0.4.19" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" dependencies = [ - "libc", + "iana-time-zone", + "js-sys", "num-integer", "num-traits", "serde", "time", + "wasm-bindgen", "winapi", ] -[[package]] -name = "cipher" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" -dependencies = [ - "generic-array", -] - [[package]] name = "cipher" version = "0.3.0" @@ -309,28 +266,18 @@ dependencies = [ "generic-array", ] -[[package]] -name = "cipher" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1873270f8f7942c191139cb8a40fd228da6c3fd2fc376d7e92d47aa14aeb59e" -dependencies = [ - "crypto-common", - "inout", -] - [[package]] name = "clap" -version = "3.1.6" +version = "3.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8c93436c21e4698bacadf42917db28b23017027a4deccb35dbe47a7e7840123" +checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" dependencies = [ "atty", "bitflags", "clap_derive", + "clap_lex", "indexmap", - "lazy_static", - "os_str_bytes", + "once_cell", "strsim", "termcolor", "textwrap", @@ -338,9 +285,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.1.4" +version = "3.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95d038ede1a964ce99f49cbe27a7fb538d1da595e4b4f70b8c8f338d17bf16" +checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" dependencies = [ "heck", "proc-macro-error", @@ -392,7 +339,7 @@ dependencies = [ "tar", "tempfile", "tokio", - "tokio-util 0.7.0", + "tokio-util", "tunnels", "url", "uuid", @@ -401,24 +348,33 @@ dependencies = [ ] [[package]] -name = "concurrent-queue" -version = "1.2.2" +name = "codespan-reporting" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "concurrent-queue" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" dependencies = [ "cache-padded", ] [[package]] name = "console" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28b32d32ca44b70c3e4acd7db1babf555fa026e385fb95f18028f88848b3c31" +checksum = "c050367d967ced717c04b65d8c619d863ef9292ce0c5760028655a2fb298718c" dependencies = [ "encode_unicode", + "lazy_static", "libc", - "once_cell", - "regex", "terminal_size", "unicode-width", "winapi", @@ -442,9 +398,9 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "cpufeatures" -version = "0.2.2" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" dependencies = [ "libc", ] @@ -460,9 +416,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.2" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" dependencies = [ "cfg-if", "crossbeam-utils", @@ -470,9 +426,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" dependencies = [ "cfg-if", "crossbeam-epoch", @@ -481,25 +437,24 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.7" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9" +checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348" dependencies = [ + "autocfg", "cfg-if", "crossbeam-utils", - "lazy_static", "memoffset", "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.7" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6" +checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" dependencies = [ "cfg-if", - "lazy_static", ] [[package]] @@ -509,20 +464,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", - "rand_core 0.6.3", "typenum", ] -[[package]] -name = "crypto-mac" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" -dependencies = [ - "generic-array", - "subtle", -] - [[package]] name = "crypto-mac" version = "0.11.1" @@ -534,25 +478,47 @@ dependencies = [ ] [[package]] -name = "ctr" -version = "0.9.1" +name = "cxx" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d14f329cfbaf5d0e06b5e87fff7e265d2673c5ea7d2c27691a2c107db1442a0" +checksum = "19f39818dcfc97d45b03953c1292efc4e80954e1583c4aa770bac1383e2310a4" dependencies = [ - "cipher 0.4.3", + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", ] [[package]] -name = "curve25519-dalek" -version = "3.2.0" +name = "cxx-build" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +checksum = "3e580d70777c116df50c390d1211993f62d40302881e54d4b79727acb83d0199" dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", - "subtle", - "zeroize", + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56a46460b88d1cec95112c8c363f0e2c39afdb237f60583b0b36343bf627ea9c" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747b608fecf06b0d72d440f27acc99288207324b793be2c17991839f3d4995ea" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -574,9 +540,9 @@ dependencies = [ [[package]] name = "dialoguer" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8c8ae48e400addc32a8710c8d62d55cb84249a7d58ac4cd959daecfbaddc545" +checksum = "a92e7e37ecef6857fdc0c0c5d42fd5b0938e46590c2183cc92dd310a6d078eb1" dependencies = [ "console", "tempfile", @@ -594,11 +560,11 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" dependencies = [ - "block-buffer 0.10.2", + "block-buffer 0.10.3", "crypto-common", "subtle", ] @@ -623,43 +589,20 @@ dependencies = [ [[package]] name = "dirs-sys" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", "redox_users", "winapi", ] -[[package]] -name = "ed25519" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9c280362032ea4203659fc489832d0204ef09f247a0506f170dafcac08c369" -dependencies = [ - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" -dependencies = [ - "curve25519-dalek", - "ed25519", - "rand 0.7.3", - "serde", - "sha2 0.9.9", - "zeroize", -] - [[package]] name = "either" -version = "1.6.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" [[package]] name = "encode_unicode" @@ -669,18 +612,18 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.30" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" dependencies = [ "cfg-if", ] [[package]] name = "enumflags2" -version = "0.6.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c8d82922337cd23a15f88b70d8e4ef5f11da38dd7cdb55e84dd5de99695da0" +checksum = "e75d4cd21b95383444831539909fbb14b9dc3fdceb2a6f5d36577329a1f55ccb" dependencies = [ "enumflags2_derive", "serde", @@ -688,9 +631,9 @@ dependencies = [ [[package]] name = "enumflags2_derive" -version = "0.6.4" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "946ee94e3dbf58fdd324f9ce245c7b238d46a66f00e86a020b71996349e46cce" +checksum = "f58dc3c5e468259f19f2d46304a6b28f1c3d034442e14b322d2b850e36f6d5ae" dependencies = [ "proc-macro2", "quote", @@ -712,35 +655,39 @@ dependencies = [ ] [[package]] -name = "fastrand" -version = "1.7.0" +name = "event-listener" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" dependencies = [ "instant", ] [[package]] name = "filetime" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98" +checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c" dependencies = [ "cfg-if", "libc", "redox_syscall", - "winapi", + "windows-sys", ] [[package]] name = "flate2" -version = "1.0.22" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" dependencies = [ - "cfg-if", "crc32fast", - "libc", "miniz_oxide", ] @@ -776,9 +723,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" +checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" dependencies = [ "futures-channel", "futures-core", @@ -791,9 +738,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" dependencies = [ "futures-core", "futures-sink", @@ -801,15 +748,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" +checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" [[package]] name = "futures-executor" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" dependencies = [ "futures-core", "futures-task", @@ -818,9 +765,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" +checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" [[package]] name = "futures-lite" @@ -839,9 +786,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" dependencies = [ "proc-macro2", "quote", @@ -850,21 +797,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" +checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" [[package]] name = "futures-task" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" +checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" [[package]] name = "futures-util" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" dependencies = [ "futures-channel", "futures-core", @@ -880,9 +827,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.5" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" dependencies = [ "typenum", "version_check", @@ -911,30 +858,20 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ "cfg-if", "libc", - "wasi 0.10.0+wasi-snapshot-preview1", -] - -[[package]] -name = "ghash" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" -dependencies = [ - "opaque-debug", - "polyval", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] name = "h2" -version = "0.3.11" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f1f717ddc7b2ba36df7e871fd88db79326551d3d6f1fc406fbfd28b582ff8e" +checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" dependencies = [ "bytes", "fnv", @@ -945,15 +882,15 @@ dependencies = [ "indexmap", "slab", "tokio", - "tokio-util 0.6.9", + "tokio-util", "tracing", ] [[package]] name = "hashbrown" -version = "0.11.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" @@ -970,46 +907,32 @@ dependencies = [ "libc", ] +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hex-literal" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" -[[package]] -name = "hkdf" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f" -dependencies = [ - "digest 0.9.0", - "hmac 0.10.1", -] - -[[package]] -name = "hmac" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" -dependencies = [ - "crypto-mac 0.10.1", - "digest 0.9.0", -] - [[package]] name = "hmac" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.3", + "digest 0.10.5", ] [[package]] name = "http" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ "bytes", "fnv", @@ -1018,9 +941,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", "http", @@ -1029,9 +952,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9100414882e15fb7feccb4897e5f0ff0ff1ca7d1a86a23208ada4d7a18e6c6c4" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" @@ -1041,9 +964,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.19" +version = "0.14.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f" +checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" dependencies = [ "bytes", "futures-channel", @@ -1076,6 +999,30 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "iana-time-zone" +version = "0.1.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5a6ef98976b22b3b7f2f3a806f858cb862044cfa66805aa3ad84cb3d3b785ed" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fde6edd6cef363e9359ed3c98ba64590ba9eecba2293eb5a723ab32aee8926aa" +dependencies = [ + "cxx", + "cxx-build", +] + [[package]] name = "idna" version = "0.3.0" @@ -1088,9 +1035,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.8.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ "autocfg", "hashbrown", @@ -1114,7 +1061,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "block-padding 0.3.2", + "block-padding", "generic-array", ] @@ -1129,30 +1076,30 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e70ee094dc02fd9c13fdad4940090f22dbd6ac7c9e7094a46cf0232a50bc7c" +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" [[package]] name = "itoa" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" [[package]] name = "js-sys" -version = "0.3.56" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" dependencies = [ "wasm-bindgen", ] [[package]] name = "keyring" -version = "1.1.2" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "894a58932447ecc241a3545135bdfd26ac0e611295b391904ce0b085310ab38c" +checksum = "38fb8399ddcabfccb274577a8d90f0653e0b5b5977797c1c8834ad09839a10e5" dependencies = [ "byteorder", "secret-service", @@ -1168,16 +1115,26 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" + +[[package]] +name = "link-cplusplus" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +dependencies = [ + "cc", +] [[package]] name = "lock_api" -version = "0.4.6" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ + "autocfg", "scopeguard", ] @@ -1198,9 +1155,9 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memoffset" @@ -1219,12 +1176,11 @@ checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" [[package]] name = "miniz_oxide" -version = "0.4.4" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" dependencies = [ "adler", - "autocfg", ] [[package]] @@ -1236,14 +1192,14 @@ dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.36.1", + "windows-sys", ] [[package]] name = "native-tls" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" +checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" dependencies = [ "lazy_static", "libc", @@ -1257,24 +1213,13 @@ dependencies = [ "tempfile", ] -[[package]] -name = "nb-connect" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1bb540dc6ef51cfe1916ec038ce7a620daf3a111e2502d745197cd53d6bca15" -dependencies = [ - "libc", - "socket2", -] - [[package]] name = "nix" -version = "0.22.3" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf" +checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" dependencies = [ "bitflags", - "cc", "cfg-if", "libc", "memoffset", @@ -1291,11 +1236,11 @@ dependencies = [ [[package]] name = "num" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f" +checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" dependencies = [ - "num-bigint 0.3.3", + "num-bigint", "num-complex", "num-integer", "num-iter", @@ -1303,17 +1248,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-bigint" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - [[package]] name = "num-bigint" version = "0.4.3" @@ -1328,18 +1262,18 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.3.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5" +checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" dependencies = [ "num-traits", ] [[package]] name = "num-integer" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg", "num-traits", @@ -1358,21 +1292,21 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.3.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ "autocfg", - "num-bigint 0.3.3", + "num-bigint", "num-integer", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", ] @@ -1395,9 +1329,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "once_cell" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" [[package]] name = "opaque-debug" @@ -1407,28 +1341,40 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "open" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0524af9508f9b5c4eb41dce095860456727748f63b478d625f119a70e0d764a" +checksum = "f2423ffbf445b82e58c3b1543655968923dd06f85432f10be2bb4f1b7122f98c" dependencies = [ "pathdiff", - "winapi", + "windows-sys", ] [[package]] name = "openssl" -version = "0.10.38" +version = "0.10.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95" +checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" dependencies = [ "bitflags", "cfg-if", "foreign-types", "libc", "once_cell", + "openssl-macros", "openssl-sys", ] +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "openssl-probe" version = "0.1.5" @@ -1446,9 +1392,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.72" +version = "0.9.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb" +checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce" dependencies = [ "autocfg", "cc", @@ -1546,14 +1492,21 @@ dependencies = [ ] [[package]] -name = "os_str_bytes" -version = "6.0.0" +name = "ordered-stream" +version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" +checksum = "44630c059eacfd6e08bdaa51b1db2ce33119caa4ddc1235e923109aa5f25ccb1" dependencies = [ - "memchr", + "futures-core", + "pin-project-lite", ] +[[package]] +name = "os_str_bytes" +version = "6.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" + [[package]] name = "parking" version = "2.0.0" @@ -1572,27 +1525,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys 0.32.0", + "windows-sys", ] [[package]] -name = "password-hash" -version = "0.4.2" +name = "paste" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" -dependencies = [ - "base64ct", - "rand_core 0.6.3", - "subtle", -] +checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" [[package]] name = "pathdiff" @@ -1606,19 +1554,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" dependencies = [ - "crypto-mac 0.11.1", -] - -[[package]] -name = "pbkdf2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" -dependencies = [ - "digest 0.10.3", - "hmac 0.12.1", - "password-hash", - "sha2 0.10.2", + "crypto-mac", ] [[package]] @@ -1641,16 +1577,17 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" +checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" [[package]] name = "polling" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" +checksum = "899b00b9c8ab553c743b3e11e87c5c7d423b2a2de229ba95b24a756344748011" dependencies = [ + "autocfg", "cfg-if", "libc", "log", @@ -1658,29 +1595,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "poly1305" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" -dependencies = [ - "cpufeatures", - "opaque-debug", - "universal-hash", -] - -[[package]] -name = "polyval" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef234e08c11dfcb2e56f79fd70f6f2eb7f025c0ce2333e82f4f0518ecad30c6" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", -] - [[package]] name = "ppv-lite86" version = "0.2.16" @@ -1689,19 +1603,11 @@ checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" [[package]] name = "proc-macro-crate" -version = "0.1.5" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" -dependencies = [ - "toml", -] - -[[package]] -name = "proc-macro-crate" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" dependencies = [ + "once_cell", "thiserror", "toml", ] @@ -1732,18 +1638,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.43" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.15" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] @@ -1769,7 +1675,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha 0.3.1", - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -1789,7 +1695,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -1803,11 +1709,11 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.5", + "getrandom 0.2.7", ] [[package]] @@ -1821,9 +1727,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.5.1" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" dependencies = [ "autocfg", "crossbeam-deque", @@ -1833,41 +1739,41 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.9.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" dependencies = [ "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "lazy_static", "num_cpus", ] [[package]] name = "redox_syscall" -version = "0.2.11" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] [[package]] name = "redox_users" -version = "0.4.0" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.5", + "getrandom 0.2.7", "redox_syscall", + "thiserror", ] [[package]] name = "regex" -version = "1.5.5" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" dependencies = [ "aho-corasick", "memchr", @@ -1876,9 +1782,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "remove_dir_all" @@ -1891,9 +1797,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.9" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f242f1488a539a79bac6dbe7c8609ae43b7914b7736210f239a37cccb32525" +checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" dependencies = [ "base64", "bytes", @@ -1907,10 +1813,10 @@ dependencies = [ "hyper-tls", "ipnet", "js-sys", - "lazy_static", "log", "mime", "native-tls", + "once_cell", "percent-encoding", "pin-project-lite", "serde", @@ -1918,7 +1824,8 @@ dependencies = [ "serde_urlencoded", "tokio", "tokio-native-tls", - "tokio-util 0.6.9", + "tokio-util", + "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", @@ -1928,19 +1835,20 @@ dependencies = [ [[package]] name = "rmp" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f55e5fa1446c4d5dd1f5daeed2a4fe193071771a2636274d0d7a3b082aa7ad6" +checksum = "44519172358fd6d58656c86ab8e7fbc9e1490c3e8f14d35ed78ca0dd07403c9f" dependencies = [ "byteorder", "num-traits", + "paste", ] [[package]] name = "rmp-serde" -version = "1.0.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3eedffbfcc6a428f230c04baf8f59bd73c1781361e4286111fe900849aaddaf" +checksum = "c5b13be192e0220b8afb7222aa5813cb62cc269ebb5cac346ca6487681d2913e" dependencies = [ "byteorder", "rmp", @@ -1950,31 +1858,25 @@ dependencies = [ [[package]] name = "russh" version = "0.34.0-beta.16" -source = "git+https://github.com/microsoft/vscode-russh?branch=own#fbf1ae38259bb20bd0b0ca310abc9d765abb6a90" +source = "git+https://github.com/microsoft/vscode-russh?branch=main#e1d29f04658b62b33391a0b14899517fd72d1f5c" dependencies = [ - "aes 0.8.1", - "aes-gcm", "bitflags", "byteorder", - "chacha20", - "ctr", - "curve25519-dalek", - "digest 0.10.3", + "digest 0.10.5", "flate2", "futures", "generic-array", "hex-literal", - "hmac 0.12.1", + "hmac", "log", - "num-bigint 0.4.3", + "num-bigint", "once_cell", "openssl", - "poly1305", "rand 0.8.5", "russh-cryptovec", "russh-keys", - "sha1", - "sha2 0.10.2", + "sha1 0.10.5", + "sha2 0.10.6", "subtle", "thiserror", "tokio", @@ -1983,7 +1885,7 @@ dependencies = [ [[package]] name = "russh-cryptovec" version = "0.7.0-beta.1" -source = "git+https://github.com/microsoft/vscode-russh?branch=own#fbf1ae38259bb20bd0b0ca310abc9d765abb6a90" +source = "git+https://github.com/microsoft/vscode-russh?branch=main#e1d29f04658b62b33391a0b14899517fd72d1f5c" dependencies = [ "libc", "winapi", @@ -1991,34 +1893,27 @@ dependencies = [ [[package]] name = "russh-keys" -version = "0.22.0-beta.6" -source = "git+https://github.com/microsoft/vscode-russh?branch=own#fbf1ae38259bb20bd0b0ca310abc9d765abb6a90" +version = "0.22.0-beta.7" +source = "git+https://github.com/microsoft/vscode-russh?branch=main#e1d29f04658b62b33391a0b14899517fd72d1f5c" dependencies = [ - "aes 0.8.1", "bcrypt-pbkdf", "bit-vec", - "block-padding 0.3.2", "byteorder", - "cbc", - "ctr", "data-encoding", "dirs 3.0.2", - "ed25519-dalek", "futures", - "hmac 0.12.1", "inout", "log", "md5", - "num-bigint 0.4.3", + "num-bigint", "num-integer", "openssl", - "pbkdf2 0.11.0", "rand 0.7.3", "rand_core 0.5.1", "russh-cryptovec", "serde", "serde_derive", - "sha2 0.10.2", + "sha2 0.10.6", "thiserror", "tokio", "tokio-stream", @@ -2033,26 +1928,20 @@ checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" [[package]] name = "ryu" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" [[package]] name = "schannel" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" dependencies = [ "lazy_static", - "winapi", + "windows-sys", ] -[[package]] -name = "scoped-tls" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" - [[package]] name = "scopeguard" version = "1.1.0" @@ -2060,30 +1949,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] -name = "secret-service" -version = "2.0.1" +name = "scratch" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2400fb1bf2a87b303ada204946294f932ade4929477e9e2bf66d7b49a66656ec" +checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" + +[[package]] +name = "secret-service" +version = "2.0.2" +source = "git+https://github.com/microsoft/vscode-secret-service-rs?rev=ccef335714cdf3744ff85f812b8fba5b6194dcfa#ccef335714cdf3744ff85f812b8fba5b6194dcfa" dependencies = [ - "aes 0.6.0", - "block-modes", - "hkdf", - "lazy_static", + "futures-util", + "generic-array", "num", + "once_cell", + "openssl", "rand 0.8.5", "serde", - "sha2 0.9.9", "zbus", - "zbus_macros", - "zvariant", - "zvariant_derive", ] [[package]] name = "security-framework" -version = "2.6.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" +checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" dependencies = [ "bitflags", "core-foundation", @@ -2104,27 +1994,27 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.136" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" dependencies = [ "serde_derive", ] [[package]] name = "serde_bytes" -version = "0.11.5" +version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16ae07dd2f88a366f15bd0632ba725227018c69a1c8550a927324f8eb8368bb9" +checksum = "cfc50e8183eeeb6178dcb167ae34a8051d63535023ae38b5d8d12beae193d37b" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.136" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" dependencies = [ "proc-macro2", "quote", @@ -2133,9 +2023,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.79" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074" dependencies = [ "itoa", "ryu", @@ -2144,9 +2034,9 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ad84e47328a31223de7fed7a4f5087f2d6ddfe586cf3ca25b7a165bc0a5aed" +checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca" dependencies = [ "proc-macro2", "quote", @@ -2173,20 +2063,35 @@ checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.3", + "digest 0.10.5", ] [[package]] name = "sha1" -version = "0.10.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77f4e7f65455545c2153c1253d25056825e77ee2533f0e41deb65a93a34852f" +checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" +dependencies = [ + "sha1_smol", +] + +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.3", + "digest 0.10.5", ] +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + [[package]] name = "sha2" version = "0.9.9" @@ -2202,13 +2107,13 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.2" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.3", + "digest 0.10.5", ] [[package]] @@ -2220,29 +2125,26 @@ dependencies = [ "libc", ] -[[package]] -name = "signature" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0ea32af43239f0d353a7dd75a22d94c329c8cdaafdcb4c1c1335aa10c298a4a" - [[package]] name = "slab" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] [[package]] name = "smallvec" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "socket2" -version = "0.4.4" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" dependencies = [ "libc", "winapi", @@ -2268,9 +2170,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.99" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" +checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" dependencies = [ "proc-macro2", "quote", @@ -2291,9 +2193,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.23.5" +version = "0.23.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07fa4c84a5305909b0eedfcc8d1f2fafdbede645bb700a45ecaafe681a0ac5d6" +checksum = "3977ec2e0520829be45c8a2df70db2bf364714d8a748316a10c3c35d4d2b01c9" dependencies = [ "cfg-if", "core-foundation-sys", @@ -2350,24 +2252,24 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" [[package]] name = "thiserror" -version = "1.0.30" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.30" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" dependencies = [ "proc-macro2", "quote", @@ -2387,9 +2289,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] @@ -2402,9 +2304,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.20.1" +version = "1.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a8325f63a7d4774dd041e363b2409ed1c5cbbd0f867795e661df066b2b0a581" +checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" dependencies = [ "autocfg", "bytes", @@ -2412,7 +2314,6 @@ dependencies = [ "memchr", "mio", "num_cpus", - "once_cell", "parking_lot", "pin-project-lite", "signal-hook-registry", @@ -2423,9 +2324,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" dependencies = [ "proc-macro2", "quote", @@ -2444,9 +2345,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.8" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" +checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" dependencies = [ "futures-core", "pin-project-lite", @@ -2469,31 +2370,17 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.6.9" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "log", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64910e1b9c1901aaf5375561e35b9c057d95ff41a44ede043a03e09279eabaf1" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" dependencies = [ "bytes", "futures-core", "futures-io", "futures-sink", - "log", "pin-project-lite", "tokio", + "tracing", ] [[package]] @@ -2507,26 +2394,38 @@ dependencies = [ [[package]] name = "tower-service" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.36" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "pin-project-lite", + "tracing-attributes", "tracing-core", ] [[package]] -name = "tracing-core" -version = "0.1.29" +name = "tracing-attributes" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ "once_cell", ] @@ -2560,7 +2459,7 @@ dependencies = [ [[package]] name = "tunnels" version = "0.1.0" -source = "git+https://github.com/connor4312/dev-tunnels?branch=host-relay#066acce214983ae28110d6d867e5b8006cc2da7f" +source = "git+https://github.com/microsoft/dev-tunnels?rev=3870e9133dfb9557774521bb447827f19b26e55d#3870e9133dfb9557774521bb447827f19b26e55d" dependencies = [ "async-trait", "chrono", @@ -2574,7 +2473,7 @@ dependencies = [ "thiserror", "tokio", "tokio-tungstenite", - "tokio-util 0.7.0", + "tokio-util", "tungstenite", "url", "uuid", @@ -2587,47 +2486,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] -name = "unicode-bidi" -version = "0.3.7" +name = "uds_windows" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" +checksum = "ce65604324d3cce9b966701489fbd0cf318cb1f7bd9dd07ac9a4ee6fb791930d" +dependencies = [ + "tempfile", + "winapi", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" [[package]] name = "unicode-ident" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" [[package]] name = "unicode-normalization" -version = "0.1.19" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] [[package]] name = "unicode-width" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "unicode-xid" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" - -[[package]] -name = "universal-hash" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" -dependencies = [ - "crypto-common", - "subtle", -] +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "url" @@ -2652,7 +2551,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "getrandom 0.2.5", + "getrandom 0.2.7", "serde", ] @@ -2704,9 +2603,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.79" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2714,13 +2613,13 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.79" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", "quote", "syn", @@ -2729,9 +2628,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.29" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb6ec270a31b1d3c7e266b999739109abce8b6c87e4b31fcfcd788b65267395" +checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" dependencies = [ "cfg-if", "js-sys", @@ -2741,9 +2640,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.79" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2751,9 +2650,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.79" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", @@ -2764,15 +2663,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.79" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] name = "web-sys" -version = "0.3.56" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" dependencies = [ "js-sys", "wasm-bindgen", @@ -2833,20 +2732,7 @@ dependencies = [ "bitflags", "err-derive", "widestring", - "windows-sys 0.36.1", -] - -[[package]] -name = "windows-sys" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6" -dependencies = [ - "windows_aarch64_msvc 0.32.0", - "windows_i686_gnu 0.32.0", - "windows_i686_msvc 0.32.0", - "windows_x86_64_gnu 0.32.0", - "windows_x86_64_msvc 0.32.0", + "windows-sys", ] [[package]] @@ -2855,67 +2741,37 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_msvc" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" - [[package]] name = "windows_aarch64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" -[[package]] -name = "windows_i686_gnu" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" - [[package]] name = "windows_i686_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" -[[package]] -name = "windows_i686_msvc" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" - [[package]] name = "windows_i686_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" -[[package]] -name = "windows_x86_64_gnu" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" - [[package]] name = "windows_x86_64_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" -[[package]] -name = "windows_x86_64_msvc" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" - [[package]] name = "windows_x86_64_msvc" version = "0.36.1" @@ -2924,18 +2780,18 @@ checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" [[package]] name = "winreg" -version = "0.7.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" dependencies = [ "winapi", ] [[package]] name = "xattr" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" dependencies = [ "libc", ] @@ -2947,64 +2803,77 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e262a29d0e61ccf2b6190d7050d4b237535fc76ce4c1210d9caa316f71dffa75" dependencies = [ "bit-vec", - "num-bigint 0.4.3", + "num-bigint", ] [[package]] name = "zbus" -version = "1.9.3" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cbeb2291cd7267a94489b71376eda33496c1b9881adf6b36f26cc2779f3fc49" +checksum = "be2db10dd0354816a3615c72deff837f983d5c8ad9a0312727d0f89a5a9e9145" dependencies = [ + "async-broadcast", + "async-channel", + "async-executor", "async-io", + "async-lock", + "async-recursion", + "async-task", + "async-trait", "byteorder", "derivative", + "dirs 4.0.0", "enumflags2", - "fastrand", - "futures", - "nb-connect", + "event-listener", + "futures-core", + "futures-sink", + "futures-util", + "hex", "nix", "once_cell", - "polling", - "scoped-tls", + "ordered-stream", + "rand 0.8.5", "serde", "serde_repr", + "sha1 0.6.1", + "static_assertions", + "tracing", + "uds_windows", + "winapi", "zbus_macros", + "zbus_names", "zvariant", ] [[package]] name = "zbus_macros" -version = "1.9.3" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa3959a7847cf95e3d51e312856617c5b1b77191176c65a79a5f14d778bbe0a6" +checksum = "7d03f1a5fb482cc0d97f3d3de9fe2e942f436a635a5fb6c12c9f03f21eb03075" dependencies = [ - "proc-macro-crate 0.1.5", + "proc-macro-crate", "proc-macro2", "quote", + "regex", "syn", ] +[[package]] +name = "zbus_names" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a408fd8a352695690f53906dc7fd036be924ec51ea5e05666ff42685ed0af5" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + [[package]] name = "zeroize" -version = "1.5.7" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] +checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" [[package]] name = "zip" @@ -3021,9 +2890,9 @@ dependencies = [ [[package]] name = "zvariant" -version = "2.10.0" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a68c7b55f2074489b7e8e07d2d0a6ee6b4f233867a653c664d8020ba53692525" +checksum = "b794fb7f59af4105697b0449ba31731ee5dbb3e773a17dbdf3d36206ea1b1644" dependencies = [ "byteorder", "enumflags2", @@ -3035,11 +2904,11 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "2.10.0" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ca5e22593eb4212382d60d26350065bf2a02c34b85bc850474a74b589a3de9" +checksum = "dd58d4b6c8e26d3dd2149c8c40c6613ef6451b9885ff1296d1ac86c388351a54" dependencies = [ - "proc-macro-crate 1.1.3", + "proc-macro-crate", "proc-macro2", "quote", "syn", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 936f75bb9dd..eca7ee53a6d 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -39,7 +39,7 @@ serde_bytes = "0.11.5" chrono = { version = "0.4", features = ["serde"] } gethostname = "0.2.3" libc = "0.2" -tunnels = { git = "https://github.com/connor4312/dev-tunnels", branch = "host-relay", features = ["connections", "vendored-openssl"] } +tunnels = { git = "https://github.com/microsoft/dev-tunnels", rev = "3870e9133dfb9557774521bb447827f19b26e55d", default-features = false, features = ["connections", "vendored-openssl"] } keyring = "1.1" dialoguer = "0.10" hyper = "0.14" @@ -56,6 +56,12 @@ windows-service = "0.5" [target.'cfg(target_os = "linux")'.dependencies] tar = { version = "0.4" } +[patch.crates-io] +russh = { git = "https://github.com/microsoft/vscode-russh", branch = "main" } +russh-cryptovec = { git = "https://github.com/microsoft/vscode-russh", branch = "main" } +russh-keys = { git = "https://github.com/microsoft/vscode-russh", branch = "main" } +secret-service = { git = "https://github.com/microsoft/vscode-secret-service-rs", rev = "ccef335714cdf3744ff85f812b8fba5b6194dcfa" } + [profile.release] strip = true lto = true diff --git a/cli/src/desktop/version_manager.rs b/cli/src/desktop/version_manager.rs index c518dc38e3a..3c3aadda9d8 100644 --- a/cli/src/desktop/version_manager.rs +++ b/cli/src/desktop/version_manager.rs @@ -419,7 +419,7 @@ mod tests { .to_string_lossy() .to_string(); assert_eq!( - RequestedVersion::try_from((&exe).as_str()).unwrap(), + RequestedVersion::try_from(exe.as_str()).unwrap(), RequestedVersion::Path(exe), ); } diff --git a/cli/src/tunnels/dev_tunnels.rs b/cli/src/tunnels/dev_tunnels.rs index 224e0a43b62..cc1c639a18f 100644 --- a/cli/src/tunnels/dev_tunnels.rs +++ b/cli/src/tunnels/dev_tunnels.rs @@ -19,7 +19,7 @@ use serde::{Deserialize, Serialize}; use std::sync::{Arc, Mutex}; use std::time::Duration; use tokio::sync::{mpsc, watch}; -use tunnels::connections::{ForwardedPortConnection, HostRelay}; +use tunnels::connections::{ForwardedPortConnection, RelayTunnelHost}; use tunnels::contracts::{ Tunnel, TunnelPort, TunnelRelayTunnelEndpoint, PORT_TOKEN, TUNNEL_PROTOCOL_AUTO, }; @@ -607,7 +607,7 @@ impl DevTunnels { struct ActiveTunnelManager { close_tx: Option>, endpoint_rx: watch::Receiver>>, - relay: Arc>, + relay: Arc>, } impl ActiveTunnelManager { @@ -620,7 +620,7 @@ impl ActiveTunnelManager { let (endpoint_tx, endpoint_rx) = watch::channel(None); let (close_tx, close_rx) = mpsc::channel(1); - let relay = Arc::new(tokio::sync::Mutex::new(HostRelay::new(locator, mgmt))); + let relay = Arc::new(tokio::sync::Mutex::new(RelayTunnelHost::new(locator, mgmt))); let relay_spawned = relay.clone(); tokio::spawn(async move { @@ -719,7 +719,7 @@ impl ActiveTunnelManager { async fn spawn_tunnel( log: log::Logger, - relay: Arc>, + relay: Arc>, mut close_rx: mpsc::Receiver<()>, endpoint_tx: watch::Sender>>, access_token_provider: impl AccessTokenProvider + 'static, diff --git a/cli/src/util/command.rs b/cli/src/util/command.rs index 25b2116268b..7a7795e1590 100644 --- a/cli/src/util/command.rs +++ b/cli/src/util/command.rs @@ -26,7 +26,7 @@ where e, format!( "failed to execute command '{}'", - (&command_str).as_ref().to_string_lossy() + command_str.as_ref().to_string_lossy() ), ) }) diff --git a/cli/src/util/sync.rs b/cli/src/util/sync.rs index 6e069215e74..5f33419488a 100644 --- a/cli/src/util/sync.rs +++ b/cli/src/util/sync.rs @@ -19,9 +19,7 @@ where /// Waits for the barrier to be closed, returning a value if one was sent. pub async fn wait(&mut self) -> Result { loop { - if let Err(e) = self.0.changed().await { - return Err(e); - } + self.0.changed().await?; if let Some(v) = *(self.0.borrow()) { return Ok(v); diff --git a/extensions/configuration-editing/package.json b/extensions/configuration-editing/package.json index a124a0432e6..37a5a9137bb 100644 --- a/extensions/configuration-editing/package.json +++ b/extensions/configuration-editing/package.json @@ -21,7 +21,7 @@ }, "dependencies": { "jsonc-parser": "^2.2.1", - "vscode-nls": "^5.1.0" + "vscode-nls": "^5.2.0" }, "capabilities": { "virtualWorkspaces": true, @@ -71,6 +71,10 @@ "fileMatch": "%APP_SETTINGS_HOME%/keybindings.json", "url": "vscode://schemas/keybindings" }, + { + "fileMatch": "%APP_SETTINGS_HOME%/profiles/*/keybindings.json", + "url": "vscode://schemas/keybindings" + }, { "fileMatch": "vscode://defaultsettings/*.json", "url": "vscode://schemas/settings/default" @@ -119,6 +123,10 @@ "fileMatch": "%APP_SETTINGS_HOME%/snippets/*.json", "url": "vscode://schemas/snippets" }, + { + "fileMatch": "%APP_SETTINGS_HOME%/profiles/*/snippets/.json", + "url": "vscode://schemas/snippets" + }, { "fileMatch": "%APP_SETTINGS_HOME%/sync/snippets/preview/*.json", "url": "vscode://schemas/snippets" diff --git a/extensions/configuration-editing/schemas/devContainer.codespaces.schema.json b/extensions/configuration-editing/schemas/devContainer.codespaces.schema.json index d224f919109..681ca6105cf 100644 --- a/extensions/configuration-editing/schemas/devContainer.codespaces.schema.json +++ b/extensions/configuration-editing/schemas/devContainer.codespaces.schema.json @@ -173,6 +173,13 @@ ] } } + }, + "openFiles": { + "type": "array", + "description": "The paths to the files to open when the codespace is created. Paths are relative to the workspace.", + "items": { + "type": "string" + } } } } diff --git a/extensions/configuration-editing/schemas/devContainer.schema.generated.json b/extensions/configuration-editing/schemas/devContainer.schema.generated.json index 4ae5033f3f1..f94a40021e1 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.generated.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.generated.json @@ -62,24 +62,6 @@ ] } }, - "containerEnv": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "Container environment variables." - }, - "containerUser": { - "type": "string", - "description": "The user the container will be started with. The default is the user on the Docker image." - }, - "mounts": { - "type": "array", - "description": "Mount points to set up when creating the container. See Docker's documentation for the --mount option for the supported syntax.", - "items": { - "type": "string" - } - }, "runArgs": { "type": "array", "description": "The arguments required when starting in the container.", @@ -278,6 +260,83 @@ "type": "boolean", "description": "Controls whether on Linux the container's user should be updated with the local user's UID and GID. On by default when opening from a local folder." }, + "containerEnv": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Container environment variables." + }, + "containerUser": { + "type": "string", + "description": "The user the container will be started with. The default is the user on the Docker image." + }, + "mounts": { + "type": "array", + "description": "Mount points to set up when creating the container. See Docker's documentation for the --mount option for the supported syntax.", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "bind", + "volume" + ], + "description": "Mount type." + }, + "source": { + "type": "string", + "description": "Mount source." + }, + "target": { + "type": "string", + "description": "Mount target." + } + }, + "required": [ + "type", + "source", + "target" + ], + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + }, + "init": { + "type": "boolean", + "description": "Passes the --init flag when creating the dev container." + }, + "privileged": { + "type": "boolean", + "description": "Passes the --privileged flag when creating the dev container." + }, + "capAdd": { + "type": "array", + "description": "Passes docker capabilities to include when creating the dev container.", + "examples": [ + "SYS_PTRACE" + ], + "items": { + "type": "string" + } + }, + "securityOpt": { + "type": "array", + "description": "Passes docker security options to include when creating the dev container.", + "examples": [ + "seccomp=unconfined" + ], + "items": { + "type": "string" + } + }, "remoteEnv": { "type": "object", "additionalProperties": { @@ -600,6 +659,13 @@ } }, "additionalProperties": false + }, + "openFiles": { + "type": "array", + "description": "The paths to the files to open when the codespace is created. Paths are relative to the workspace.", + "items": { + "type": "string" + } } }, "additionalProperties": false @@ -672,24 +738,6 @@ ] } }, - "containerEnv": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "Container environment variables." - }, - "containerUser": { - "type": "string", - "description": "The user the container will be started with. The default is the user on the Docker image." - }, - "mounts": { - "type": "array", - "description": "Mount points to set up when creating the container. See Docker's documentation for the --mount option for the supported syntax.", - "items": { - "type": "string" - } - }, "runArgs": { "type": "array", "description": "The arguments required when starting in the container.", @@ -888,6 +936,83 @@ "type": "boolean", "description": "Controls whether on Linux the container's user should be updated with the local user's UID and GID. On by default when opening from a local folder." }, + "containerEnv": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Container environment variables." + }, + "containerUser": { + "type": "string", + "description": "The user the container will be started with. The default is the user on the Docker image." + }, + "mounts": { + "type": "array", + "description": "Mount points to set up when creating the container. See Docker's documentation for the --mount option for the supported syntax.", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "bind", + "volume" + ], + "description": "Mount type." + }, + "source": { + "type": "string", + "description": "Mount source." + }, + "target": { + "type": "string", + "description": "Mount target." + } + }, + "required": [ + "type", + "source", + "target" + ], + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + }, + "init": { + "type": "boolean", + "description": "Passes the --init flag when creating the dev container." + }, + "privileged": { + "type": "boolean", + "description": "Passes the --privileged flag when creating the dev container." + }, + "capAdd": { + "type": "array", + "description": "Passes docker capabilities to include when creating the dev container.", + "examples": [ + "SYS_PTRACE" + ], + "items": { + "type": "string" + } + }, + "securityOpt": { + "type": "array", + "description": "Passes docker security options to include when creating the dev container.", + "examples": [ + "seccomp=unconfined" + ], + "items": { + "type": "string" + } + }, "remoteEnv": { "type": "object", "additionalProperties": { @@ -1210,6 +1335,13 @@ } }, "additionalProperties": false + }, + "openFiles": { + "type": "array", + "description": "The paths to the files to open when the codespace is created. Paths are relative to the workspace.", + "items": { + "type": "string" + } } }, "additionalProperties": false @@ -1248,24 +1380,6 @@ ] } }, - "containerEnv": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "Container environment variables." - }, - "containerUser": { - "type": "string", - "description": "The user the container will be started with. The default is the user on the Docker image." - }, - "mounts": { - "type": "array", - "description": "Mount points to set up when creating the container. See Docker's documentation for the --mount option for the supported syntax.", - "items": { - "type": "string" - } - }, "runArgs": { "type": "array", "description": "The arguments required when starting in the container.", @@ -1464,6 +1578,83 @@ "type": "boolean", "description": "Controls whether on Linux the container's user should be updated with the local user's UID and GID. On by default when opening from a local folder." }, + "containerEnv": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Container environment variables." + }, + "containerUser": { + "type": "string", + "description": "The user the container will be started with. The default is the user on the Docker image." + }, + "mounts": { + "type": "array", + "description": "Mount points to set up when creating the container. See Docker's documentation for the --mount option for the supported syntax.", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "bind", + "volume" + ], + "description": "Mount type." + }, + "source": { + "type": "string", + "description": "Mount source." + }, + "target": { + "type": "string", + "description": "Mount target." + } + }, + "required": [ + "type", + "source", + "target" + ], + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + }, + "init": { + "type": "boolean", + "description": "Passes the --init flag when creating the dev container." + }, + "privileged": { + "type": "boolean", + "description": "Passes the --privileged flag when creating the dev container." + }, + "capAdd": { + "type": "array", + "description": "Passes docker capabilities to include when creating the dev container.", + "examples": [ + "SYS_PTRACE" + ], + "items": { + "type": "string" + } + }, + "securityOpt": { + "type": "array", + "description": "Passes docker security options to include when creating the dev container.", + "examples": [ + "seccomp=unconfined" + ], + "items": { + "type": "string" + } + }, "remoteEnv": { "type": "object", "additionalProperties": { @@ -1786,6 +1977,13 @@ } }, "additionalProperties": false + }, + "openFiles": { + "type": "array", + "description": "The paths to the files to open when the codespace is created. Paths are relative to the workspace.", + "items": { + "type": "string" + } } }, "additionalProperties": false @@ -2014,6 +2212,83 @@ "type": "boolean", "description": "Controls whether on Linux the container's user should be updated with the local user's UID and GID. On by default when opening from a local folder." }, + "containerEnv": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Container environment variables." + }, + "containerUser": { + "type": "string", + "description": "The user the container will be started with. The default is the user on the Docker image." + }, + "mounts": { + "type": "array", + "description": "Mount points to set up when creating the container. See Docker's documentation for the --mount option for the supported syntax.", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "bind", + "volume" + ], + "description": "Mount type." + }, + "source": { + "type": "string", + "description": "Mount source." + }, + "target": { + "type": "string", + "description": "Mount target." + } + }, + "required": [ + "type", + "source", + "target" + ], + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + }, + "init": { + "type": "boolean", + "description": "Passes the --init flag when creating the dev container." + }, + "privileged": { + "type": "boolean", + "description": "Passes the --privileged flag when creating the dev container." + }, + "capAdd": { + "type": "array", + "description": "Passes docker capabilities to include when creating the dev container.", + "examples": [ + "SYS_PTRACE" + ], + "items": { + "type": "string" + } + }, + "securityOpt": { + "type": "array", + "description": "Passes docker security options to include when creating the dev container.", + "examples": [ + "seccomp=unconfined" + ], + "items": { + "type": "string" + } + }, "remoteEnv": { "type": "object", "additionalProperties": { @@ -2336,6 +2611,13 @@ } }, "additionalProperties": false + }, + "openFiles": { + "type": "array", + "description": "The paths to the files to open when the codespace is created. Paths are relative to the workspace.", + "items": { + "type": "string" + } } }, "additionalProperties": false @@ -2529,6 +2811,83 @@ "type": "boolean", "description": "Controls whether on Linux the container's user should be updated with the local user's UID and GID. On by default when opening from a local folder." }, + "containerEnv": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Container environment variables." + }, + "containerUser": { + "type": "string", + "description": "The user the container will be started with. The default is the user on the Docker image." + }, + "mounts": { + "type": "array", + "description": "Mount points to set up when creating the container. See Docker's documentation for the --mount option for the supported syntax.", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "bind", + "volume" + ], + "description": "Mount type." + }, + "source": { + "type": "string", + "description": "Mount source." + }, + "target": { + "type": "string", + "description": "Mount target." + } + }, + "required": [ + "type", + "source", + "target" + ], + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + }, + "init": { + "type": "boolean", + "description": "Passes the --init flag when creating the dev container." + }, + "privileged": { + "type": "boolean", + "description": "Passes the --privileged flag when creating the dev container." + }, + "capAdd": { + "type": "array", + "description": "Passes docker capabilities to include when creating the dev container.", + "examples": [ + "SYS_PTRACE" + ], + "items": { + "type": "string" + } + }, + "securityOpt": { + "type": "array", + "description": "Passes docker security options to include when creating the dev container.", + "examples": [ + "seccomp=unconfined" + ], + "items": { + "type": "string" + } + }, "remoteEnv": { "type": "object", "additionalProperties": { @@ -2851,6 +3210,13 @@ } }, "additionalProperties": false + }, + "openFiles": { + "type": "array", + "description": "The paths to the files to open when the codespace is created. Paths are relative to the workspace.", + "items": { + "type": "string" + } } }, "additionalProperties": false diff --git a/extensions/configuration-editing/schemas/devContainer.schema.src.json b/extensions/configuration-editing/schemas/devContainer.schema.src.json index 73456101bb9..f908f29ac59 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.src.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.src.json @@ -177,6 +177,55 @@ "type": "boolean", "description": "Controls whether on Linux the container's user should be updated with the local user's UID and GID. On by default when opening from a local folder." }, + "containerEnv": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Container environment variables." + }, + "containerUser": { + "type": "string", + "description": "The user the container will be started with. The default is the user on the Docker image." + }, + "mounts": { + "type": "array", + "description": "Mount points to set up when creating the container. See Docker's documentation for the --mount option for the supported syntax.", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/Mount" + }, + { + "type": "string" + } + ] + } + }, + "init": { + "type": "boolean", + "description": "Passes the --init flag when creating the dev container." + }, + "privileged": { + "type": "boolean", + "description": "Passes the --privileged flag when creating the dev container." + }, + "capAdd": { + "type": "array", + "description": "Passes docker capabilities to include when creating the dev container.", + "examples": [ "SYS_PTRACE" ], + "items": { + "type": "string" + } + }, + "securityOpt": { + "type": "array", + "description": "Passes docker security options to include when creating the dev container.", + "examples": [ "seccomp=unconfined" ], + "items": { + "type": "string" + } + }, "remoteEnv": { "type": "object", "additionalProperties": { @@ -501,6 +550,13 @@ ] } } + }, + "openFiles": { + "type": "array", + "description": "The paths to the files to open when the codespace is created. Paths are relative to the workspace.", + "items": { + "type": "string" + } } } } @@ -529,24 +585,6 @@ ] } }, - "containerEnv": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "Container environment variables." - }, - "containerUser": { - "type": "string", - "description": "The user the container will be started with. The default is the user on the Docker image." - }, - "mounts": { - "type": "array", - "description": "Mount points to set up when creating the container. See Docker's documentation for the --mount option for the supported syntax.", - "items": { - "type": "string" - } - }, "runArgs": { "type": "array", "description": "The arguments required when starting in the container.", @@ -728,6 +766,33 @@ "service", "workspaceFolder" ] + }, + "Mount": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "bind", + "volume" + ], + "description": "Mount type." + }, + "source": { + "type": "string", + "description": "Mount source." + }, + "target": { + "type": "string", + "description": "Mount target." + } + }, + "required": [ + "type", + "source", + "target" + ], + "additionalProperties": false } }, "oneOf": [ diff --git a/extensions/configuration-editing/yarn.lock b/extensions/configuration-editing/yarn.lock index e8272098f1e..5bb8132d373 100644 --- a/extensions/configuration-editing/yarn.lock +++ b/extensions/configuration-editing/yarn.lock @@ -12,7 +12,7 @@ jsonc-parser@^2.2.1: resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.1.tgz#db73cd59d78cce28723199466b2a03d1be1df2bc" integrity sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w== -vscode-nls@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.1.0.tgz#443b301a7465d88c81c0f4e1914f9857f0dce1e4" - integrity sha512-37Ha44QrLFwR2IfSSYdOArzUvOyoWbOYTwQC+wS0NfqKjhW7s0WQ1lMy5oJXgSZy9sAiZS5ifELhbpXodeMR8w== +vscode-nls@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.2.0.tgz#3cb6893dd9bd695244d8a024bdf746eea665cc3f" + integrity sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng== diff --git a/extensions/cpp/cgmanifest.json b/extensions/cpp/cgmanifest.json index 79c46dad831..92d0016b0e6 100644 --- a/extensions/cpp/cgmanifest.json +++ b/extensions/cpp/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "jeff-hykin/better-cpp-syntax", "repositoryUrl": "https://github.com/jeff-hykin/better-cpp-syntax", - "commitHash": "924295fc44bde1a00fab60da3a2caca4509adb25" + "commitHash": "32be139c7d3cdf07195af4b3a5c639ebf4e3b356" } }, "license": "MIT", - "version": "1.15.18", + "version": "1.15.23", "description": "The original JSON grammars were derived from https://github.com/atom/language-c which was originally converted from the C TextMate bundle https://github.com/textmate/c.tmbundle." }, { diff --git a/extensions/cpp/language-configuration.json b/extensions/cpp/language-configuration.json index 9a88814ff35..0f365702269 100644 --- a/extensions/cpp/language-configuration.json +++ b/extensions/cpp/language-configuration.json @@ -13,7 +13,8 @@ { "open": "{", "close": "}" }, { "open": "(", "close": ")" }, { "open": "'", "close": "'", "notIn": ["string", "comment"] }, - { "open": "\"", "close": "\"", "notIn": ["string"] } + { "open": "\"", "close": "\"", "notIn": ["string"] }, + { "open": "/*", "close": "*/", "notIn": ["string", "comment"] } ], "surroundingPairs": [ ["{", "}"], diff --git a/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json b/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json index b8a6aa53111..f109e7ea778 100644 --- a/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json +++ b/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/jeff-hykin/better-cpp-syntax/commit/924295fc44bde1a00fab60da3a2caca4509adb25", + "version": "https://github.com/jeff-hykin/better-cpp-syntax/commit/32be139c7d3cdf07195af4b3a5c639ebf4e3b356", "name": "C++", "scopeName": "source.cpp.embedded.macro", "patterns": [ @@ -519,7 +519,7 @@ "name": "comment.block.cpp" }, "builtin_storage_type_initilizer": { - "begin": "(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", + "begin": "\\s*+((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\{)", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\{)", "end": "\\}|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))~\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))~\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?(::))?(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?(::))?(?:(?:\\s)+)?((?|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))|(?=(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?(\\()", + "begin": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?(\\()", "end": "\\)|(?=(?))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\()", + "begin": "(?:(?:^|\\G|(?<=;|\\}))|(?<=>))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\()", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)?(?![\\w<:.]))", + "match": "(?<=^|\\))(?:(?:\\s)+)?(->)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)?(?![\\w<:.]))", "captures": { "1": { "name": "punctuation.definition.function.return-type.cpp" @@ -4240,7 +4240,7 @@ ] }, "function_pointer": { - "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()|(?=(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))", + "match": "(?<=protected|virtual|private|public|,|:)(?:(?:\\s)+)?(?!(?:(?:(?:protected)|(?:private)|(?:public))|virtual))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))", "captures": { "1": { "name": "meta.qualified_type.cpp", @@ -5334,15 +5334,23 @@ "name": "storage.modifier.lambda.$0.cpp" }, { - "match": "(->)((?:.+?(?=\\{|$))?)", - "captures": { - "1": { + "begin": "->", + "end": "(?=\\{)|(?=(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<4>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<4>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(operator)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(?:(?:((?:(?:delete\\[\\])|(?:delete)|(?:new\\[\\])|(?:new)|(?:\\->\\*)|(?:<<=)|(?:>>=)|(?:<=>)|(?:\\+\\+)|(?:\\-\\-)|(?:\\(\\))|(?:\\[\\])|(?:\\->)|(?:\\+\\+)|(?:\\-\\-)|(?:<<)|(?:>>)|(?:<=)|(?:>=)|(?:==)|(?:!=)|(?:&&)|(?:\\|\\|)|(?:\\+=)|(?:\\-=)|(?:\\*=)|(?:\\/=)|(?:%=)|(?:&=)|(?:\\^=)|(?:\\|=)|(?:\\+)|(?:\\-)|!|~|(?:\\*)|&|(?:\\*)|(?:\\/)|%|(?:\\+)|(?:\\-)|<|>|&|(?:\\^)|(?:\\|)|=|,))|((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:\\[\\])?)))|(\"\")((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\<|\\()", + "begin": "(?:(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(operator)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(?:(?:((?:(?:delete\\[\\])|(?:delete)|(?:new\\[\\])|(?:<=>)|(?:<<=)|(?:new)|(?:>>=)|(?:\\->\\*)|(?:\\/=)|(?:%=)|(?:&=)|(?:>=)|(?:\\|=)|(?:\\+\\+)|(?:\\-\\-)|(?:\\(\\))|(?:\\[\\])|(?:\\->)|(?:\\+\\+)|(?:<<)|(?:>>)|(?:\\-\\-)|(?:<=)|(?:\\^=)|(?:==)|(?:!=)|(?:&&)|(?:\\|\\|)|(?:\\+=)|(?:\\-=)|(?:\\*=)|,|(?:\\+)|(?:\\-)|!|~|(?:\\*)|&|(?:\\*)|(?:\\/)|%|(?:\\+)|(?:\\-)|<|>|&|(?:\\^)|(?:\\|)|=))|((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:\\[\\])?)))|(\"\")((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\<|\\()", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(default)|(delete))", + "captures": { + "1": { + "name": "keyword.operator.assignment.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "keyword.other.default.function.cpp" + }, + "7": { + "name": "keyword.other.delete.function.cpp" + } + } + }, { "include": "$self" } @@ -6971,7 +7018,7 @@ "include": "source.cpp#vararg_ellipses" }, { - "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", + "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -7351,7 +7398,7 @@ "include": "source.cpp#vararg_ellipses" }, { - "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", + "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -8827,7 +8874,7 @@ "name": "storage.type.template.cpp" }, "2": { - "name": "punctuation.section.angle-brackets.start.template.definition.cpp" + "name": "punctuation.section.angle-brackets.begin.template.definition.cpp" } }, "endCaptures": { @@ -8847,7 +8894,7 @@ }, "endCaptures": { "0": { - "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + "name": "punctuation.section.angle-brackets.end.template.call.cpp" } }, "patterns": [ @@ -9428,7 +9475,7 @@ "endCaptures": {}, "patterns": [ { - "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()|(?=(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<6>?)+>)(?:\\s)*+)?::)*\\s*+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<6>?)+>)(?:\\s)*+)?::)*\\s*+)?((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", + "begin": "\\s*+((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { "0": { @@ -2025,10 +2025,10 @@ ] }, "6": { - "name": "keyword.other.default.constructor.cpp" + "name": "keyword.other.default.function.cpp keyword.other.default.constructor.cpp" }, "7": { - "name": "keyword.other.delete.constructor.cpp" + "name": "keyword.other.delete.function.cpp keyword.other.delete.constructor.cpp" } } }, @@ -2176,7 +2176,7 @@ ] }, "control_flow_keywords": { - "match": "((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\{)", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\{)", "end": "\\}", "beginCaptures": { "1": { @@ -3296,10 +3296,10 @@ ] }, "6": { - "name": "keyword.other.default.constructor.cpp" + "name": "keyword.other.default.function.cpp keyword.other.default.constructor.cpp keyword.other.default.destructor.cpp" }, "7": { - "name": "keyword.other.delete.constructor.cpp" + "name": "keyword.other.delete.function.cpp keyword.other.delete.constructor.cpp keyword.other.delete.destructor.cpp" } } }, @@ -3363,7 +3363,7 @@ ] }, "destructor_root": { - "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))~\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))~\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { "0": { @@ -3587,10 +3587,10 @@ ] }, "6": { - "name": "keyword.other.default.constructor.cpp" + "name": "keyword.other.default.function.cpp keyword.other.default.constructor.cpp keyword.other.default.destructor.cpp" }, "7": { - "name": "keyword.other.delete.constructor.cpp" + "name": "keyword.other.delete.function.cpp keyword.other.delete.constructor.cpp keyword.other.delete.destructor.cpp" } } }, @@ -3784,7 +3784,7 @@ "match": "(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?(::))?(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?(::))?(?:(?:\\s)+)?((?|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))", "beginCaptures": { "0": { @@ -4519,7 +4519,7 @@ ] }, "function_call": { - "begin": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?(\\()", + "begin": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?(\\()", "end": "\\)", "beginCaptures": { "1": { @@ -4593,7 +4593,7 @@ ] }, "function_definition": { - "begin": "(?:(?:^|\\G|(?<=;|\\}))|(?<=>))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\()", + "begin": "(?:(?:^|\\G|(?<=;|\\}))|(?<=>))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\()", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { "0": { @@ -5156,7 +5156,7 @@ ] }, { - "match": "(?<=^|\\))(?:(?:\\s)+)?(->)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)?(?![\\w<:.]))", + "match": "(?<=^|\\))(?:(?:\\s)+)?(->)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)?(?![\\w<:.]))", "captures": { "1": { "name": "punctuation.definition.function.return-type.cpp" @@ -5404,7 +5404,7 @@ ] }, "function_pointer": { - "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()", "beginCaptures": { "1": { @@ -5755,7 +5755,7 @@ ] }, "function_pointer_parameter": { - "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()", "beginCaptures": { "1": { @@ -6510,7 +6510,7 @@ "name": "storage.type.modifier.virtual.cpp" }, { - "match": "(?<=protected|virtual|private|public|,|:)(?:(?:\\s)+)?(?!(?:(?:(?:protected)|(?:private)|(?:public))|virtual))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))", + "match": "(?<=protected|virtual|private|public|,|:)(?:(?:\\s)+)?(?!(?:(?:(?:protected)|(?:private)|(?:public))|virtual))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))", "captures": { "1": { "name": "meta.qualified_type.cpp", @@ -6708,7 +6708,7 @@ ] }, "inline_builtin_storage_type": { - "match": "(?:\\s)*+(?)((?:.+?(?=\\{|$))?)", - "captures": { - "1": { + "begin": "->", + "end": "(?=\\{)", + "beginCaptures": { + "0": { "name": "punctuation.definition.lambda.return-type.cpp" + } + }, + "endCaptures": {}, + "patterns": [ + { + "include": "#comments" }, - "2": { + { + "match": "\\S+", "name": "storage.type.return-type.lambda.cpp" } - } + ] }, { "begin": "\\{", @@ -7148,7 +7156,7 @@ "name": "entity.name.function.preprocessor.cpp" }, "member_access": { - "match": "(?:((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:(?:\\s)+)?(?:(?:\\.\\*|\\.)|(?:->\\*|->))(?:(?:\\s)+)?)*)(?:(?:\\s)+)?(\\b(?!uint_least16_t[^\\w]|uint_least32_t[^\\w]|uint_least64_t[^\\w]|int_least16_t[^\\w]|int_least32_t[^\\w]|int_least64_t[^\\w]|uint_least8_t[^\\w]|uint_fast16_t[^\\w]|uint_fast32_t[^\\w]|uint_fast64_t[^\\w]|int_least8_t[^\\w]|int_fast16_t[^\\w]|int_fast32_t[^\\w]|int_fast64_t[^\\w]|uint_fast8_t[^\\w]|suseconds_t[^\\w]|int_fast8_t[^\\w]|useconds_t[^\\w]|blksize_t[^\\w]|in_addr_t[^\\w]|in_port_t[^\\w]|uintptr_t[^\\w]|uintmax_t[^\\w]|uintmax_t[^\\w]|uintmax_t[^\\w]|unsigned[^\\w]|u_quad_t[^\\w]|blkcnt_t[^\\w]|uint16_t[^\\w]|uint32_t[^\\w]|uint64_t[^\\w]|intptr_t[^\\w]|intmax_t[^\\w]|intmax_t[^\\w]|wchar_t[^\\w]|u_short[^\\w]|qaddr_t[^\\w]|caddr_t[^\\w]|daddr_t[^\\w]|fixpt_t[^\\w]|nlink_t[^\\w]|segsz_t[^\\w]|swblk_t[^\\w]|clock_t[^\\w]|ssize_t[^\\w]|int16_t[^\\w]|int32_t[^\\w]|int64_t[^\\w]|uint8_t[^\\w]|signed[^\\w]|double[^\\w]|u_char[^\\w]|u_long[^\\w]|ushort[^\\w]|quad_t[^\\w]|mode_t[^\\w]|size_t[^\\w]|time_t[^\\w]|int8_t[^\\w]|short[^\\w]|float[^\\w]|u_int[^\\w]|div_t[^\\w]|dev_t[^\\w]|gid_t[^\\w]|ino_t[^\\w]|key_t[^\\w]|pid_t[^\\w]|off_t[^\\w]|uid_t[^\\w]|auto[^\\w]|void[^\\w]|char[^\\w]|long[^\\w]|bool[^\\w]|uint[^\\w]|id_t[^\\w]|id_t[^\\w]|int[^\\w])(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b(?!\\())", + "match": "(?:((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:(?:\\s)+)?(?:(?:\\.\\*|\\.)|(?:->\\*|->))(?:(?:\\s)+)?)*)(?:(?:\\s)+)?(\\b(?!uint_least32_t[^\\w]|uint_least16_t[^\\w]|uint_least64_t[^\\w]|int_least32_t[^\\w]|int_least64_t[^\\w]|uint_fast32_t[^\\w]|uint_fast64_t[^\\w]|uint_least8_t[^\\w]|uint_fast16_t[^\\w]|int_least16_t[^\\w]|int_fast16_t[^\\w]|int_least8_t[^\\w]|uint_fast8_t[^\\w]|int_fast64_t[^\\w]|int_fast32_t[^\\w]|int_fast8_t[^\\w]|suseconds_t[^\\w]|useconds_t[^\\w]|in_addr_t[^\\w]|uintmax_t[^\\w]|uintmax_t[^\\w]|uintmax_t[^\\w]|in_port_t[^\\w]|uintptr_t[^\\w]|blksize_t[^\\w]|uint32_t[^\\w]|uint64_t[^\\w]|u_quad_t[^\\w]|intmax_t[^\\w]|intmax_t[^\\w]|unsigned[^\\w]|blkcnt_t[^\\w]|uint16_t[^\\w]|intptr_t[^\\w]|swblk_t[^\\w]|wchar_t[^\\w]|u_short[^\\w]|qaddr_t[^\\w]|caddr_t[^\\w]|daddr_t[^\\w]|fixpt_t[^\\w]|nlink_t[^\\w]|segsz_t[^\\w]|clock_t[^\\w]|ssize_t[^\\w]|int16_t[^\\w]|int32_t[^\\w]|int64_t[^\\w]|uint8_t[^\\w]|int8_t[^\\w]|mode_t[^\\w]|quad_t[^\\w]|ushort[^\\w]|u_long[^\\w]|u_char[^\\w]|double[^\\w]|signed[^\\w]|time_t[^\\w]|size_t[^\\w]|key_t[^\\w]|div_t[^\\w]|ino_t[^\\w]|uid_t[^\\w]|gid_t[^\\w]|off_t[^\\w]|pid_t[^\\w]|float[^\\w]|dev_t[^\\w]|u_int[^\\w]|short[^\\w]|bool[^\\w]|id_t[^\\w]|uint[^\\w]|long[^\\w]|char[^\\w]|void[^\\w]|auto[^\\w]|id_t[^\\w]|int[^\\w])(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b(?!\\())", "captures": { "1": { "patterns": [ @@ -7609,7 +7617,7 @@ ] }, "namespace_alias": { - "match": "(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<8>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<8>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<4>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<4>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(operator)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(?:(?:((?:(?:delete\\[\\])|(?:delete)|(?:new\\[\\])|(?:new)|(?:\\->\\*)|(?:<<=)|(?:>>=)|(?:<=>)|(?:\\+\\+)|(?:\\-\\-)|(?:\\(\\))|(?:\\[\\])|(?:\\->)|(?:\\+\\+)|(?:\\-\\-)|(?:<<)|(?:>>)|(?:<=)|(?:>=)|(?:==)|(?:!=)|(?:&&)|(?:\\|\\|)|(?:\\+=)|(?:\\-=)|(?:\\*=)|(?:\\/=)|(?:%=)|(?:&=)|(?:\\^=)|(?:\\|=)|(?:\\+)|(?:\\-)|!|~|(?:\\*)|&|(?:\\*)|(?:\\/)|%|(?:\\+)|(?:\\-)|<|>|&|(?:\\^)|(?:\\|)|=|,))|((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:\\[\\])?)))|(\"\")((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\<|\\()", + "begin": "(?:(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(operator)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(?:(?:((?:(?:delete\\[\\])|(?:delete)|(?:new\\[\\])|(?:<=>)|(?:<<=)|(?:new)|(?:>>=)|(?:\\->\\*)|(?:\\/=)|(?:%=)|(?:&=)|(?:>=)|(?:\\|=)|(?:\\+\\+)|(?:\\-\\-)|(?:\\(\\))|(?:\\[\\])|(?:\\->)|(?:\\+\\+)|(?:<<)|(?:>>)|(?:\\-\\-)|(?:<=)|(?:\\^=)|(?:==)|(?:!=)|(?:&&)|(?:\\|\\|)|(?:\\+=)|(?:\\-=)|(?:\\*=)|,|(?:\\+)|(?:\\-)|!|~|(?:\\*)|&|(?:\\*)|(?:\\/)|%|(?:\\+)|(?:\\-)|<|>|&|(?:\\^)|(?:\\|)|=))|((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:\\[\\])?)))|(\"\")((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\<|\\()", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { "0": { @@ -8736,6 +8744,45 @@ { "include": "#qualifiers_and_specifiers_post_parameters" }, + { + "match": "(\\=)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(default)|(delete))", + "captures": { + "1": { + "name": "keyword.operator.assignment.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "keyword.other.default.function.cpp" + }, + "7": { + "name": "keyword.other.delete.function.cpp" + } + } + }, { "include": "$self" } @@ -10475,7 +10522,7 @@ "include": "#vararg_ellipses" }, { - "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", + "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -11513,7 +11560,7 @@ "include": "#vararg_ellipses" }, { - "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", + "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -12848,7 +12895,7 @@ ] }, "qualified_type": { - "match": "\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)?(?![\\w<:.])", + "match": "\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)?(?![\\w<:.])", "captures": { "0": { "patterns": [ @@ -13042,27 +13089,27 @@ "name": "meta.qualified_type.cpp" }, "qualifiers_and_specifiers_post_parameters": { - "match": "((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "match": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", - "captures": { - "1": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "2": { - "name": "comment.block.cpp" - }, - "3": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] + "5": { + "name": "storage.modifier.specifier.functional.post-parameters.$5.cpp" } } } @@ -13118,7 +13131,7 @@ } }, "scope_resolution": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13140,7 +13153,7 @@ } }, "scope_resolution_function_call": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13162,7 +13175,7 @@ } }, "scope_resolution_function_call_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13200,7 +13213,7 @@ } }, "scope_resolution_function_definition": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13222,7 +13235,7 @@ } }, "scope_resolution_function_definition_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13260,7 +13273,7 @@ } }, "scope_resolution_function_definition_operator_overload": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13282,7 +13295,7 @@ } }, "scope_resolution_function_definition_operator_overload_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13320,7 +13333,7 @@ } }, "scope_resolution_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13358,7 +13371,7 @@ } }, "scope_resolution_namespace_alias": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13380,7 +13393,7 @@ } }, "scope_resolution_namespace_alias_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13418,7 +13431,7 @@ } }, "scope_resolution_namespace_block": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13440,7 +13453,7 @@ } }, "scope_resolution_namespace_block_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13478,7 +13491,7 @@ } }, "scope_resolution_namespace_using": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13500,7 +13513,7 @@ } }, "scope_resolution_namespace_using_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13538,7 +13551,7 @@ } }, "scope_resolution_parameter": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13560,7 +13573,7 @@ } }, "scope_resolution_parameter_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13598,7 +13611,7 @@ } }, "scope_resolution_template_call": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13620,7 +13633,7 @@ } }, "scope_resolution_template_call_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13658,7 +13671,7 @@ } }, "scope_resolution_template_definition": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13680,7 +13693,7 @@ } }, "scope_resolution_template_definition_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13722,7 +13735,7 @@ "name": "punctuation.terminator.statement.cpp" }, "simple_type": { - "match": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?", + "match": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?", "captures": { "1": { "name": "meta.qualified_type.cpp", @@ -16160,10 +16173,10 @@ ] }, "template_argument_defaulted": { - "match": "(?<=<|,)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\s)+)*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)(?:(?:\\s)+)?([=])", + "match": "(?<=<|,)(?:(?:\\s)+)?((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)(?:\\s)+((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(\\=)", "captures": { "1": { - "name": "storage.type.template.cpp" + "name": "storage.type.template.argument.$1.cpp" }, "2": { "name": "entity.name.type.template.cpp" @@ -16248,7 +16261,7 @@ "name": "storage.type.template.cpp" }, "2": { - "name": "punctuation.section.angle-brackets.start.template.definition.cpp" + "name": "punctuation.section.angle-brackets.begin.template.definition.cpp" } }, "endCaptures": { @@ -16268,7 +16281,7 @@ }, "endCaptures": { "0": { - "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + "name": "punctuation.section.angle-brackets.end.template.call.cpp" } }, "patterns": [ @@ -16283,7 +16296,7 @@ ] }, "template_definition_argument": { - "match": "((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)|((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\s)+)+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))|((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)(?:(?:\\s)+)?(\\.\\.\\.)(?:(?:\\s)+)?((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))(?:(?:\\s)+)?(?:(,)|(?=>|$))", + "match": "((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)|((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\s)+)+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))|((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)(?:(?:\\s)+)?(\\.\\.\\.)(?:(?:\\s)+)?((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))|(?)(?:(?:\\s)+)?(class|typename)(?:(?:\\s)+((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))?)(?:(?:\\s)+)?(?:(\\=)(?:(?:\\s)+)?(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?(?:(,)|(?=>|$))", "captures": { "1": { "patterns": [ @@ -16334,7 +16347,7 @@ "name": "entity.name.type.template.cpp" }, "6": { - "name": "storage.type.template.cpp" + "name": "storage.type.template.argument.$6.cpp" }, "7": { "name": "punctuation.vararg-ellipses.template.definition.cpp" @@ -16343,6 +16356,30 @@ "name": "entity.name.type.template.cpp" }, "9": { + "name": "storage.type.template.cpp" + }, + "10": { + "name": "punctuation.section.angle-brackets.begin.template.definition.cpp" + }, + "11": { + "name": "storage.type.template.argument.$11.cpp" + }, + "12": { + "name": "entity.name.type.template.cpp" + }, + "13": { + "name": "punctuation.section.angle-brackets.end.template.definition.cpp" + }, + "14": { + "name": "storage.type.template.argument.$14.cpp" + }, + "15": { + "name": "entity.name.type.template.cpp" + }, + "16": { + "name": "keyword.operator.assignment.cpp" + }, + "17": { "name": "punctuation.separator.delimiter.comma.template.argument.cpp" } } @@ -16367,13 +16404,13 @@ ] }, "template_isolated_definition": { - "match": "(?(?:(?:\\s)+)?$)", + "match": "(?)(?:(?:\\s)+)?$", "captures": { "1": { "name": "storage.type.template.cpp" }, "2": { - "name": "punctuation.section.angle-brackets.start.template.definition.cpp" + "name": "punctuation.section.angle-brackets.begin.template.definition.cpp" }, "3": { "name": "meta.template.definition.cpp", @@ -16526,7 +16563,7 @@ } }, "type_alias": { - "match": "(using)(?:(?:\\s)+)?(?!namespace)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)?(?![\\w<:.]))(?:(?:\\s)+)?(\\=)(?:(?:\\s)+)?((?:typename)?)(?:(?:\\s)+)?((?:(?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)?(?![\\w<:.]))|(.*(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)?(?:(?:\\s)+)?(?:(;)|\\n)", + "match": "(using)(?:(?:\\s)+)?(?!namespace)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)?(?![\\w<:.]))(?:(?:\\s)+)?(\\=)(?:(?:\\s)+)?((?:typename)?)(?:(?:\\s)+)?((?:(?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)?(?![\\w<:.]))|(.*(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)?(?:(?:\\s)+)?(?:(;)|\\n)", "captures": { "1": { "name": "keyword.other.using.directive.cpp" @@ -17616,7 +17653,7 @@ "endCaptures": {}, "patterns": [ { - "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()", "beginCaptures": { "1": { @@ -18901,7 +18938,7 @@ ] }, "typename": { - "match": "(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<17>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<17>?)+>)?(?![\\w<:.]))", + "match": "(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<17>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<17>?)+>)?(?![\\w<:.]))", "captures": { "1": { "name": "storage.modifier.cpp" @@ -19790,7 +19827,7 @@ } }, "using_namespace": { - "begin": "(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<6>?)+>)(?:\\s)*+)?::)*\\s*+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<6>?)+>)(?:\\s)*+)?::)*\\s*+)?((?('fetchBeforeCheckout', false) === true; + const pullBeforeCheckout = config.get('pullBeforeCheckout', false) === true; - if (fetchBeforeCheckout) { + if (pullBeforeCheckout) { await this.repository.fastForwardBranch(this.ref.name!); } @@ -599,7 +599,7 @@ export class CommandCenter { choices.push(addToWorkspace); } - const result = await window.showInformationMessage(message, ...choices); + const result = await window.showInformationMessage(message, { modal: true }, ...choices); action = result === open ? PostCloneAction.Open : result === openNewWindow ? PostCloneAction.OpenNewWindow @@ -1162,7 +1162,7 @@ export class CommandCenter { const repository = this.model.getRepository(uri); if (!repository) { - console.log(`FAILED to accept merge because uri ${uri.toString()} doesn't belong to any repository`); + console.log(`FAILED to complete merge because uri ${uri.toString()} doesn't belong to any repository`); return; } @@ -1183,7 +1183,7 @@ export class CommandCenter { // make sure to save the merged document const doc = workspace.textDocuments.find(doc => doc.uri.toString() === uri.toString()); if (!doc) { - console.log(`FAILED to accept merge because uri ${uri.toString()} doesn't match a document`); + console.log(`FAILED to complete merge because uri ${uri.toString()} doesn't match a document`); return; } if (doc.isDirty) { diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 205ba569cdd..eaf4dc74163 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -18,6 +18,8 @@ import { detectEncoding } from './encoding'; import { Ref, RefType, Branch, Remote, ForcePushMode, GitErrorCodes, LogOptions, Change, Status, CommitOptions, BranchQuery } from './api/git'; import * as byline from 'byline'; import { StringDecoder } from 'string_decoder'; +import { OutputChannelLogger } from './log'; +import TelemetryReporter from '@vscode/extension-telemetry'; // https://github.com/microsoft/vscode/issues/65693 const MAX_CLI_LENGTH = 30000; @@ -373,11 +375,14 @@ export class Git { private _onOutput = new EventEmitter(); get onOutput(): EventEmitter { return this._onOutput; } - constructor(options: IGitOptions) { + private readonly telemetryReporter: TelemetryReporter; + + constructor(options: IGitOptions, telemetryReporter: TelemetryReporter) { this.path = options.gitPath; this.version = options.version; this.userAgent = options.userAgent; this.env = options.env || {}; + this.telemetryReporter = telemetryReporter; const onConfigurationChanged = (e?: ConfigurationChangeEvent) => { if (e !== undefined && !e.affectsConfiguration('git.commandsToLog')) { @@ -396,8 +401,8 @@ export class Git { return Versions.compare(Versions.fromString(this.version), Versions.fromString(version)); } - open(repository: string, dotGit: { path: string; commonPath?: string }): Repository { - return new Repository(this, repository, dotGit); + open(repository: string, dotGit: { path: string; commonPath?: string }, outputChannelLogger: OutputChannelLogger): Repository { + return new Repository(this, repository, dotGit, outputChannelLogger); } async init(repository: string): Promise { @@ -555,7 +560,9 @@ export class Git { } private async _exec(args: string[], options: SpawnOptions = {}): Promise> { + const startSpawn = Date.now(); const child = this.spawn(args, options); + const durSpawn = Date.now() - startSpawn; options.onSpawn?.(child); @@ -563,12 +570,13 @@ export class Git { child.stdin!.end(options.input, 'utf8'); } - const startTime = Date.now(); + const startExec = Date.now(); const bufferResult = await exec(child, options.cancellationToken); + const durExec = Date.now() - startExec; if (options.log !== false) { // command - this.log(`> git ${args.join(' ')} [${Date.now() - startTime}ms]\n`); + this.log(`> git ${args.join(' ')} [${durExec}ms]\n`); // stdout if (bufferResult.stdout.length > 0 && args.find(a => this.commandsToLog.includes(a))) { @@ -581,6 +589,16 @@ export class Git { } } + /* __GDPR__ + "git.execDuration" : { + "owner": "lszomoru", + "comment": "Time it takes to spawn and execute a git command", + "durSpawn": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth","isMeasurement": true, "comment": "Time it took to run spawn git" }, + "durExec": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth","isMeasurement": true, "comment": "Time git took" } + } + */ + this.telemetryReporter.sendTelemetryEvent('git.execDuration', undefined, { durSpawn, durExec }); + let encoding = options.encoding || 'utf8'; encoding = iconv.encodingExists(encoding) ? encoding : 'utf8'; @@ -896,7 +914,8 @@ export class Repository { constructor( private _git: Git, private repositoryRoot: string, - readonly dotGit: { path: string; commonPath?: string } + readonly dotGit: { path: string; commonPath?: string }, + private outputChannelLogger: OutputChannelLogger ) { } get git(): Git { @@ -1979,6 +1998,16 @@ export class Repository { async getHEAD(): Promise { try { + // Attempt to parse the HEAD file + const result = await this.getHEADFS(); + return result; + } + catch (err) { + this.outputChannelLogger.logWarning(err.message); + } + + try { + // Fallback to using git to determine HEAD const result = await this.exec(['symbolic-ref', '--short', 'HEAD']); if (!result.stdout) { @@ -1986,15 +2015,35 @@ export class Repository { } return { name: result.stdout.trim(), commit: undefined, type: RefType.Head }; - } catch (err) { - const result = await this.exec(['rev-parse', 'HEAD']); - - if (!result.stdout) { - throw new Error('Error parsing HEAD'); - } - - return { name: undefined, commit: result.stdout.trim(), type: RefType.Head }; } + catch (err) { } + + // Detached HEAD + const result = await this.exec(['rev-parse', 'HEAD']); + + if (!result.stdout) { + throw new Error('Error parsing HEAD'); + } + + return { name: undefined, commit: result.stdout.trim(), type: RefType.Head }; + } + + async getHEADFS(): Promise { + const raw = await fs.readFile(path.join(this.dotGit.commonPath ?? this.dotGit.path, 'HEAD'), 'utf8'); + + // Branch + const branchMatch = raw.match(/^ref: refs\/heads\/(?.*)$/m); + if (branchMatch?.groups?.name) { + return { name: branchMatch.groups.name, commit: undefined, type: RefType.Head }; + } + + // Detached + const commitMatch = raw.match(/^(?[0-9a-f]{40})$/m); + if (commitMatch?.groups?.commit) { + return { name: undefined, commit: commitMatch.groups.commit, type: RefType.Head }; + } + + throw new Error(`Unable to parse HEAD file. HEAD file contents: ${raw}.`); } async findTrackingBranches(upstreamBranch: string): Promise { diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 907c0147398..dd2e8904c3b 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -89,7 +89,7 @@ async function createModel(context: ExtensionContext, outputChannelLogger: Outpu userAgent: `git/${info.version} (${(os as any).version?.() ?? os.type()} ${os.release()}; ${os.platform()} ${os.arch()}) vscode/${vscodeVersion} (${env.appName})`, version: info.version, env: environment, - }); + }, telemetryReporter); const model = new Model(git, askpass, context.globalState, outputChannelLogger, telemetryReporter); disposables.push(model); diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index c10a9d6a193..d25289c5f3f 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -5,7 +5,7 @@ import { workspace, WorkspaceFoldersChangeEvent, Uri, window, Event, EventEmitter, QuickPickItem, Disposable, SourceControl, SourceControlResourceGroup, TextEditor, Memento, commands } from 'vscode'; import TelemetryReporter from '@vscode/extension-telemetry'; -import { Repository, RepositoryState } from './repository'; +import { Operation, Repository, RepositoryState } from './repository'; import { memoize, sequentialize, debounce } from './decorators'; import { dispose, anyEvent, filterEvent, isDescendant, pathEquals, toDisposable, eventToPromise } from './util'; import { Git } from './git'; @@ -387,7 +387,7 @@ export class Model implements IRemoteSourcePublisherRegistry, IPostCommitCommand } const dotGit = await this.git.getRepositoryDotGit(repositoryRoot); - const repository = new Repository(this.git.open(repositoryRoot, dotGit), this, this, this, this.globalState, this.outputChannelLogger, this.telemetryReporter); + const repository = new Repository(this.git.open(repositoryRoot, dotGit, this.outputChannelLogger), this, this, this, this.globalState, this.outputChannelLogger, this.telemetryReporter); this.open(repository); repository.status(); // do not await this, we want SCM to know about the repo asap @@ -450,14 +450,45 @@ export class Model implements IRemoteSourcePublisherRegistry, IPostCommitCommand .forEach(p => this.eventuallyScanPossibleGitRepository(p)); }; - const statusListener = repository.onDidRunGitStatus(checkForSubmodules); + const updateMergeChanges = () => { + // set mergeChanges context + const mergeChanges: Uri[] = []; + for (const { repository } of this.openRepositories.values()) { + for (const state of repository.mergeGroup.resourceStates) { + mergeChanges.push(state.resourceUri); + } + } + commands.executeCommand('setContext', 'git.mergeChanges', mergeChanges); + }; + + const statusListener = repository.onDidRunGitStatus(() => { + checkForSubmodules(); + updateMergeChanges(); + }); checkForSubmodules(); + const updateCommitInProgressContext = () => { + let commitInProgress = false; + for (const { repository } of this.openRepositories.values()) { + if (repository.operations.isRunning(Operation.Commit)) { + commitInProgress = true; + break; + } + } + + commands.executeCommand('setContext', 'commitInProgress', commitInProgress); + }; + + const operationEvent = anyEvent(repository.onDidRunOperation as Event, repository.onRunOperation as Event); + const operationListener = operationEvent(() => updateCommitInProgressContext()); + updateCommitInProgressContext(); + const dispose = () => { disappearListener.dispose(); changeListener.dispose(); originalResourceChangeListener.dispose(); statusListener.dispose(); + operationListener.dispose(); repository.dispose(); this.openRepositories = this.openRepositories.filter(e => e !== openRepository); @@ -466,6 +497,7 @@ export class Model implements IRemoteSourcePublisherRegistry, IPostCommitCommand const openRepository = { repository, dispose }; this.openRepositories.push(openRepository); + updateMergeChanges(); this._onDidOpenRepository.fire(repository); } diff --git a/extensions/git/src/protocolHandler.ts b/extensions/git/src/protocolHandler.ts index 3dccfa48665..9ed1f3639ec 100644 --- a/extensions/git/src/protocolHandler.ts +++ b/extensions/git/src/protocolHandler.ts @@ -12,6 +12,7 @@ import * as querystring from 'querystring'; import { OutputChannelLogger } from './log'; const schemes = new Set(['file', 'git', 'http', 'https', 'ssh']); +const refRegEx = /^$|[~\^:\\\*\s\[\]]|^-|^\.|\/\.|\.\.|\.lock\/|\.lock$|\/$|\.$/; export class GitProtocolHandler implements UriHandler { @@ -44,7 +45,7 @@ export class GitProtocolHandler implements UriHandler { } if (ref !== undefined && typeof ref !== 'string') { - this.outputChannelLogger.logWarning('Failed to open URI:' + uri.toString()); + this.outputChannelLogger.logWarning('Failed to open URI due to multiple references:' + uri.toString()); return; } @@ -62,6 +63,11 @@ export class GitProtocolHandler implements UriHandler { if (!schemes.has(cloneUri.scheme.toLowerCase())) { throw new Error('Unsupported scheme.'); } + + // Validate the reference + if (typeof ref === 'string' && refRegEx.test(ref)) { + throw new Error('Invalid reference.'); + } } catch (ex) { this.outputChannelLogger.logWarning('Invalid URI:' + uri.toString()); diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index d622fd23c87..5697163e20d 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -325,6 +325,7 @@ export const enum Operation { Reset = 'Reset', Remote = 'Remote', Fetch = 'Fetch', + FetchNoProgress = 'FetchNoProgress', Pull = 'Pull', Push = 'Push', CherryPick = 'CherryPick', @@ -377,7 +378,7 @@ function isReadOnly(operation: Operation): boolean { function shouldShowProgress(operation: Operation): boolean { switch (operation) { - case Operation.Fetch: + case Operation.FetchNoProgress: case Operation.CheckIgnore: case Operation.GetObjectDetails: case Operation.Show: @@ -460,10 +461,8 @@ class ProgressManager { this.updateEnablement(); this.repository.onDidChangeOperations(() => { - const commitInProgress = this.repository.operations.isRunning(Operation.Commit); - - this.repository.sourceControl.inputBox.enabled = !commitInProgress; - commands.executeCommand('setContext', 'commitInProgress', commitInProgress); + // Disable input box when the commit operation is running + this.repository.sourceControl.inputBox.enabled = !this.repository.operations.isRunning(Operation.Commit); }); } @@ -1471,8 +1470,8 @@ export class Repository implements Disposable { } @throttle - async fetchAll(cancellationToken?: CancellationToken): Promise { - await this._fetch({ all: true, cancellationToken }); + async fetchAll(options: { silent?: boolean } = {}, cancellationToken?: CancellationToken): Promise { + await this._fetch({ all: true, silent: options.silent, cancellationToken }); } async fetch(options: FetchOptions): Promise { @@ -1486,7 +1485,8 @@ export class Repository implements Disposable { options.prune = prune; } - await this.run(Operation.Fetch, async () => this.repository.fetch(options)); + const operation = options.silent === true ? Operation.FetchNoProgress : Operation.Fetch; + await this.run(operation, async () => this.repository.fetch(options)); } @throttle @@ -1590,7 +1590,7 @@ export class Repository implements Disposable { const fn = async (cancellationToken?: CancellationToken) => { // When fetchOnPull is enabled, fetch all branches when pulling if (fetchOnPull) { - await this.fetchAll(cancellationToken); + await this.fetchAll({}, cancellationToken); } if (await this.checkIfMaybeRebased(this.HEAD?.name)) { @@ -2037,7 +2037,7 @@ export class Repository implements Disposable { if (sort !== 'alphabetically' && sort !== 'committerdate') { sort = 'alphabetically'; } - const [refs, remotes, submodules, rebaseCommit, mergeInProgress] = await Promise.all([this.repository.getRefs({ sort }), this.repository.getRemotes(), this.repository.getSubmodules(), this.getRebaseCommit(), this.isMergeInProgress()]); + const [refs, remotes, submodules, rebaseCommit, mergeInProgress, commitTemplate] = await Promise.all([this.repository.getRefs({ sort }), this.repository.getRemotes(), this.repository.getSubmodules(), this.getRebaseCommit(), this.isMergeInProgress(), this.getInputTemplate()]); this._HEAD = HEAD; this._refs = refs!; @@ -2103,12 +2103,9 @@ export class Repository implements Disposable { // set count badge this.setCountBadge(); - // set mergeChanges context - commands.executeCommand('setContext', 'git.mergeChanges', merge.map(item => item.resourceUri)); - this._onDidChangeStatus.fire(); - this._sourceControl.commitTemplate = await this.getInputTemplate(); + this._sourceControl.commitTemplate = commitTemplate; } private setCountBadge(): void { diff --git a/extensions/git/yarn.lock b/extensions/git/yarn.lock index 502b35d9c38..b5a79f3f238 100644 --- a/extensions/git/yarn.lock +++ b/extensions/git/yarn.lock @@ -186,10 +186,10 @@ util-deprecate@^1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== -vscode-nls@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.1.0.tgz#443b301a7465d88c81c0f4e1914f9857f0dce1e4" - integrity sha512-37Ha44QrLFwR2IfSSYdOArzUvOyoWbOYTwQC+wS0NfqKjhW7s0WQ1lMy5oJXgSZy9sAiZS5ifELhbpXodeMR8w== +vscode-nls@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.2.0.tgz#3cb6893dd9bd695244d8a024bdf746eea665cc3f" + integrity sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng== vscode-uri@^2.0.0: version "2.0.0" diff --git a/extensions/github-authentication/package.json b/extensions/github-authentication/package.json index 35a95e4354d..dbabf7fcbbf 100644 --- a/extensions/github-authentication/package.json +++ b/extensions/github-authentication/package.json @@ -34,16 +34,16 @@ "id": "github" }, { - "label": "GitHub Enterprise", + "label": "GitHub Enterprise Server", "id": "github-enterprise" } ], "configuration": { - "title": "GitHub Enterprise Authentication Provider", + "title": "GitHub Enterprise Server Authentication Provider", "properties": { "github-enterprise.uri": { "type": "string", - "description": "URI of your GitHub Enterprise Instance" + "description": "GitHub Enterprise Server URI" } } } @@ -65,7 +65,6 @@ "node-fetch": "2.6.7", "uuid": "8.1.0", "@vscode/extension-telemetry": "0.6.2", - "vscode-nls": "^5.1.0", "vscode-tas-client": "^0.1.47" }, "devDependencies": { diff --git a/extensions/github-authentication/src/github.ts b/extensions/github-authentication/src/github.ts index f6f2301ab16..0c7273d8bf0 100644 --- a/extensions/github-authentication/src/github.ts +++ b/extensions/github-authentication/src/github.ts @@ -256,7 +256,7 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid */ this._telemetryReporter?.sendTelemetryEvent('loginFailed'); - vscode.window.showErrorMessage(`Sign in failed: ${e}`); + vscode.window.showErrorMessage(vscode.l10n.t('Sign in failed: {0}', `${e}`)); this._logger.error(e); throw e; } @@ -299,7 +299,7 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid */ this._telemetryReporter?.sendTelemetryEvent('logoutFailed'); - vscode.window.showErrorMessage(`Sign out failed: ${e}`); + vscode.window.showErrorMessage(vscode.l10n.t('Sign out failed: {0}', `${e}`)); this._logger.error(e); throw e; } diff --git a/extensions/github-authentication/src/githubServer.ts b/extensions/github-authentication/src/githubServer.ts index 94ce5429e78..42e43549a58 100644 --- a/extensions/github-authentication/src/githubServer.ts +++ b/extensions/github-authentication/src/githubServer.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vscode-nls'; import * as vscode from 'vscode'; import fetch, { Response } from 'node-fetch'; import { v4 as uuid } from 'uuid'; @@ -15,7 +14,6 @@ import { isSupportedEnvironment } from './common/env'; import { LoopbackAuthServer } from './authServer'; import path = require('path'); -const localize = nls.loadMessageBundle(); const CLIENT_ID = '01ab8ac9400c4e429b23'; const GITHUB_TOKEN_URL = 'https://vscode.dev/codeExchangeProxyEndpoints/github/login/oauth/access_token'; const NETWORK_ERROR = 'network error'; @@ -156,16 +154,16 @@ export class GitHubServer implements IGitHubServer { // Used for showing a friendlier message to the user when the explicitly cancel a flow. let userCancelled: boolean | undefined; - const yes = localize('yes', "Yes"); - const no = localize('no', "No"); + const yes = vscode.l10n.t('Yes'); + const no = vscode.l10n.t('No'); const promptToContinue = async () => { if (userCancelled === undefined) { // We haven't had a failure yet so wait to prompt return; } const message = userCancelled - ? localize('userCancelledMessage', "Having trouble logging in? Would you like to try a different way?") - : localize('otherReasonMessage', "You have not yet finished authorizing this extension to use GitHub. Would you like to keep trying?"); + ? vscode.l10n.t('Having trouble logging in? Would you like to try a different way?') + : vscode.l10n.t('otherReasonMessage', 'You have not yet finished authorizing this extension to use GitHub. Would you like to keep trying?'); const result = await vscode.window.showWarningMessage(message, yes, no); if (result !== yes) { throw new Error('Cancelled'); @@ -221,7 +219,11 @@ export class GitHubServer implements IGitHubServer { this._logger.info(`Trying without local server... (${scopes})`); return await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, - title: localize('signingIn', 'Signing in to {0}...', this.baseUri.authority), + title: vscode.l10n.t({ + message: 'Signing in to {0}...', + args: [this.baseUri.authority], + comment: ['The {0} will be a url, e.g. github.com'] + }), cancellable: true }, async (_, token) => { const existingNonces = this._pendingNonces.get(scopes) || []; @@ -266,7 +268,11 @@ export class GitHubServer implements IGitHubServer { this._logger.info(`Trying with local server... (${scopes})`); return await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, - title: localize('signingInAnotherWay', "Signing in to {0}...", this.baseUri.authority), + title: vscode.l10n.t({ + message: 'Signing in to {0}...', + args: [this.baseUri.authority], + comment: ['The {0} will be a url, e.g. github.com'] + }), cancellable: true }, async (_, token) => { const redirectUri = await this.getRedirectEndpoint(); @@ -323,15 +329,15 @@ export class GitHubServer implements IGitHubServer { const json = await result.json() as IGitHubDeviceCodeResponse; - + const button = vscode.l10n.t('Copy & Continue to GitHub'); const modalResult = await vscode.window.showInformationMessage( - localize('code.title', "Your Code: {0}", json.user_code), + vscode.l10n.t({ message: 'Your Code: {0}', args: [json.user_code], comment: ['The {0} will be a code, e.g. 123-456'] }), { modal: true, - detail: localize('code.detail', "To finish authenticating, navigate to GitHub and paste in the above one-time code.") - }, 'Copy & Continue to GitHub'); + detail: vscode.l10n.t('To finish authenticating, navigate to GitHub and paste in the above one-time code.') + }, button); - if (modalResult !== 'Copy & Continue to GitHub') { + if (modalResult !== button) { throw new Error('User Cancelled'); } @@ -372,11 +378,14 @@ export class GitHubServer implements IGitHubServer { return await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, cancellable: true, - title: localize( - 'progress', - "Open [{0}]({0}) in a new tab and paste your one-time code: {1}", - json.verification_uri, - json.user_code) + title: vscode.l10n.t({ + message: 'Open [{0}]({0}) in a new tab and paste your one-time code: {1}', + args: [json.verification_uri, json.user_code], + comment: [ + 'The [{0}]({0}) will be a url and the {1} will be a code, e.g. 123-456', + '{Locked="[{0}]({0})"}' + ] + }) }, async (_, token) => { const refreshTokenUri = this.baseUri.with({ path: '/login/oauth/access_token', diff --git a/extensions/github-authentication/yarn.lock b/extensions/github-authentication/yarn.lock index 6ebb79992dc..bfc332713a8 100644 --- a/extensions/github-authentication/yarn.lock +++ b/extensions/github-authentication/yarn.lock @@ -143,11 +143,6 @@ uuid@8.1.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.1.0.tgz#6f1536eb43249f473abc6bd58ff983da1ca30d8d" integrity sha512-CI18flHDznR0lq54xBycOVmphdCYnQLKn8abKn7PXUiKUGdEd+/l9LWNJmugXel4hXq7S+RMNl34ecyC9TntWg== -vscode-nls@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.1.0.tgz#443b301a7465d88c81c0f4e1914f9857f0dce1e4" - integrity sha512-37Ha44QrLFwR2IfSSYdOArzUvOyoWbOYTwQC+wS0NfqKjhW7s0WQ1lMy5oJXgSZy9sAiZS5ifELhbpXodeMR8w== - vscode-tas-client@^0.1.47: version "0.1.47" resolved "https://registry.yarnpkg.com/vscode-tas-client/-/vscode-tas-client-0.1.47.tgz#d66795cbbaa231aba659b6c40d43927d73596375" diff --git a/extensions/github/package.json b/extensions/github/package.json index 1b21d4b52a1..a262e352a11 100644 --- a/extensions/github/package.json +++ b/extensions/github/package.json @@ -135,7 +135,7 @@ "dependencies": { "@octokit/rest": "19.0.4", "tunnel": "^0.0.6", - "vscode-nls": "^5.1.0" + "vscode-nls": "^5.2.0" }, "devDependencies": { "@types/node": "16.x" diff --git a/extensions/github/yarn.lock b/extensions/github/yarn.lock index c199d2f7553..8db5da00683 100644 --- a/extensions/github/yarn.lock +++ b/extensions/github/yarn.lock @@ -152,10 +152,10 @@ universal-user-agent@^6.0.0: resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== -vscode-nls@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.1.0.tgz#443b301a7465d88c81c0f4e1914f9857f0dce1e4" - integrity sha512-37Ha44QrLFwR2IfSSYdOArzUvOyoWbOYTwQC+wS0NfqKjhW7s0WQ1lMy5oJXgSZy9sAiZS5ifELhbpXodeMR8w== +vscode-nls@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.2.0.tgz#3cb6893dd9bd695244d8a024bdf746eea665cc3f" + integrity sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng== webidl-conversions@^3.0.0: version "3.0.1" diff --git a/extensions/groovy/package.json b/extensions/groovy/package.json index a7000f89e20..1f50ff6caf7 100644 --- a/extensions/groovy/package.json +++ b/extensions/groovy/package.json @@ -30,7 +30,7 @@ "Jenkinsfile" ], "filenamePatterns": [ - "Jenkinsfile.*" + "Jenkinsfile*" ], "firstLine": "^#!.*\\bgroovy\\b", "configuration": "./language-configuration.json" diff --git a/extensions/grunt/package.json b/extensions/grunt/package.json index 245fd717f8c..0f5e605bcf9 100644 --- a/extensions/grunt/package.json +++ b/extensions/grunt/package.json @@ -17,7 +17,7 @@ "watch": "gulp watch-extension:grunt" }, "dependencies": { - "vscode-nls": "^5.1.0" + "vscode-nls": "^5.2.0" }, "devDependencies": { "@types/node": "16.x" diff --git a/extensions/grunt/yarn.lock b/extensions/grunt/yarn.lock index 90475ddc7e0..8b8c7a6c15b 100644 --- a/extensions/grunt/yarn.lock +++ b/extensions/grunt/yarn.lock @@ -7,7 +7,7 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== -vscode-nls@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.1.0.tgz#443b301a7465d88c81c0f4e1914f9857f0dce1e4" - integrity sha512-37Ha44QrLFwR2IfSSYdOArzUvOyoWbOYTwQC+wS0NfqKjhW7s0WQ1lMy5oJXgSZy9sAiZS5ifELhbpXodeMR8w== +vscode-nls@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.2.0.tgz#3cb6893dd9bd695244d8a024bdf746eea665cc3f" + integrity sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng== diff --git a/extensions/gulp/package.json b/extensions/gulp/package.json index 062dc4a788f..41c23eb1145 100644 --- a/extensions/gulp/package.json +++ b/extensions/gulp/package.json @@ -17,7 +17,7 @@ "watch": "gulp watch-extension:gulp" }, "dependencies": { - "vscode-nls": "^5.1.0" + "vscode-nls": "^5.2.0" }, "devDependencies": { "@types/node": "16.x" diff --git a/extensions/gulp/yarn.lock b/extensions/gulp/yarn.lock index 90475ddc7e0..8b8c7a6c15b 100644 --- a/extensions/gulp/yarn.lock +++ b/extensions/gulp/yarn.lock @@ -7,7 +7,7 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== -vscode-nls@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.1.0.tgz#443b301a7465d88c81c0f4e1914f9857f0dce1e4" - integrity sha512-37Ha44QrLFwR2IfSSYdOArzUvOyoWbOYTwQC+wS0NfqKjhW7s0WQ1lMy5oJXgSZy9sAiZS5ifELhbpXodeMR8w== +vscode-nls@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.2.0.tgz#3cb6893dd9bd695244d8a024bdf746eea665cc3f" + integrity sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng== diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index 527932fd7d8..5a46ebe48bd 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -261,7 +261,7 @@ "@vscode/extension-telemetry": "0.6.2", "vscode-languageclient": "^8.1.0-next.1", "vscode-nls": "^5.2.0", - "vscode-uri": "^3.0.5" + "vscode-uri": "^3.0.6" }, "devDependencies": { "@types/node": "16.x" diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index 00d2fe73472..7dc7e70a2f0 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -14,7 +14,7 @@ "vscode-languageserver": "^8.1.0-next.1", "vscode-languageserver-textdocument": "^1.0.7", "vscode-nls": "^5.2.0", - "vscode-uri": "^3.0.5" + "vscode-uri": "^3.0.6" }, "devDependencies": { "@types/mocha": "^9.1.1", diff --git a/extensions/html-language-features/server/src/modes/embeddedSupport.ts b/extensions/html-language-features/server/src/modes/embeddedSupport.ts index 9cc401fdf38..c3398f8f80f 100644 --- a/extensions/html-language-features/server/src/modes/embeddedSupport.ts +++ b/extensions/html-language-features/server/src/modes/embeddedSupport.ts @@ -196,9 +196,9 @@ function getSuffix(c: EmbeddedRegion) { } function substituteWithWhitespace(result: string, start: number, end: number, oldContent: string, before: string, after: string) { - let accumulatedWS = 0; result += before; - for (let i = start + before.length; i < end; i++) { + let accumulatedWS = -before.length; // start with a negative value to account for the before string + for (let i = start; i < end; i++) { const ch = oldContent[i]; if (ch === '\n' || ch === '\r') { // only write new lines, skip the whitespace 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 1e63a72ec89..722578cb07c 100644 --- a/extensions/html-language-features/server/src/test/embedded.test.ts +++ b/extensions/html-language-features/server/src/test/embedded.test.ts @@ -122,7 +122,7 @@ suite('HTML Embedded Support', () => { assertEmbeddedLanguageContent('
', 'javascript', ' foo(); bar(); '); assertEmbeddedLanguageContent('
', 'javascript', ' return; '); - + assertEmbeddedLanguageContent('
', 'javascript', ' return;\n foo(); '); }); }); diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index c467e1ac9b4..2532ccb506a 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -67,12 +67,7 @@ vscode-nls@^5.2.0: resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.2.0.tgz#3cb6893dd9bd695244d8a024bdf746eea665cc3f" integrity sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng== -vscode-uri@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.4.tgz#703c6dd7c0b727ee1c34a1287434138fb52d054f" - integrity sha512-aEmKD6H8Sg8gaQAUrnadG0BMeWXtiWhRsj1a94n2FYsMkDpgnK7BRVzZjOUYIvkv2B+bp5Bmt4ImZCpYbnJwkg== - -vscode-uri@^3.0.5: - version "3.0.5" - resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.5.tgz#3dd5a9e154e7c9a40de5eaa450a0ce59b435e584" - integrity sha512-bBp2pi1o6ynwlnGL8Tt6UBL1w3VsVZtHCU/Sl73bRfqjno3jMcVSCybdY+hj+31A8FQOELZJWwY+shLVLtcNew== +vscode-uri@^3.0.6, vscode-uri@^3.0.4: + version "3.0.6" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.6.tgz#5e6e2e1a4170543af30151b561a41f71db1d6f91" + integrity sha512-fmL7V1eiDBFRRnu+gfRWTzyPpNIHJTc4mWnFkwBUmO9U3KPgJAmTx7oxi2bl/Rh6HLdU7+4C9wlj0k2E4AdKFQ== diff --git a/extensions/html-language-features/yarn.lock b/extensions/html-language-features/yarn.lock index cfe8b8f7ad1..478e8548591 100644 --- a/extensions/html-language-features/yarn.lock +++ b/extensions/html-language-features/yarn.lock @@ -116,10 +116,10 @@ vscode-nls@^5.2.0: resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.2.0.tgz#3cb6893dd9bd695244d8a024bdf746eea665cc3f" integrity sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng== -vscode-uri@^3.0.5: - version "3.0.5" - resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.5.tgz#3dd5a9e154e7c9a40de5eaa450a0ce59b435e584" - integrity sha512-bBp2pi1o6ynwlnGL8Tt6UBL1w3VsVZtHCU/Sl73bRfqjno3jMcVSCybdY+hj+31A8FQOELZJWwY+shLVLtcNew== +vscode-uri@^3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.6.tgz#5e6e2e1a4170543af30151b561a41f71db1d6f91" + integrity sha512-fmL7V1eiDBFRRnu+gfRWTzyPpNIHJTc4mWnFkwBUmO9U3KPgJAmTx7oxi2bl/Rh6HLdU7+4C9wlj0k2E4AdKFQ== yallist@^4.0.0: version "4.0.0" diff --git a/extensions/html/package.json b/extensions/html/package.json index aad035c05cc..238f1348955 100644 --- a/extensions/html/package.json +++ b/extensions/html/package.json @@ -76,6 +76,12 @@ "meta.tag string.quoted": "other" } } + ], + "snippets": [ + { + "language": "html", + "path": "./snippets/html.code-snippets" + } ] }, "repository": { diff --git a/extensions/html/snippets/html.code-snippets b/extensions/html/snippets/html.code-snippets new file mode 100644 index 00000000000..62d61b43b7b --- /dev/null +++ b/extensions/html/snippets/html.code-snippets @@ -0,0 +1,18 @@ +{ + "html doc": { + "isFileTemplate": true, + "body": [ + "", + "", + "", + "\t", + "\t${1:title}", + "", + "", + "\t$0", + "", + "" + ], + "description": "HTML Document" + } +} diff --git a/extensions/image-preview/README.md b/extensions/image-preview/README.md deleted file mode 100644 index d3f0bd6cb6c..00000000000 --- a/extensions/image-preview/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Image Preview - -**Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled. - -## Features - -This extension provides VS Code's built-in image preview functionality. - -Supported image formats: - -- `*.jpg`, `*.jpe`, `*.jpeg` -- `*.png` -- `*.bmp` -- `*.gif` -- `*.ico` -- `*.webp` diff --git a/extensions/ini/package.json b/extensions/ini/package.json index aa98ee3a353..0c7dda3c880 100644 --- a/extensions/ini/package.json +++ b/extensions/ini/package.json @@ -27,9 +27,9 @@ { "id": "properties", "extensions": [ + ".conf", ".properties", ".cfg", - ".conf", ".directory", ".gitattributes", ".gitconfig", diff --git a/extensions/ipynb/package.json b/extensions/ipynb/package.json index 2d10494e412..9facfaa3322 100644 --- a/extensions/ipynb/package.json +++ b/extensions/ipynb/package.json @@ -13,7 +13,8 @@ "diffContentOptions" ], "activationEvents": [ - "*" + "onNotebook:jupyter-notebook", + "onCommand:ipynb.newUntitledIpynb" ], "extensionKind": [ "workspace", diff --git a/extensions/ipynb/src/notebookAttachmentCleaner.ts b/extensions/ipynb/src/notebookAttachmentCleaner.ts index 265c284d984..a72761754df 100644 --- a/extensions/ipynb/src/notebookAttachmentCleaner.ts +++ b/extensions/ipynb/src/notebookAttachmentCleaner.ts @@ -153,16 +153,16 @@ export class AttachmentCleaner implements vscode.CodeActionProvider { this.saveAllAttachmentsToCache(cell.metadata, notebookUri, cellFragment); } - if (this.checkMetadataAttachmentsExistence(cell.metadata)) { + if (this.checkMetadataHasAttachmentsField(cell.metadata)) { // the cell metadata contains attachments, check if any are used in the markdown source - for (const currFilename of Object.keys(cell.metadata.attachments)) { + for (const [currFilename, attachment] of Object.entries(cell.metadata.attachments)) { // means markdown reference is present in the metadata, rendering will work properly // therefore, we don't need to check it in the next loop either if (markdownAttachmentsRefedInCell.has(currFilename)) { // attachment reference is present in the markdown source, no need to cache it markdownAttachmentsRefedInCell.get(currFilename)!.valid = true; - markdownAttachmentsInUse[currFilename] = cell.metadata.attachments[currFilename]; + markdownAttachmentsInUse[currFilename] = attachment as IAttachmentData; } else { // attachment reference is not present in the markdown source, cache it this.saveAttachmentToCache(notebookUri, cellFragment, currFilename, cell.metadata); @@ -227,7 +227,7 @@ export class AttachmentCleaner implements vscode.CodeActionProvider { const diagnostics: IAttachmentDiagnostic[] = []; const markdownAttachments = this.getAttachmentNames(document); - if (this.checkMetadataAttachmentsExistence(activeCell.metadata)) { + if (this.checkMetadataHasAttachmentsField(activeCell.metadata)) { for (const [currFilename, attachment] of markdownAttachments) { if (!activeCell.metadata.attachments[currFilename]) { // no attachment reference in the metadata @@ -295,8 +295,8 @@ export class AttachmentCleaner implements vscode.CodeActionProvider { * @param metadata metadata of cell * @returns boolean representing the presence of any attachments */ - private checkMetadataAttachmentsExistence(metadata: { [key: string]: any }): boolean { - return !!(metadata.attachments); + private checkMetadataHasAttachmentsField(metadata: { [key: string]: unknown }): metadata is { readonly attachments: Record } { + return !!metadata.attachments && typeof metadata.attachments === 'object'; } /** @@ -305,14 +305,16 @@ export class AttachmentCleaner implements vscode.CodeActionProvider { * @param notebookUri uri for the notebook being edited * @param cellFragment fragment of cell being edited */ - private saveAllAttachmentsToCache(metadata: { [key: string]: any }, notebookUri: string, cellFragment: string): void { + private saveAllAttachmentsToCache(metadata: { [key: string]: unknown }, notebookUri: string, cellFragment: string): void { const documentCache = this._attachmentCache.get(notebookUri) ?? new Map(); this._attachmentCache.set(notebookUri, documentCache); const cellCache = documentCache.get(cellFragment) ?? new Map(); documentCache.set(cellFragment, cellCache); - for (const currFilename of Object.keys(metadata.attachments)) { - cellCache.set(currFilename, metadata.attachments[currFilename]); + if (metadata.attachments && typeof metadata.attachments === 'object') { + for (const [currFilename, attachment] of Object.entries(metadata.attachments)) { + cellCache.set(currFilename, attachment); + } } } diff --git a/extensions/ipynb/src/serializers.ts b/extensions/ipynb/src/serializers.ts index f075737b4d8..96da8f2c617 100644 --- a/extensions/ipynb/src/serializers.ts +++ b/extensions/ipynb/src/serializers.ts @@ -7,7 +7,6 @@ import type * as nbformat from '@jupyterlab/nbformat'; import { NotebookCell, NotebookCellData, NotebookCellKind, NotebookCellOutput } from 'vscode'; import { CellOutputMetadata } from './common'; import { textMimeTypes } from './deserializers'; -import { compressOutputItemStreams } from './streamCompressor'; const textDecoder = new TextDecoder(); @@ -277,17 +276,21 @@ type JupyterOutput = function convertStreamOutput(output: NotebookCellOutput): JupyterOutput { const outputs: string[] = []; - const compressedStream = output.items.length ? new TextDecoder().decode(compressOutputItemStreams(output.items[0].mime, output.items)) : ''; - // Ensure each line is a separate entry in an array (ending with \n). - const lines = compressedStream.split('\n'); - // If the last item in `outputs` is not empty and the first item in `lines` is not empty, then concate them. - // As they are part of the same line. - if (outputs.length && lines.length && lines[0].length > 0) { - outputs[outputs.length - 1] = `${outputs[outputs.length - 1]}${lines.shift()!}`; - } - for (const line of lines) { - outputs.push(line); - } + output.items + .filter((opit) => opit.mime === CellOutputMimeTypes.stderr || opit.mime === CellOutputMimeTypes.stdout) + .map((opit) => textDecoder.decode(opit.data)) + .forEach(value => { + // Ensure each line is a separate entry in an array (ending with \n). + const lines = value.split('\n'); + // If the last item in `outputs` is not empty and the first item in `lines` is not empty, then concate them. + // As they are part of the same line. + if (outputs.length && lines.length && lines[0].length > 0) { + outputs[outputs.length - 1] = `${outputs[outputs.length - 1]}${lines.shift()!}`; + } + for (const line of lines) { + outputs.push(line); + } + }); for (let index = 0; index < (outputs.length - 1); index++) { outputs[index] = `${outputs[index]}\n`; diff --git a/extensions/ipynb/src/streamCompressor.ts b/extensions/ipynb/src/streamCompressor.ts deleted file mode 100644 index cea3184e16c..00000000000 --- a/extensions/ipynb/src/streamCompressor.ts +++ /dev/null @@ -1,63 +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 type { NotebookCellOutputItem } from 'vscode'; - - -/** - * Given a stream of individual stdout outputs, this function will return the compressed lines, escaping some of the common terminal escape codes. - * E.g. some terminal escape codes would result in the previous line getting cleared, such if we had 3 lines and - * last line contained such a code, then the result string would be just the first two lines. - */ -export function compressOutputItemStreams(mimeType: string, outputs: NotebookCellOutputItem[]) { - // return outputs.find(op => op.mime === mimeType)!.data.buffer; - - const buffers: Uint8Array[] = []; - let startAppending = false; - // Pick the first set of outputs with the same mime type. - for (const output of outputs) { - if (output.mime === mimeType) { - if ((buffers.length === 0 || startAppending)) { - buffers.push(output.data); - startAppending = true; - } - } else if (startAppending) { - startAppending = false; - } - } - compressStreamBuffer(buffers); - const totalBytes = buffers.reduce((p, c) => p + c.byteLength, 0); - const combinedBuffer = new Uint8Array(totalBytes); - let offset = 0; - for (const buffer of buffers) { - combinedBuffer.set(buffer, offset); - offset = offset + buffer.byteLength; - } - return combinedBuffer; -} -const MOVE_CURSOR_1_LINE_COMMAND = `${String.fromCharCode(27)}[A`; -const MOVE_CURSOR_1_LINE_COMMAND_BYTES = MOVE_CURSOR_1_LINE_COMMAND.split('').map(c => c.charCodeAt(0)); -const LINE_FEED = 10; -function compressStreamBuffer(streams: Uint8Array[]) { - streams.forEach((stream, index) => { - if (index === 0 || stream.length < MOVE_CURSOR_1_LINE_COMMAND.length) { - return; - } - - const previousStream = streams[index - 1]; - - // Remove the previous line if required. - const command = stream.subarray(0, MOVE_CURSOR_1_LINE_COMMAND.length); - if (command[0] === MOVE_CURSOR_1_LINE_COMMAND_BYTES[0] && command[1] === MOVE_CURSOR_1_LINE_COMMAND_BYTES[1] && command[2] === MOVE_CURSOR_1_LINE_COMMAND_BYTES[2]) { - const lastIndexOfLineFeed = previousStream.lastIndexOf(LINE_FEED); - if (lastIndexOfLineFeed === -1) { - return; - } - streams[index - 1] = previousStream.subarray(0, lastIndexOfLineFeed); - streams[index] = stream.subarray(MOVE_CURSOR_1_LINE_COMMAND.length); - } - }); - return streams; -} diff --git a/extensions/jake/package.json b/extensions/jake/package.json index 5ffa78e3981..4e6a94a8b24 100644 --- a/extensions/jake/package.json +++ b/extensions/jake/package.json @@ -17,7 +17,7 @@ "watch": "gulp watch-extension:jake" }, "dependencies": { - "vscode-nls": "^5.1.0" + "vscode-nls": "^5.2.0" }, "devDependencies": { "@types/node": "16.x" diff --git a/extensions/jake/yarn.lock b/extensions/jake/yarn.lock index 90475ddc7e0..8b8c7a6c15b 100644 --- a/extensions/jake/yarn.lock +++ b/extensions/jake/yarn.lock @@ -7,7 +7,7 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== -vscode-nls@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.1.0.tgz#443b301a7465d88c81c0f4e1914f9857f0dce1e4" - integrity sha512-37Ha44QrLFwR2IfSSYdOArzUvOyoWbOYTwQC+wS0NfqKjhW7s0WQ1lMy5oJXgSZy9sAiZS5ifELhbpXodeMR8w== +vscode-nls@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.2.0.tgz#3cb6893dd9bd695244d8a024bdf746eea665cc3f" + integrity sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng== diff --git a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json index 1695f820fcc..ed2e78282ca 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/73af17bf3e45339df06d92751ab366ce96c38516", + "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/c2744520e325330d0608fc1d1993d7fe98e66202", "name": "JavaScript (with React support)", "scopeName": "source.js", "patterns": [ @@ -2674,7 +2674,7 @@ { "name": "meta.object.member.js meta.object-literal.key.js", "begin": "(?=[\\'\\\"\\`])", - "end": "(?=:)|((?<=[\\'\\\"\\`])(?=((\\s*[\\(\\<,}])|(\\s+(as)\\s+))))", + "end": "(?=:)|((?<=[\\'\\\"\\`])(?=((\\s*[\\(\\<,}])|(\\s+(as|satisifies)\\s+))))", "patterns": [ { "include": "#comment" @@ -2687,7 +2687,7 @@ { "name": "meta.object.member.js meta.object-literal.key.js", "begin": "(?x)(?=(\\b(?]|\\|\\||\\&\\&|\\!\\=\\=|$|^|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|^|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|^|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|^|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|((? { cachedElements.push({ element: element as HTMLElement, line }); } } - console.log(cachedElements); } return cachedElements; }; diff --git a/extensions/markdown-language-features/server/README.md b/extensions/markdown-language-features/server/README.md index 761baf37719..6b6e0b0c81c 100644 --- a/extensions/markdown-language-features/server/README.md +++ b/extensions/markdown-language-features/server/README.md @@ -64,8 +64,12 @@ The server supports the following settings: - `enabled` — Enable/disable validation of links to fragments in the current files: `[text](#head)` - `fileLinks` - `enabled` — Enable/disable validation of links to file in the workspace. - - `markdownFragmentLinks` — Enable/disable validation of links to headers in other Markdown files. + - `markdownFragmentLinks` — Enable/disable validation of links to headers in other Markdown files. Use `inherit` to inherit the `fragmentLinks` setting. - `ignoredLinks` — Array of glob patterns for files that should not be validated. + - `unusedLinkDefinitions` + - `enabled` — Enable/disable validation of unused link definitions. + - `duplicateLinkDefinitions` + - `enabled` — Enable/disable validation of duplicated link definitions. ### Custom requests diff --git a/extensions/markdown-language-features/server/package.json b/extensions/markdown-language-features/server/package.json index c38c618f05d..a5322a81c4a 100644 --- a/extensions/markdown-language-features/server/package.json +++ b/extensions/markdown-language-features/server/package.json @@ -13,8 +13,8 @@ "vscode-languageserver": "^8.0.2", "vscode-languageserver-textdocument": "^1.0.5", "vscode-languageserver-types": "^3.17.1", - "vscode-markdown-languageservice": "^0.1.0-alpha.10", - "vscode-nls": "^5.0.1", + "vscode-markdown-languageservice": "^0.2.0-alpha.2", + "vscode-nls": "^5.2.0", "vscode-uri": "^3.0.3" }, "devDependencies": { diff --git a/extensions/markdown-language-features/server/src/configuration.ts b/extensions/markdown-language-features/server/src/configuration.ts index 69358261f82..b521e478605 100644 --- a/extensions/markdown-language-features/server/src/configuration.ts +++ b/extensions/markdown-language-features/server/src/configuration.ts @@ -6,7 +6,7 @@ import { Connection, Emitter } from 'vscode-languageserver'; import { Disposable } from './util/dispose'; -export type ValidateEnabled = 'ignore' | 'warning' | 'error'; +export type ValidateEnabled = 'ignore' | 'warning' | 'error' | 'hint'; interface Settings { readonly markdown: { @@ -26,9 +26,15 @@ interface Settings { }; readonly fileLinks: { readonly enabled: ValidateEnabled; - readonly markdownFragmentLinks: ValidateEnabled; + readonly markdownFragmentLinks: ValidateEnabled | 'inherit'; }; readonly ignoredLinks: readonly string[]; + readonly unusedLinkDefinitions: { + readonly enabled: ValidateEnabled; + }; + readonly duplicateLinkDefinitions: { + readonly enabled: ValidateEnabled; + }; }; }; } diff --git a/extensions/markdown-language-features/server/src/languageFeatures/diagnostics.ts b/extensions/markdown-language-features/server/src/languageFeatures/diagnostics.ts index d0f4d9c73af..0f7e6954dbc 100644 --- a/extensions/markdown-language-features/server/src/languageFeatures/diagnostics.ts +++ b/extensions/markdown-language-features/server/src/languageFeatures/diagnostics.ts @@ -15,6 +15,8 @@ const defaultDiagnosticOptions: md.DiagnosticOptions = { validateReferences: md.DiagnosticLevel.ignore, validateFragmentLinks: md.DiagnosticLevel.ignore, validateMarkdownFileLinkFragments: md.DiagnosticLevel.ignore, + validateUnusedLinkDefinitions: md.DiagnosticLevel.ignore, + validateDuplicateLinkDefinitions: md.DiagnosticLevel.ignore, ignoreLinks: [], }; @@ -23,6 +25,7 @@ function convertDiagnosticLevel(enabled: ValidateEnabled): md.DiagnosticLevel | case 'error': return md.DiagnosticLevel.error; case 'warning': return md.DiagnosticLevel.warning; case 'ignore': return md.DiagnosticLevel.ignore; + case 'hint': return md.DiagnosticLevel.hint; default: return md.DiagnosticLevel.ignore; } } @@ -33,11 +36,14 @@ function getDiagnosticsOptions(config: ConfigurationManager): md.DiagnosticOptio return defaultDiagnosticOptions; } + const validateFragmentLinks = convertDiagnosticLevel(settings.markdown.validate.fragmentLinks.enabled); return { validateFileLinks: convertDiagnosticLevel(settings.markdown.validate.fileLinks.enabled), validateReferences: convertDiagnosticLevel(settings.markdown.validate.referenceLinks.enabled), validateFragmentLinks: convertDiagnosticLevel(settings.markdown.validate.fragmentLinks.enabled), - validateMarkdownFileLinkFragments: convertDiagnosticLevel(settings.markdown.validate.fileLinks.markdownFragmentLinks), + validateMarkdownFileLinkFragments: settings.markdown.validate.fileLinks.markdownFragmentLinks === 'inherit' ? validateFragmentLinks : convertDiagnosticLevel(settings.markdown.validate.fileLinks.markdownFragmentLinks), + validateUnusedLinkDefinitions: convertDiagnosticLevel(settings.markdown.validate.unusedLinkDefinitions.enabled), + validateDuplicateLinkDefinitions: convertDiagnosticLevel(settings.markdown.validate.duplicateLinkDefinitions.enabled), ignoreLinks: settings.markdown.validate.ignoredLinks, }; } diff --git a/extensions/markdown-language-features/server/src/server.ts b/extensions/markdown-language-features/server/src/server.ts index 95a088b439b..c17a32a63ee 100644 --- a/extensions/markdown-language-features/server/src/server.ts +++ b/extensions/markdown-language-features/server/src/server.ts @@ -93,6 +93,7 @@ export async function startServer(connection: Connection, serverConfig: { completionProvider: { triggerCharacters: ['.', '/', '#'] }, definitionProvider: true, documentLinkProvider: { resolveProvider: true }, + documentHighlightProvider: false, // TODO: Disabling for now documentSymbolProvider: true, foldingRangeProvider: true, referencesProvider: true, @@ -232,6 +233,14 @@ export async function startServer(connection: Connection, serverConfig: { return codeAction; }); + connection.onDocumentHighlight(async (params, token) => { + const document = documents.get(params.textDocument.uri); + if (!document) { + return undefined; + } + return mdLs!.getDocumentHighlights(document, params.position, token); + }); + connection.onRequest(protocol.getReferencesToFileInWorkspace, (async (params: { uri: string }, token: CancellationToken) => { return mdLs!.getFileReferences(URI.parse(params.uri), token); })); diff --git a/extensions/markdown-language-features/server/yarn.lock b/extensions/markdown-language-features/server/yarn.lock index 00347dd2bd8..f9067270e01 100644 --- a/extensions/markdown-language-features/server/yarn.lock +++ b/extensions/markdown-language-features/server/yarn.lock @@ -42,10 +42,10 @@ vscode-languageserver@^8.0.2: dependencies: vscode-languageserver-protocol "3.17.2" -vscode-markdown-languageservice@^0.1.0-alpha.10: - version "0.1.0-alpha.10" - resolved "https://registry.yarnpkg.com/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.1.0-alpha.10.tgz#012dcf600b9d1a738cd7071f17627285342d17c7" - integrity sha512-GZTxGZp49BIf/k5plc5x+Bp70kmwaTdt523p+wifG9AQ0uKMSRcwmlKu8mOcJUd0ZvDR3ORI/Cze90Dy5HCM2A== +vscode-markdown-languageservice@^0.2.0-alpha.2: + version "0.2.0-alpha.2" + resolved "https://registry.yarnpkg.com/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.2.0-alpha.2.tgz#ed08bc3f7f19c5fb0283547a7855542224ce377c" + integrity sha512-2/Mp/b63ohC8k0h1boWohf2G6SwLb3IA+c3UcqPs4FdaRpGqpf2wVXaT5aQ5qVRGpqvCwfyyyr2YEdqwuQc5KA== dependencies: picomatch "^2.3.1" vscode-languageserver-textdocument "^1.0.5" @@ -53,7 +53,7 @@ vscode-markdown-languageservice@^0.1.0-alpha.10: vscode-nls "^5.0.1" vscode-uri "^3.0.3" -vscode-nls@^5.0.1: +vscode-nls@^5.0.1, vscode-nls@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.2.0.tgz#3cb6893dd9bd695244d8a024bdf746eea665cc3f" integrity sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng== diff --git a/extensions/markdown-language-features/src/languageFeatures/fileReferences.ts b/extensions/markdown-language-features/src/languageFeatures/fileReferences.ts index b1c06b85f9f..46ed49f3c4b 100644 --- a/extensions/markdown-language-features/src/languageFeatures/fileReferences.ts +++ b/extensions/markdown-language-features/src/languageFeatures/fileReferences.ts @@ -21,10 +21,7 @@ export class FindFileReferencesCommand implements Command { ) { } public async execute(resource?: vscode.Uri) { - if (!resource) { - resource = vscode.window.activeTextEditor?.document.uri; - } - + resource ??= vscode.window.activeTextEditor?.document.uri; if (!resource) { vscode.window.showErrorMessage(localize('error.noResource', "Find file references failed. No resource provided.")); return; diff --git a/extensions/markdown-language-features/src/languageFeatures/linkUpdater.ts b/extensions/markdown-language-features/src/languageFeatures/linkUpdater.ts index cfe16893b39..0b50ed4fc80 100644 --- a/extensions/markdown-language-features/src/languageFeatures/linkUpdater.ts +++ b/extensions/markdown-language-features/src/languageFeatures/linkUpdater.ts @@ -18,9 +18,9 @@ import { convertRange } from './fileReferences'; const localize = nls.loadMessageBundle(); const settingNames = Object.freeze({ - enabled: 'experimental.updateLinksOnFileMove.enabled', - externalFileGlobs: 'experimental.updateLinksOnFileMove.externalFileGlobs', - enableForDirectories: 'experimental.updateLinksOnFileMove.enableForDirectories', + enabled: 'updateLinksOnFileMove.enabled', + externalFileGlobs: 'updateLinksOnFileMove.externalFileGlobs', + enableForDirectories: 'updateLinksOnFileMove.enableForDirectories', }); const enum UpdateLinksOnFileMoveSetting { @@ -45,14 +45,11 @@ class UpdateLinksOnFileRenameHandler extends Disposable { super(); this._register(vscode.workspace.onDidRenameFiles(async (e) => { - for (const { newUri, oldUri } of e.files) { - const config = vscode.workspace.getConfiguration('markdown', newUri); - if (!await this.shouldParticipateInLinkUpdate(config, newUri)) { - continue; + await Promise.all(e.files.map(async (rename) => { + if (await this.shouldParticipateInLinkUpdate(rename.newUri)) { + this._pendingRenames.add(rename); } - - this._pendingRenames.add({ newUri, oldUri }); - } + })); if (this._pendingRenames.size) { this._delayer.trigger(() => { @@ -95,7 +92,8 @@ class UpdateLinksOnFileRenameHandler extends Disposable { return false; } } - private async shouldParticipateInLinkUpdate(config: vscode.WorkspaceConfiguration, newUri: vscode.Uri): Promise { + private async shouldParticipateInLinkUpdate(newUri: vscode.Uri): Promise { + const config = vscode.workspace.getConfiguration('markdown', newUri); const setting = config.get(settingNames.enabled); if (setting === UpdateLinksOnFileMoveSetting.Never) { return false; @@ -123,50 +121,38 @@ class UpdateLinksOnFileRenameHandler extends Disposable { return false; } - const enum Choice { - None = 0, - Accept = 1, - Reject = 2, - Always = 3, - Never = 4, - } + const rejectItem: vscode.MessageItem = { + title: localize('reject.title', "No"), + isCloseAffordance: true, + }; - interface Item extends vscode.MessageItem { - readonly choice: Choice; - } + const acceptItem: vscode.MessageItem = { + title: localize('accept.title', "Yes"), + }; - const response = await vscode.window.showInformationMessage( + const alwaysItem: vscode.MessageItem = { + title: localize('always.title', "Always automatically update Markdown Links"), + }; + + const neverItem: vscode.MessageItem = { + title: localize('never.title', "Never automatically update Markdown Links"), + }; + + const choice = await vscode.window.showInformationMessage( newResources.length === 1 ? localize('prompt', "Update Markdown links for '{0}'?", path.basename(newResources[0].fsPath)) : this.getConfirmMessage(localize('promptMoreThanOne', "Update Markdown link for the following {0} files?", newResources.length), newResources), { modal: true, - }, { - title: localize('reject.title', "No"), - choice: Choice.Reject, - isCloseAffordance: true, - }, { - title: localize('accept.title', "Yes"), - choice: Choice.Accept, - }, { - title: localize('always.title', "Always automatically update Markdown Links"), - choice: Choice.Always, - }, { - title: localize('never.title', "Never automatically update Markdown Links"), - choice: Choice.Never, - }); + }, rejectItem, acceptItem, alwaysItem, neverItem); - if (!response) { - return false; - } - - switch (response.choice) { - case Choice.Accept: { + switch (choice) { + case acceptItem: { return true; } - case Choice.Reject: { + case rejectItem: { return false; } - case Choice.Always: { + case alwaysItem: { const config = vscode.workspace.getConfiguration('markdown', newResources[0]); config.update( settingNames.enabled, @@ -174,7 +160,7 @@ class UpdateLinksOnFileRenameHandler extends Disposable { this.getConfigTargetScope(config, settingNames.enabled)); return true; } - case Choice.Never: { + case neverItem: { const config = vscode.workspace.getConfiguration('markdown', newResources[0]); config.update( settingNames.enabled, @@ -182,9 +168,10 @@ class UpdateLinksOnFileRenameHandler extends Disposable { this.getConfigTargetScope(config, settingNames.enabled)); return false; } + default: { + return false; + } } - - return false; } private async getEditsForFileRename(renames: readonly RenameAction[], token: vscode.CancellationToken): Promise<{ edit: vscode.WorkspaceEdit; resourcesBeingRenamed: vscode.Uri[] } | undefined> { @@ -241,6 +228,6 @@ class UpdateLinksOnFileRenameHandler extends Disposable { } } -export function registerUpdateLinksOnRename(client: MdLanguageClient) { +export function registerUpdateLinksOnRename(client: MdLanguageClient): vscode.Disposable { return new UpdateLinksOnFileRenameHandler(client); } diff --git a/extensions/markdown-language-features/src/logging.ts b/extensions/markdown-language-features/src/logging.ts index 2f9a9732d58..d296fed655d 100644 --- a/extensions/markdown-language-features/src/logging.ts +++ b/extensions/markdown-language-features/src/logging.ts @@ -35,9 +35,7 @@ export class VsCodeOutputLogger extends Disposable implements ILogger { private _outputChannel?: vscode.OutputChannel; private get outputChannel() { - if (!this._outputChannel) { - this._outputChannel = this._register(vscode.window.createOutputChannel('Markdown')); - } + this._outputChannel ??= this._register(vscode.window.createOutputChannel('Markdown')); return this._outputChannel; } diff --git a/extensions/markdown-language-features/src/markdownExtensions.ts b/extensions/markdown-language-features/src/markdownExtensions.ts index cd6dfeb4260..0c768d0d48e 100644 --- a/extensions/markdown-language-features/src/markdownExtensions.ts +++ b/extensions/markdown-language-features/src/markdownExtensions.ts @@ -144,9 +144,7 @@ class VSCodeExtensionMarkdownContributionProvider extends Disposable implements public readonly onContributionsChanged = this._onContributionsChanged.event; public get contributions(): MarkdownContributions { - if (!this._contributions) { - this._contributions = this.getCurrentContributions(); - } + this._contributions ??= this.getCurrentContributions(); return this._contributions; } diff --git a/extensions/markdown-language-features/src/preview/preview.ts b/extensions/markdown-language-features/src/preview/preview.ts index 26f94614ad8..51348bd7128 100644 --- a/extensions/markdown-language-features/src/preview/preview.ts +++ b/extensions/markdown-language-features/src/preview/preview.ts @@ -158,7 +158,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { this._register(watcher.onDidChange(uri => { if (this.isPreviewOf(uri)) { // Only use the file system event when VS Code does not already know about the file - if (!vscode.workspace.textDocuments.some(doc => doc.uri.toString() !== uri.toString())) { + if (!vscode.workspace.textDocuments.some(doc => doc.uri.toString() === uri.toString())) { this.refresh(); } } diff --git a/extensions/markdown-language-features/src/preview/previewManager.ts b/extensions/markdown-language-features/src/preview/previewManager.ts index 50196ac882e..9e32e954c92 100644 --- a/extensions/markdown-language-features/src/preview/previewManager.ts +++ b/extensions/markdown-language-features/src/preview/previewManager.ts @@ -42,8 +42,9 @@ class PreviewStore extends Disposable { } public get(resource: vscode.Uri, previewSettings: DynamicPreviewSettings): T | undefined { + const previewColumn = this.resolvePreviewColumn(previewSettings); for (const preview of this._previews) { - if (preview.matchesResource(resource, previewSettings.previewColumn, previewSettings.locked)) { + if (preview.matchesResource(resource, previewColumn, previewSettings.locked)) { return preview; } } @@ -57,6 +58,18 @@ class PreviewStore extends Disposable { public delete(preview: T) { this._previews.delete(preview); } + + private resolvePreviewColumn(previewSettings: DynamicPreviewSettings): vscode.ViewColumn | undefined { + if (previewSettings.previewColumn === vscode.ViewColumn.Active) { + return vscode.window.tabGroups.activeTabGroup.viewColumn; + } + + if (previewSettings.previewColumn === vscode.ViewColumn.Beside) { + return vscode.window.tabGroups.activeTabGroup.viewColumn + 1; + } + + return previewSettings.previewColumn; + } } export class MarkdownPreviewManager extends Disposable implements vscode.WebviewPanelSerializer, vscode.CustomTextEditorProvider { diff --git a/extensions/markdown-language-features/src/test/documentLink.test.ts b/extensions/markdown-language-features/src/test/documentLink.test.ts index 37fe52e3dfb..0000069a660 100644 --- a/extensions/markdown-language-features/src/test/documentLink.test.ts +++ b/extensions/markdown-language-features/src/test/documentLink.test.ts @@ -134,7 +134,7 @@ async function getLinksForFile(file: vscode.Uri): Promise } }); - test('Should navigate to fragment within current untitled file', async () => { // TODO: skip for now for ls migration + test.skip('Should navigate to fragment within current untitled file', async () => { // TODO: skip for now for ls migration const testFile = workspaceFile('x.md').with({ scheme: 'untitled' }); await withFileContents(testFile, joinLines( '[](#second)', @@ -169,9 +169,8 @@ async function withFileContents(file: vscode.Uri, contents: string): Promise { }); }); - suite.only('image-caching', () => { + suite('image-caching', () => { const input = '![](img.png) [](no-img.png) ![](http://example.org/img.png) ![](img.png) ![](./img2.png)'; test('Extracts all images', async () => { diff --git a/extensions/markdown-language-features/yarn.lock b/extensions/markdown-language-features/yarn.lock index 5ffe3ff4b82..6bd778ed899 100644 --- a/extensions/markdown-language-features/yarn.lock +++ b/extensions/markdown-language-features/yarn.lock @@ -258,10 +258,10 @@ vscode-nls@^5.0.1: resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.1.tgz#ba23fc4d4420d25e7f886c8e83cbdcec47aa48b2" integrity sha512-hHQV6iig+M21lTdItKPkJAaWrxALQb/nqpVffakO4knJOh3DrU2SXOMzUzNgo1eADPzu3qSsJY1weCzvR52q9A== -vscode-nls@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.1.0.tgz#443b301a7465d88c81c0f4e1914f9857f0dce1e4" - integrity sha512-37Ha44QrLFwR2IfSSYdOArzUvOyoWbOYTwQC+wS0NfqKjhW7s0WQ1lMy5oJXgSZy9sAiZS5ifELhbpXodeMR8w== +vscode-nls@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.2.0.tgz#3cb6893dd9bd695244d8a024bdf746eea665cc3f" + integrity sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng== vscode-uri@^3.0.3: version "3.0.3" diff --git a/extensions/image-preview/.vscodeignore b/extensions/media-preview/.vscodeignore similarity index 100% rename from extensions/image-preview/.vscodeignore rename to extensions/media-preview/.vscodeignore diff --git a/extensions/media-preview/README.md b/extensions/media-preview/README.md new file mode 100644 index 00000000000..30bb44b87a1 --- /dev/null +++ b/extensions/media-preview/README.md @@ -0,0 +1,29 @@ +# Media Preview + +**Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled. + +## Features + +This extension provides basic preview for images, audio and video files. + +### Supported image file extensions + +- `.jpg`, `.jpe`, `.jpeg` +- `.png` +- `.bmp` +- `.gif` +- `.ico` +- `.webp` +- `.avif` + + +### Supported audio formats + +- `.mp3` +- `.wav` +- `.ogg` + +### Supported video formats + +- `.mp4` (does not support `aac` audio track) +- `.webm` (vp8 only) diff --git a/extensions/image-preview/extension-browser.webpack.config.js b/extensions/media-preview/extension-browser.webpack.config.js similarity index 100% rename from extensions/image-preview/extension-browser.webpack.config.js rename to extensions/media-preview/extension-browser.webpack.config.js diff --git a/extensions/image-preview/extension.webpack.config.js b/extensions/media-preview/extension.webpack.config.js similarity index 100% rename from extensions/image-preview/extension.webpack.config.js rename to extensions/media-preview/extension.webpack.config.js diff --git a/extensions/image-preview/icon.png b/extensions/media-preview/icon.png similarity index 100% rename from extensions/image-preview/icon.png rename to extensions/media-preview/icon.png diff --git a/extensions/image-preview/media/audioPreview.css b/extensions/media-preview/media/audioPreview.css similarity index 100% rename from extensions/image-preview/media/audioPreview.css rename to extensions/media-preview/media/audioPreview.css diff --git a/extensions/image-preview/media/audioPreview.js b/extensions/media-preview/media/audioPreview.js similarity index 100% rename from extensions/image-preview/media/audioPreview.js rename to extensions/media-preview/media/audioPreview.js diff --git a/extensions/image-preview/media/imagePreview.css b/extensions/media-preview/media/imagePreview.css similarity index 100% rename from extensions/image-preview/media/imagePreview.css rename to extensions/media-preview/media/imagePreview.css diff --git a/extensions/image-preview/media/imagePreview.js b/extensions/media-preview/media/imagePreview.js similarity index 100% rename from extensions/image-preview/media/imagePreview.js rename to extensions/media-preview/media/imagePreview.js diff --git a/extensions/image-preview/media/loading-dark.svg b/extensions/media-preview/media/loading-dark.svg similarity index 100% rename from extensions/image-preview/media/loading-dark.svg rename to extensions/media-preview/media/loading-dark.svg diff --git a/extensions/image-preview/media/loading-hc.svg b/extensions/media-preview/media/loading-hc.svg similarity index 100% rename from extensions/image-preview/media/loading-hc.svg rename to extensions/media-preview/media/loading-hc.svg diff --git a/extensions/image-preview/media/loading.svg b/extensions/media-preview/media/loading.svg similarity index 100% rename from extensions/image-preview/media/loading.svg rename to extensions/media-preview/media/loading.svg diff --git a/extensions/image-preview/media/videoPreview.css b/extensions/media-preview/media/videoPreview.css similarity index 100% rename from extensions/image-preview/media/videoPreview.css rename to extensions/media-preview/media/videoPreview.css diff --git a/extensions/image-preview/media/videoPreview.js b/extensions/media-preview/media/videoPreview.js similarity index 98% rename from extensions/image-preview/media/videoPreview.js rename to extensions/media-preview/media/videoPreview.js index a8f1f6955fd..54aa60ad86f 100644 --- a/extensions/image-preview/media/videoPreview.js +++ b/extensions/media-preview/media/videoPreview.js @@ -30,6 +30,7 @@ if (settings.src !== null) { video.src = settings.src; } + video.playsInline = true; video.controls = true; function onLoaded() { diff --git a/extensions/image-preview/package.json b/extensions/media-preview/package.json similarity index 89% rename from extensions/image-preview/package.json rename to extensions/media-preview/package.json index 03c2784a533..384ab75f58b 100644 --- a/extensions/image-preview/package.json +++ b/extensions/media-preview/package.json @@ -1,5 +1,5 @@ { - "name": "image-preview", + "name": "media-preview", "displayName": "%displayName%", "description": "%description%", "extensionKind": [ @@ -12,7 +12,7 @@ "license": "MIT", "aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255", "engines": { - "vscode": "^1.39.0" + "vscode": "^1.70.0" }, "main": "./out/extension", "browser": "./dist/browser/extension.js", @@ -50,7 +50,7 @@ "priority": "builtin", "selector": [ { - "filenamePattern": "*.{mp3,wav,opus,aac}" + "filenamePattern": "*.{mp3,wav,ogg}" } ] }, @@ -93,16 +93,16 @@ } }, "scripts": { - "compile": "gulp compile-extension:image-preview", - "watch": "npm run build-preview && gulp watch-extension:image-preview", + "compile": "gulp compile-extension:media-preview", + "watch": "npm run build-preview && gulp watch-extension:media-preview", "vscode:prepublish": "npm run build-ext", - "build-ext": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:image-preview ./tsconfig.json", + "build-ext": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:media-preview ./tsconfig.json", "compile-web": "npx webpack-cli --config extension-browser.webpack.config --mode none", "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, "dependencies": { "@vscode/extension-telemetry": "0.6.2", - "vscode-nls": "^5.1.0" + "vscode-nls": "^5.2.0" }, "repository": { "type": "git", diff --git a/extensions/image-preview/package.nls.json b/extensions/media-preview/package.nls.json similarity index 67% rename from extensions/image-preview/package.nls.json rename to extensions/media-preview/package.nls.json index 423aaf0c82b..3e45e4d0d2e 100644 --- a/extensions/image-preview/package.nls.json +++ b/extensions/media-preview/package.nls.json @@ -1,6 +1,6 @@ { - "displayName": "Image Preview", - "description": "Provides VS Code's built-in image preview", + "displayName": "Media Preview", + "description": "Provides VS Code's built-in previews for images, audio, and video", "customEditor.audioPreview.displayName": "Audio Preview", "customEditor.imagePreview.displayName": "Image Preview", "customEditor.videoPreview.displayName": "Video Preview", diff --git a/extensions/image-preview/src/audioPreview.ts b/extensions/media-preview/src/audioPreview.ts similarity index 100% rename from extensions/image-preview/src/audioPreview.ts rename to extensions/media-preview/src/audioPreview.ts diff --git a/extensions/image-preview/src/binarySizeStatusBarEntry.ts b/extensions/media-preview/src/binarySizeStatusBarEntry.ts similarity index 100% rename from extensions/image-preview/src/binarySizeStatusBarEntry.ts rename to extensions/media-preview/src/binarySizeStatusBarEntry.ts diff --git a/extensions/image-preview/src/extension.ts b/extensions/media-preview/src/extension.ts similarity index 100% rename from extensions/image-preview/src/extension.ts rename to extensions/media-preview/src/extension.ts diff --git a/extensions/image-preview/src/imagePreview/index.ts b/extensions/media-preview/src/imagePreview/index.ts similarity index 100% rename from extensions/image-preview/src/imagePreview/index.ts rename to extensions/media-preview/src/imagePreview/index.ts diff --git a/extensions/image-preview/src/imagePreview/sizeStatusBarEntry.ts b/extensions/media-preview/src/imagePreview/sizeStatusBarEntry.ts similarity index 100% rename from extensions/image-preview/src/imagePreview/sizeStatusBarEntry.ts rename to extensions/media-preview/src/imagePreview/sizeStatusBarEntry.ts diff --git a/extensions/image-preview/src/imagePreview/zoomStatusBarEntry.ts b/extensions/media-preview/src/imagePreview/zoomStatusBarEntry.ts similarity index 100% rename from extensions/image-preview/src/imagePreview/zoomStatusBarEntry.ts rename to extensions/media-preview/src/imagePreview/zoomStatusBarEntry.ts diff --git a/extensions/image-preview/src/mediaPreview.ts b/extensions/media-preview/src/mediaPreview.ts similarity index 100% rename from extensions/image-preview/src/mediaPreview.ts rename to extensions/media-preview/src/mediaPreview.ts diff --git a/extensions/image-preview/src/ownedStatusBarEntry.ts b/extensions/media-preview/src/ownedStatusBarEntry.ts similarity index 100% rename from extensions/image-preview/src/ownedStatusBarEntry.ts rename to extensions/media-preview/src/ownedStatusBarEntry.ts diff --git a/extensions/image-preview/src/util/dispose.ts b/extensions/media-preview/src/util/dispose.ts similarity index 100% rename from extensions/image-preview/src/util/dispose.ts rename to extensions/media-preview/src/util/dispose.ts diff --git a/extensions/image-preview/src/util/dom.ts b/extensions/media-preview/src/util/dom.ts similarity index 100% rename from extensions/image-preview/src/util/dom.ts rename to extensions/media-preview/src/util/dom.ts diff --git a/extensions/image-preview/src/videoPreview.ts b/extensions/media-preview/src/videoPreview.ts similarity index 100% rename from extensions/image-preview/src/videoPreview.ts rename to extensions/media-preview/src/videoPreview.ts diff --git a/extensions/image-preview/tsconfig.json b/extensions/media-preview/tsconfig.json similarity index 100% rename from extensions/image-preview/tsconfig.json rename to extensions/media-preview/tsconfig.json diff --git a/extensions/image-preview/yarn.lock b/extensions/media-preview/yarn.lock similarity index 92% rename from extensions/image-preview/yarn.lock rename to extensions/media-preview/yarn.lock index 61a6bb946db..a9af1f07de4 100644 --- a/extensions/image-preview/yarn.lock +++ b/extensions/media-preview/yarn.lock @@ -46,7 +46,7 @@ "@microsoft/1ds-core-js" "^3.2.3" "@microsoft/1ds-post-js" "^3.2.3" -vscode-nls@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.1.0.tgz#443b301a7465d88c81c0f4e1914f9857f0dce1e4" - integrity sha512-37Ha44QrLFwR2IfSSYdOArzUvOyoWbOYTwQC+wS0NfqKjhW7s0WQ1lMy5oJXgSZy9sAiZS5ifELhbpXodeMR8w== +vscode-nls@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.2.0.tgz#3cb6893dd9bd695244d8a024bdf746eea665cc3f" + integrity sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng== diff --git a/extensions/merge-conflict/package.json b/extensions/merge-conflict/package.json index 978341508f8..caba136ce52 100644 --- a/extensions/merge-conflict/package.json +++ b/extensions/merge-conflict/package.json @@ -166,7 +166,7 @@ } }, "dependencies": { - "vscode-nls": "^5.1.0" + "vscode-nls": "^5.2.0" }, "devDependencies": { "@types/node": "16.x" diff --git a/extensions/merge-conflict/yarn.lock b/extensions/merge-conflict/yarn.lock index 90475ddc7e0..8b8c7a6c15b 100644 --- a/extensions/merge-conflict/yarn.lock +++ b/extensions/merge-conflict/yarn.lock @@ -7,7 +7,7 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== -vscode-nls@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.1.0.tgz#443b301a7465d88c81c0f4e1914f9857f0dce1e4" - integrity sha512-37Ha44QrLFwR2IfSSYdOArzUvOyoWbOYTwQC+wS0NfqKjhW7s0WQ1lMy5oJXgSZy9sAiZS5ifELhbpXodeMR8w== +vscode-nls@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.2.0.tgz#3cb6893dd9bd695244d8a024bdf746eea665cc3f" + integrity sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng== diff --git a/extensions/microsoft-authentication/package.json b/extensions/microsoft-authentication/package.json index 07f803a5930..1fb6e7d83a5 100644 --- a/extensions/microsoft-authentication/package.json +++ b/extensions/microsoft-authentication/package.json @@ -61,8 +61,7 @@ "sha.js": "2.4.11", "stream": "0.0.2", "uuid": "^8.2.0", - "@vscode/extension-telemetry": "0.6.2", - "vscode-nls": "^5.1.0" + "@vscode/extension-telemetry": "0.6.2" }, "repository": { "type": "git", diff --git a/extensions/microsoft-authentication/src/AADHelper.ts b/extensions/microsoft-authentication/src/AADHelper.ts index 288157ba00d..3a7307a47c0 100644 --- a/extensions/microsoft-authentication/src/AADHelper.ts +++ b/extensions/microsoft-authentication/src/AADHelper.ts @@ -7,7 +7,6 @@ import * as randomBytes from 'randombytes'; import * as querystring from 'querystring'; import { Buffer } from 'buffer'; import * as vscode from 'vscode'; -import * as nls from 'vscode-nls'; import { v4 as uuid } from 'uuid'; import fetch, { Response } from 'node-fetch'; import Logger from './logger'; @@ -17,8 +16,6 @@ import { BetterTokenStorage, IDidChangeInOtherWindowEvent } from './betterSecret import { LoopbackAuthServer } from './authServer'; import path = require('path'); -const localize = nls.loadMessageBundle(); - const redirectUrl = 'https://vscode.dev/redirect'; const loginEndpointUrl = 'https://login.microsoftonline.com/'; const DEFAULT_CLIENT_ID = 'aebc6443-996d-45c2-90f0-388ff96faa56'; @@ -139,7 +136,7 @@ export class AzureActiveDirectoryService { sessionId: session.id }); } else { - vscode.window.showErrorMessage(localize('signOut', "You have been signed out because reading stored authentication information failed.")); + vscode.window.showErrorMessage(vscode.l10n.t('You have been signed out because reading stored authentication information failed.')); Logger.error(e); await this.removeSessionByIToken({ accessToken: undefined, @@ -446,7 +443,7 @@ export class AzureActiveDirectoryService { onDidChangeSessions.fire({ added: [], removed: [], changed: [this.convertToSessionSync(refreshedToken)] }); } catch (e) { if (e.message !== REFRESH_NETWORK_FAILURE) { - vscode.window.showErrorMessage(localize('signOut', "You have been signed out because reading stored authentication information failed.")); + vscode.window.showErrorMessage(vscode.l10n.t('You have been signed out because reading stored authentication information failed.')); await this.removeSessionById(sessionId); } } @@ -683,9 +680,9 @@ export class AzureActiveDirectoryService { private async handleCodeInputBox(inputBox: vscode.InputBox, verifier: string, scopeData: IScopeData): Promise { inputBox.ignoreFocusOut = true; - inputBox.title = localize('pasteCodeTitle', 'Microsoft Authentication'); - inputBox.prompt = localize('pasteCodePrompt', 'Provide the authorization code to complete the sign in flow.'); - inputBox.placeholder = localize('pasteCodePlaceholder', 'Paste authorization code here...'); + inputBox.title = vscode.l10n.t('Microsoft Authentication'); + inputBox.prompt = vscode.l10n.t('Provide the authorization code to complete the sign in flow.'); + inputBox.placeholder = vscode.l10n.t('Paste authorization code here...'); return new Promise((resolve: (value: vscode.AuthenticationSession) => void, reject) => { inputBox.show(); inputBox.onDidAccept(async () => { @@ -829,7 +826,7 @@ export class AzureActiveDirectoryService { } catch (e) { // Network failures will automatically retry on next poll. if (e.message !== REFRESH_NETWORK_FAILURE) { - vscode.window.showErrorMessage(localize('signOut', "You have been signed out because reading stored authentication information failed.")); + vscode.window.showErrorMessage(vscode.l10n.t('You have been signed out because reading stored authentication information failed.')); await this.removeSessionById(session.id); } return; diff --git a/extensions/microsoft-authentication/src/logger.ts b/extensions/microsoft-authentication/src/logger.ts index 92b5876e1b4..38ae6c732d6 100644 --- a/extensions/microsoft-authentication/src/logger.ts +++ b/extensions/microsoft-authentication/src/logger.ts @@ -5,5 +5,5 @@ import * as vscode from 'vscode'; -const Logger = vscode.window.createOutputChannel('Microsoft Authentication', { log: true }); +const Logger = vscode.window.createOutputChannel(vscode.l10n.t('Microsoft Authentication'), { log: true }); export default Logger; diff --git a/extensions/microsoft-authentication/yarn.lock b/extensions/microsoft-authentication/yarn.lock index 6706f6a4aae..d66929ff7f6 100644 --- a/extensions/microsoft-authentication/yarn.lock +++ b/extensions/microsoft-authentication/yarn.lock @@ -198,11 +198,6 @@ uuid@^8.2.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.2.0.tgz#cb10dd6b118e2dada7d0cd9730ba7417c93d920e" integrity sha512-CYpGiFTUrmI6OBMkAdjSDM0k5h8SkkiTP4WAjQgDgNB1S3Ou9VBEvr6q0Kv2H1mMk7IWfxYGpMH5sd5AvcIV2Q== -vscode-nls@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.1.0.tgz#443b301a7465d88c81c0f4e1914f9857f0dce1e4" - integrity sha512-37Ha44QrLFwR2IfSSYdOArzUvOyoWbOYTwQC+wS0NfqKjhW7s0WQ1lMy5oJXgSZy9sAiZS5ifELhbpXodeMR8w== - webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" diff --git a/extensions/notebook-renderers/src/color.ts b/extensions/notebook-renderers/src/color.ts index 2c01cae1071..9ad281d35c4 100644 --- a/extensions/notebook-renderers/src/color.ts +++ b/extensions/notebook-renderers/src/color.ts @@ -866,9 +866,7 @@ export class Color { private _toString?: string; toString(): string { - if (!this._toString) { - this._toString = Color.Format.CSS.format(this); - } + this._toString ??= Color.Format.CSS.format(this); return this._toString; } diff --git a/extensions/npm/package.json b/extensions/npm/package.json index 9cc9ac5dedb..cae1fd7eab1 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -22,7 +22,7 @@ "jsonc-parser": "^2.2.1", "minimatch": "^3.0.4", "request-light": "^0.5.7", - "vscode-nls": "^5.1.0", + "vscode-nls": "^5.2.0", "which": "^2.0.2", "which-pm": "^2.0.0" }, diff --git a/extensions/npm/yarn.lock b/extensions/npm/yarn.lock index 3479f1c58b6..bfd3b4b3de1 100644 --- a/extensions/npm/yarn.lock +++ b/extensions/npm/yarn.lock @@ -192,10 +192,10 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -vscode-nls@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.1.0.tgz#443b301a7465d88c81c0f4e1914f9857f0dce1e4" - integrity sha512-37Ha44QrLFwR2IfSSYdOArzUvOyoWbOYTwQC+wS0NfqKjhW7s0WQ1lMy5oJXgSZy9sAiZS5ifELhbpXodeMR8w== +vscode-nls@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.2.0.tgz#3cb6893dd9bd695244d8a024bdf746eea665cc3f" + integrity sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng== which-pm@^2.0.0: version "2.0.0" diff --git a/extensions/package.json b/extensions/package.json index 8f1df05f4e5..e12225cc76f 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -4,7 +4,7 @@ "license": "MIT", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "4.8.3" + "typescript": "4.8.4" }, "scripts": { "postinstall": "node ./postinstall.mjs" diff --git a/extensions/php-language-features/package.json b/extensions/php-language-features/package.json index 8782b3d83b4..dd7f6445d1a 100644 --- a/extensions/php-language-features/package.json +++ b/extensions/php-language-features/package.json @@ -74,7 +74,7 @@ "watch": "npx gulp watch-extension:php-language-features" }, "dependencies": { - "vscode-nls": "^5.1.0", + "vscode-nls": "^5.2.0", "which": "^2.0.2" }, "devDependencies": { diff --git a/extensions/php-language-features/yarn.lock b/extensions/php-language-features/yarn.lock index 62c8b33e55d..baafbeb7ca3 100644 --- a/extensions/php-language-features/yarn.lock +++ b/extensions/php-language-features/yarn.lock @@ -17,10 +17,10 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= -vscode-nls@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.1.0.tgz#443b301a7465d88c81c0f4e1914f9857f0dce1e4" - integrity sha512-37Ha44QrLFwR2IfSSYdOArzUvOyoWbOYTwQC+wS0NfqKjhW7s0WQ1lMy5oJXgSZy9sAiZS5ifELhbpXodeMR8w== +vscode-nls@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.2.0.tgz#3cb6893dd9bd695244d8a024bdf746eea665cc3f" + integrity sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng== which@^2.0.2: version "2.0.2" diff --git a/extensions/php/snippets/php.code-snippets b/extensions/php/snippets/php.code-snippets index 9c061291647..3c213765b4b 100644 --- a/extensions/php/snippets/php.code-snippets +++ b/extensions/php/snippets/php.code-snippets @@ -12,6 +12,7 @@ }, "PHPDoc class …": { "prefix": "doc_class", + "isFileTemplate": true, "body": [ "/**", " * ${6:undocumented class}", @@ -42,6 +43,7 @@ }, "PHPDoc function …": { "prefix": "doc_f", + "isFileTemplate": true, "body": [ "/**", " * ${1:undocumented function summary}", diff --git a/extensions/python/package.json b/extensions/python/package.json index 6134d05a215..17cbe7787e1 100644 --- a/extensions/python/package.json +++ b/extensions/python/package.json @@ -28,7 +28,6 @@ "py" ], "filenames": [ - "Snakefile", "SConstruct", "SConscript" ], diff --git a/extensions/python/test/colorize-fixtures/test.py b/extensions/python/test/colorize-fixtures/test.py index c92f2c70a8b..c32f12e4f8b 100644 --- a/extensions/python/test/colorize-fixtures/test.py +++ b/extensions/python/test/colorize-fixtures/test.py @@ -19,13 +19,10 @@ class Monkey: }=42): pass -if 1900 < year < 2100 and 1 <= month <= 12 \ - and 1 <= day <= 31 and 0 <= hour < 24 \ - and 0 <= minute < 60 and 0 <= second < 60: # Looks like a valid date - return 1 +pass def firstn(g, n): - for i in range(n): + for _ in range(n): yield g.next() reduce(lambda x,y: x+y, [47,11,42,13]) @@ -37,15 +34,14 @@ mydictionary = { } def steuern(einkommen): - """Berechnung der zu zahlenden Steuern fuer ein zu versteuerndes Einkommen von x""" - if einkommen <= 8004: - steuer = 0 - elif einkommen <= 13469: - y = (einkommen -8004.0)/10000.0 - steuer = (912.17 * y + 1400)*y - else: - steuer = einkommen * 0.44 - 15694 - return steuer + """Berechnung der zu zahlenden Steuern fuer ein zu versteuerndes Einkommen von x""" + if einkommen <= 8004: + return 0 + elif einkommen <= 13469: + y = (einkommen -8004.0)/10000.0 + return (912.17 * y + 1400)*y + else: + return einkommen * 0.44 - 15694 def beliebig(x, y, *mehr): print "x=", x, ", x=", y diff --git a/extensions/razor/build/update-grammar.mjs b/extensions/razor/build/update-grammar.mjs new file mode 100644 index 00000000000..259f4278466 --- /dev/null +++ b/extensions/razor/build/update-grammar.mjs @@ -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. + *--------------------------------------------------------------------------------------------*/ +// @ts-check + +import * as vscodeGrammarUpdater from 'vscode-grammar-updater'; + +function patchGrammar(grammar) { + grammar.scopeName = 'text.html.cshtml'; + return grammar; +} + +const razorGrammarRepo = 'OmniSharp/omnisharp-vscode'; +const grammarPath = 'src/razor/syntaxes/aspnetcorerazor.tmLanguage.json'; +vscodeGrammarUpdater.update(razorGrammarRepo, grammarPath, './syntaxes/cshtml.tmLanguage.json', grammar => patchGrammar(grammar)); + + diff --git a/extensions/razor/cgmanifest.json b/extensions/razor/cgmanifest.json index 2ad1d82c1ea..22f3f066f95 100644 --- a/extensions/razor/cgmanifest.json +++ b/extensions/razor/cgmanifest.json @@ -4,13 +4,37 @@ "component": { "type": "git", "git": { - "name": "demyte/language-cshtml", - "repositoryUrl": "https://github.com/demyte/language-cshtml", - "commitHash": "e6e54d5a86a28cc1e44609a32aaa10a244cd3f81" + "name": "OmniSharp/omnisharp-vscode", + "repositoryUrl": "https://github.com/OmniSharp/omnisharp-vscode", + "commitHash": "85d6eeea14e9aef51934697de78c77fbddbf1aa8" } }, "license": "MIT", - "version": "0.3.0" + "version": "1.24.0", + "licenseDetail": [ + "MIT License", + "", + "Copyright (c) .NET Foundation and Contributors", + "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." + ] } ], "version": 1 diff --git a/extensions/razor/package.json b/extensions/razor/package.json index ace1ed7f959..2312ce86c48 100644 --- a/extensions/razor/package.json +++ b/extensions/razor/package.json @@ -8,6 +8,9 @@ "engines": { "vscode": "0.10.x" }, + "scripts": { + "update-grammar": "node ./build/update-grammar.mjs" + }, "contributes": { "languages": [ { diff --git a/extensions/razor/syntaxes/cshtml.tmLanguage.json b/extensions/razor/syntaxes/cshtml.tmLanguage.json index c662cee2fa3..0f48c15e575 100644 --- a/extensions/razor/syntaxes/cshtml.tmLanguage.json +++ b/extensions/razor/syntaxes/cshtml.tmLanguage.json @@ -1,57 +1,480 @@ { "information_for_contributors": [ - "This file has been converted from https://github.com/demyte/language-cshtml/blob/master/grammars/cshtml.json", + "This file has been converted from https://github.com/OmniSharp/omnisharp-vscode/blob/master/src/razor/syntaxes/aspnetcorerazor.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/demyte/language-cshtml/commit/e6e54d5a86a28cc1e44609a32aaa10a244cd3f81", + "version": "https://github.com/OmniSharp/omnisharp-vscode/commit/85d6eeea14e9aef51934697de78c77fbddbf1aa8", "name": "ASP.NET Razor", "scopeName": "text.html.cshtml", "patterns": [ { - "include": "#razor-directives" - }, - { - "include": "#razor-code-block" - }, - { - "include": "#razor-else-if" - }, - { - "include": "#razor-if" - }, - { - "include": "#razor-else" - }, - { - "include": "#razor-foreach" - }, - { - "include": "#explicit-razor-expression" - }, - { - "include": "#implicit-razor-expression" + "include": "#razor-control-structures" }, { "include": "text.html.basic" } ], "repository": { - "comments": { - "begin": "@\\*", - "captures": { - "0": { - "name": "punctuation.definition.comment.source.cshtml" - } - }, - "end": "\\*@", - "name": "comment.block.cshtml" - }, - "razor-directives": { - "name": "meta.directive.cshtml", + "razor-control-structures": { "patterns": [ { - "include": "#using-directive" + "include": "#razor-comment" + }, + { + "include": "#razor-codeblock" + }, + { + "include": "#explicit-razor-expression" + }, + { + "include": "#escaped-transition" + }, + { + "include": "#directives" + }, + { + "include": "#transitioned-csharp-control-structures" + }, + { + "include": "#implicit-expression" + } + ] + }, + "escaped-transition": { + "name": "constant.character.escape.razor.transition", + "match": "@@" + }, + "transition": { + "match": "@", + "name": "keyword.control.cshtml.transition" + }, + "razor-codeblock": { + "name": "meta.structure.razor.codeblock", + "begin": "(@)(\\{)", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#transition" + } + ] + }, + "2": { + "name": "keyword.control.razor.directive.codeblock.open" + } + }, + "contentName": "source.cs", + "patterns": [ + { + "include": "#razor-codeblock-body" + } + ], + "end": "(\\})", + "endCaptures": { + "1": { + "name": "keyword.control.razor.directive.codeblock.close" + } + } + }, + "razor-codeblock-body": { + "patterns": [ + { + "include": "#text-tag" + }, + { + "include": "#wellformed-html" + }, + { + "include": "#razor-single-line-markup" + }, + { + "include": "#razor-control-structures" + }, + { + "include": "source.cs" + } + ] + }, + "razor-single-line-markup": { + "match": "(\\@\\:)([^$]*)$", + "captures": { + "1": { + "name": "keyword.control.razor.singleLineMarkup" + }, + "2": { + "patterns": [ + { + "include": "#razor-control-structures" + }, + { + "include": "text.html.basic" + } + ] + } + } + }, + "text-tag": { + "begin": "()", + "beginCaptures": { + "1": { + "name": "keyword.control.cshtml.transition.textTag.open" + } + }, + "patterns": [ + { + "include": "#wellformed-html" + }, + { + "include": "$self" + } + ], + "end": "()", + "endCaptures": { + "1": { + "name": "keyword.control.cshtml.transition.textTag.close" + } + } + }, + "razor-comment": { + "name": "meta.comment.razor", + "begin": "(@)(\\*)", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#transition" + } + ] + }, + "2": { + "name": "keyword.control.razor.comment.star" + } + }, + "contentName": "comment.block.razor", + "end": "(\\*)(@)", + "endCaptures": { + "1": { + "name": "keyword.control.razor.comment.star" + }, + "2": { + "patterns": [ + { + "include": "#transition" + } + ] + } + } + }, + "wellformed-html": { + "patterns": [ + { + "include": "#void-tag" + }, + { + "include": "#non-void-tag" + } + ] + }, + "void-tag": { + "name": "meta.tag.structure.$3.void.html", + "begin": "(?i)(<)(!)?(area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)(?=\\s|/?>)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "constant.character.escape.razor.tagHelperOptOut" + }, + "3": { + "name": "entity.name.tag.html" + } + }, + "patterns": [ + { + "include": "text.html.basic#attribute" + } + ], + "end": "/?>", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + } + }, + "non-void-tag": { + "begin": "(?=<(!)?([^/\\s>]+)(\\s|/?>))", + "end": "()|(/>)", + "endCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "name": "punctuation.definition.tag.end.html" + }, + "4": { + "name": "punctuation.definition.tag.end.html" + } + }, + "patterns": [ + { + "begin": "(<)(!)?([^/\\s>]+)(?=\\s|/?>)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "constant.character.escape.razor.tagHelperOptOut" + }, + "3": { + "name": "entity.name.tag.html" + } + }, + "end": "(?=/?>)", + "patterns": [ + { + "include": "#razor-control-structures" + }, + { + "include": "text.html.basic#attribute" + } + ] + }, + { + "begin": ">", + "beginCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "end": "(?=\\{\\}\\)\\]'\"])" + }, + "implicit-expression-body": { + "patterns": [ + { + "include": "#implicit-expression-invocation-start" + }, + { + "include": "#implicit-expression-accessor-start" + } + ], + "end": "(?=[\\s<>\\{\\}\\)\\]'\"])" + }, + "implicit-expression-invocation-start": { + "begin": "([_[:alpha:]][_[:alnum:]]*)(?=\\()", + "beginCaptures": { + "1": { + "name": "entity.name.function.cs" + } + }, + "patterns": [ + { + "include": "#implicit-expression-continuation" + } + ], + "end": "(?=[\\s<>\\{\\}\\)\\]'\"])" + }, + "implicit-expression-accessor-start": { + "begin": "([_[:alpha:]][_[:alnum:]]*)", + "beginCaptures": { + "1": { + "name": "variable.other.object.cs" + } + }, + "patterns": [ + { + "include": "#implicit-expression-continuation" + } + ], + "end": "(?=[\\s<>\\{\\}\\)\\]'\"])" + }, + "implicit-expression-continuation": { + "patterns": [ + { + "include": "#balanced-parenthesis-csharp" + }, + { + "include": "#balanced-brackets-csharp" + }, + { + "include": "#implicit-expression-invocation" + }, + { + "include": "#implicit-expression-accessor" + }, + { + "include": "#implicit-expression-extension" + } + ], + "end": "(?=[\\s<>\\{\\}\\)\\]'\"])" + }, + "implicit-expression-accessor": { + "match": "(?<=\\.)[_[:alpha:]][_[:alnum:]]*", + "name": "variable.other.object.property.cs" + }, + "implicit-expression-invocation": { + "match": "(?<=\\.)[_[:alpha:]][_[:alnum:]]*(?=\\()", + "name": "entity.name.function.cs" + }, + "implicit-expression-operator": { + "patterns": [ + { + "include": "#implicit-expression-dot-operator" + }, + { + "include": "#implicit-expression-null-conditional-operator" + }, + { + "include": "#implicit-expression-null-forgiveness-operator" + } + ] + }, + "implicit-expression-dot-operator": { + "match": "(\\.)(?=[_[:alpha:]][_[:alnum:]]*)", + "captures": { + "1": { + "name": "punctuation.accessor.cs" + } + } + }, + "implicit-expression-null-conditional-operator": { + "match": "(\\?)(?=[.\\[])", + "captures": { + "1": { + "name": "keyword.operator.null-conditional.cs" + } + } + }, + "implicit-expression-null-forgiveness-operator": { + "match": "(\\!)(?=(?:\\.[_[:alpha:]][_[:alnum:]]*)|\\?|[\\[\\(])", + "captures": { + "1": { + "name": "keyword.operator.logical.cs" + } + } + }, + "balanced-parenthesis-csharp": { + "begin": "(\\()", + "beginCaptures": { + "1": { + "name": "punctuation.parenthesis.open.cs" + } + }, + "name": "razor.test.balanced.parenthesis", + "patterns": [ + { + "include": "source.cs" + } + ], + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.parenthesis.close.cs" + } + } + }, + "balanced-brackets-csharp": { + "begin": "(\\[)", + "beginCaptures": { + "1": { + "name": "punctuation.squarebracket.open.cs" + } + }, + "name": "razor.test.balanced.brackets", + "patterns": [ + { + "include": "source.cs" + } + ], + "end": "(\\])", + "endCaptures": { + "1": { + "name": "punctuation.squarebracket.close.cs" + } + } + }, + "directives": { + "patterns": [ + { + "include": "#code-directive" + }, + { + "include": "#functions-directive" + }, + { + "include": "#page-directive" + }, + { + "include": "#addTagHelper-directive" + }, + { + "include": "#removeTagHelper-directive" + }, + { + "include": "#tagHelperPrefix-directive" }, { "include": "#model-directive" @@ -59,283 +482,1030 @@ { "include": "#inherits-directive" }, + { + "include": "#implements-directive" + }, + { + "include": "#namespace-directive" + }, { "include": "#inject-directive" }, { - "include": "#implements-directive" + "include": "#attribute-directive" + }, + { + "include": "#section-directive" }, { "include": "#layout-directive" }, { - "include": "#page-directive" - }, - { - "include": "#functions-directive" + "include": "#using-directive" } ] }, - "explicit-razor-expression": { - "name": "meta.expression.explicit.cshtml", - "begin": "(@)\\(", - "captures": { - "0": { - "name": "keyword.control.cshtml" + "code-directive": { + "begin": "(@)(code)\\s*", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#transition" + } + ] + }, + "2": { + "name": "keyword.control.razor.directive.code" } }, "patterns": [ { - "include": "source.cs" + "include": "#directive-codeblock" } ], - "end": "\\)" - }, - "implicit-razor-expression": { - "name": "meta.expression.implicit.cshtml", - "begin": "(@)([a-zA-Z0-9\\.\\_\\(\\)]+)", - "captures": { - "0": { - "name": "keyword.control.cshtml" - } - }, - "end": "$" - }, - "using-directive": { - "name": "meta.directive.using.cshtml", - "begin": "(@using)\\s+", - "captures": { - "0": { - "name": "keyword.control.cshtml" - } - }, - "patterns": [ - { - "include": "#csharp-namespace-identifier" - } - ], - "end": "$" - }, - "model-directive": { - "name": "meta.directive.model.cshtml", - "begin": "(@model)\\s+", - "captures": { - "0": { - "name": "keyword.control.cshtml" - } - }, - "patterns": [ - { - "include": "#csharp-type-name" - } - ], - "end": "$" - }, - "inherits-directive": { - "name": "meta.directive.inherits.cshtml", - "begin": "(@inherits)\\s+", - "captures": { - "0": { - "name": "keyword.control.cshtml" - } - }, - "patterns": [ - { - "include": "#csharp-type-name" - } - ], - "end": "$" - }, - "inject-directive": { - "name": "meta.directive.inject.cshtml", - "begin": "(@inject)\\s+", - "captures": { - "0": { - "name": "keyword.control.cshtml" - } - }, - "patterns": [ - { - "include": "#csharp-type-name" - } - ], - "end": "$" - }, - "implements-directive": { - "name": "meta.directive.implements.cshtml", - "begin": "(@implements)\\s+", - "captures": { - "0": { - "name": "keyword.control.cshtml" - } - }, - "patterns": [ - { - "include": "#csharp-type-name" - } - ], - "end": "$" - }, - "layout-directive": { - "name": "meta.directive.layout.cshtml", - "begin": "(@layout)\\s+", - "captures": { - "0": { - "name": "keyword.control.cshtml" - } - }, - "patterns": [ - { - "include": "#csharp-type-name" - } - ], - "end": "$" - }, - "page-directive": { - "name": "meta.directive.page.cshtml", - "begin": "(@page)\\s+", - "captures": { - "0": { - "name": "keyword.control.cshtml" - } - }, - "patterns": [ - { - "include": "source.cs" - } - ], - "end": "$" + "end": "(?<=})|\\s" }, "functions-directive": { - "name": "meta.directive.functions.cshtml", - "match": "(@functions)", - "captures": { - "0": { - "name": "keyword.control.cshtml" + "begin": "(@)(functions)\\s*", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#transition" + } + ] + }, + "2": { + "name": "keyword.control.razor.directive.functions" + } + }, + "patterns": [ + { + "include": "#directive-codeblock" + } + ], + "end": "(?<=})|\\s" + }, + "directive-codeblock": { + "begin": "(\\{)", + "beginCaptures": { + "1": { + "name": "keyword.control.razor.directive.codeblock.open" + } + }, + "name": "meta.structure.razor.directive.codeblock", + "contentName": "source.cs", + "patterns": [ + { + "include": "source.cs" + } + ], + "end": "(\\})", + "endCaptures": { + "1": { + "name": "keyword.control.razor.directive.codeblock.close" } } }, - "razor-if": { - "begin": "(@if)", + "page-directive": { + "name": "meta.directive", + "match": "(@)(page)\\s+([^$]+)?", "captures": { - "0": { - "name": "keyword.control.cshtml" + "1": { + "patterns": [ + { + "include": "#transition" + } + ] + }, + "2": { + "name": "keyword.control.razor.directive.page" + }, + "3": { + "patterns": [ + { + "include": "source.cs#string-literal" + } + ] } - }, - "patterns": [ - { - "include": "source.cs" - } - ], - "end": "$" + } }, - "razor-else": { - "begin": "(else)", + "addTagHelper-directive": { + "name": "meta.directive", + "match": "(@)(addTagHelper)\\s+([^$]+)?", "captures": { - "0": { - "name": "keyword.control.cshtml" + "1": { + "patterns": [ + { + "include": "#transition" + } + ] + }, + "2": { + "name": "keyword.control.razor.directive.addTagHelper" + }, + "3": { + "patterns": [ + { + "include": "#tagHelper-directive-argument" + } + ] } - }, - "patterns": [ - { - "include": "source.cs" - } - ], - "end": "$" + } }, - "razor-else-if": { - "begin": "(else\\s+if)", + "removeTagHelper-directive": { + "name": "meta.directive", + "match": "(@)(removeTagHelper)\\s+([^$]+)?", "captures": { - "0": { - "name": "keyword.control.cshtml" + "1": { + "patterns": [ + { + "include": "#transition" + } + ] + }, + "2": { + "name": "keyword.control.razor.directive.removeTagHelper" + }, + "3": { + "patterns": [ + { + "include": "#tagHelper-directive-argument" + } + ] } - }, - "patterns": [ - { - "include": "source.cs" - } - ], - "end": "$" + } }, - "razor-foreach": { - "begin": "(@foreach)\\s+\\(", + "tagHelperPrefix-directive": { + "name": "meta.directive", + "match": "(@)(tagHelperPrefix)\\s+([^$]+)?", "captures": { - "0": { - "name": "keyword.control.cshtml" + "1": { + "patterns": [ + { + "include": "#transition" + } + ] + }, + "2": { + "name": "keyword.control.razor.directive.tagHelperPrefix" + }, + "3": { + "patterns": [ + { + "include": "#tagHelper-directive-argument" + } + ] } - }, - "patterns": [ - { - "include": "source.cs" - } - ], - "end": "\\)" + } }, - "razor-code-block": { - "begin": "@?\\{", - "captures": { - "0": { - "name": "keyword.control.cshtml" - } - }, + "tagHelper-directive-argument": { "patterns": [ { - "include": "text.html.cshtml" + "include": "source.cs#string-literal" }, { - "include": "source.cs" - } - ], - "end": "\\}" - }, - "csharp-namespace-identifier": { - "patterns": [ - { - "name": "entity.name.type.namespace.cs", - "match": "[_[:alpha:]][_[:alnum:]]*" + "include": "#unquoted-string-argument" } ] }, - "csharp-type-name": { + "unquoted-string-argument": { + "name": "string.quoted.double.cs", + "match": "[^$]+" + }, + "model-directive": { + "name": "meta.directive", + "match": "(@)(model)\\s+([^$]+)?", + "captures": { + "1": { + "patterns": [ + { + "include": "#transition" + } + ] + }, + "2": { + "name": "keyword.control.razor.directive.model" + }, + "3": { + "patterns": [ + { + "include": "source.cs#type" + } + ] + } + } + }, + "inherits-directive": { + "name": "meta.directive", + "match": "(@)(inherits)\\s+([^$]+)?", + "captures": { + "1": { + "patterns": [ + { + "include": "#transition" + } + ] + }, + "2": { + "name": "keyword.control.razor.directive.inherits" + }, + "3": { + "patterns": [ + { + "include": "source.cs#type" + } + ] + } + } + }, + "implements-directive": { + "name": "meta.directive", + "match": "(@)(implements)\\s+([^$]+)?", + "captures": { + "1": { + "patterns": [ + { + "include": "#transition" + } + ] + }, + "2": { + "name": "keyword.control.razor.directive.implements" + }, + "3": { + "patterns": [ + { + "include": "source.cs#type" + } + ] + } + } + }, + "layout-directive": { + "name": "meta.directive", + "match": "(@)(layout)\\s+([^$]+)?", + "captures": { + "1": { + "patterns": [ + { + "include": "#transition" + } + ] + }, + "2": { + "name": "keyword.control.razor.directive.layout" + }, + "3": { + "patterns": [ + { + "include": "source.cs#type" + } + ] + } + } + }, + "namespace-directive": { + "name": "meta.directive", + "match": "(@)(namespace)\\s+([^\\s]+)?", + "captures": { + "1": { + "patterns": [ + { + "include": "#transition" + } + ] + }, + "2": { + "name": "keyword.control.razor.directive.namespace" + }, + "3": { + "patterns": [ + { + "include": "#namespace-directive-argument" + } + ] + } + } + }, + "namespace-directive-argument": { + "match": "([_[:alpha:]][_[:alnum:]]*)(\\.)?", + "captures": { + "1": { + "name": "entity.name.type.namespace.cs" + }, + "2": { + "name": "punctuation.accessor.cs" + } + } + }, + "inject-directive": { + "name": "meta.directive", + "match": "(@)(inject)\\s*([\\S\\s]+?)?\\s*([_[:alpha:]][_[:alnum:]]*)?\\s*(?=$)", + "captures": { + "1": { + "patterns": [ + { + "include": "#transition" + } + ] + }, + "2": { + "name": "keyword.control.razor.directive.inject" + }, + "3": { + "patterns": [ + { + "include": "source.cs#type" + } + ] + }, + "4": { + "name": "entity.name.variable.property.cs" + } + } + }, + "attribute-directive": { + "name": "meta.directive", + "begin": "(@)(attribute)\\b\\s+", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#transition" + } + ] + }, + "2": { + "name": "keyword.control.razor.directive.attribute" + } + }, "patterns": [ { - "match": "([_[:alpha:]][_[:alnum:]]*)\\s*(\\:\\:)", - "captures": { - "1": { - "name": "entity.name.type.alias.cs" - }, - "2": { - "name": "punctuation.separator.coloncolon.cs" + "include": "source.cs#attribute-section" + } + ], + "end": "(?<=\\])|$" + }, + "section-directive": { + "name": "meta.directive.block", + "begin": "(@)(section)\\b\\s+([_[:alpha:]][_[:alnum:]]*)?", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#transition" } - } + ] + }, + "2": { + "name": "keyword.control.razor.directive.section" + }, + "3": { + "name": "variable.other.razor.directive.sectionName" + } + }, + "patterns": [ + { + "include": "#directive-markupblock" + } + ], + "end": "(?<=})" + }, + "directive-markupblock": { + "name": "meta.structure.razor.directive.markblock", + "begin": "(\\{)", + "beginCaptures": { + "1": { + "name": "keyword.control.razor.directive.codeblock.open" + } + }, + "patterns": [ + { + "include": "$self" + } + ], + "end": "(\\})", + "endCaptures": { + "1": { + "name": "keyword.control.razor.directive.codeblock.close" + } + } + }, + "using-directive": { + "name": "meta.directive", + "match": "(@)(using)\\b\\s+(?!\\(|\\s)(.+?)?(;)?$", + "captures": { + "1": { + "patterns": [ + { + "include": "#transition" + } + ] + }, + "2": { + "name": "keyword.other.using.cs" + }, + "3": { + "patterns": [ + { + "include": "#using-static-directive" + }, + { + "include": "#using-alias-directive" + }, + { + "include": "#using-standard-directive" + } + ] + }, + "4": { + "name": "keyword.control.razor.optionalSemicolon" + } + } + }, + "using-static-directive": { + "match": "(static)\\b\\s+(.+)", + "captures": { + "1": { + "name": "keyword.other.static.cs" + }, + "2": { + "patterns": [ + { + "include": "source.cs#type" + } + ] + } + } + }, + "using-alias-directive": { + "match": "([_[:alpha:]][_[:alnum:]]*)\\b\\s*(=)\\s*(.+)\\s*", + "captures": { + "1": { + "name": "entity.name.type.alias.cs" + }, + "2": { + "name": "keyword.operator.assignment.cs" + }, + "3": { + "patterns": [ + { + "include": "source.cs#type" + } + ] + } + } + }, + "using-standard-directive": { + "match": "([_[:alpha:]][_[:alnum:]]*)\\s*", + "captures": { + "1": { + "name": "entity.name.type.namespace.cs" + } + } + }, + "transitioned-csharp-control-structures": { + "patterns": [ + { + "include": "#using-statement" }, { - "match": "([_[:alpha:]][_[:alnum:]]*)\\s*(\\.)", - "captures": { - "1": { - "name": "storage.type.cs" - }, - "2": { - "name": "punctuation.accessor.cs" - } - } + "include": "#if-statement" }, { - "match": "(\\.)\\s*([_[:alpha:]][_[:alnum:]]*)", - "captures": { - "1": { - "name": "punctuation.accessor.cs" - }, - "2": { - "name": "storage.type.cs" - } - } + "include": "#else-part" }, { - "name": "storage.type.cs", - "match": "[_[:alpha:]][_[:alnum:]]*" + "include": "#foreach-statement" + }, + { + "include": "#for-statement" + }, + { + "include": "#while-statement" + }, + { + "include": "#switch-statement" + }, + { + "include": "#lock-statement" + }, + { + "include": "#do-statement" + }, + { + "include": "#try-statement" } ] + }, + "using-statement": { + "name": "meta.statement.using.razor", + "begin": "(?:^\\s*|(@))(using)\\b\\s*(?=\\()", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#transition" + } + ] + }, + "2": { + "name": "keyword.other.using.cs" + } + }, + "patterns": [ + { + "include": "#csharp-condition" + }, + { + "include": "#csharp-code-block" + }, + { + "include": "#razor-codeblock-body" + } + ], + "end": "(?<=})" + }, + "if-statement": { + "name": "meta.statement.if.razor", + "begin": "(?:^\\s*|(@))(if)\\b\\s*(?=\\()", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#transition" + } + ] + }, + "2": { + "name": "keyword.control.conditional.if.cs" + } + }, + "patterns": [ + { + "include": "#csharp-condition" + }, + { + "include": "#csharp-code-block" + }, + { + "include": "#razor-codeblock-body" + } + ], + "end": "(?<=})" + }, + "else-part": { + "name": "meta.statement.else.razor", + "begin": "(?:^|(?<=}))\\s*(else)\\b\\s*?(?: (if))?\\s*?(?=[\\n\\(\\{])", + "beginCaptures": { + "1": { + "name": "keyword.control.conditional.else.cs" + }, + "2": { + "name": "keyword.control.conditional.if.cs" + } + }, + "patterns": [ + { + "include": "#csharp-condition" + }, + { + "include": "#csharp-code-block" + }, + { + "include": "#razor-codeblock-body" + } + ], + "end": "(?<=})" + }, + "for-statement": { + "name": "meta.statement.for.razor", + "begin": "(?:^\\s*|(@))(for)\\b\\s*(?=\\()", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#transition" + } + ] + }, + "2": { + "name": "keyword.control.loop.for.cs" + } + }, + "patterns": [ + { + "include": "#csharp-condition" + }, + { + "include": "#csharp-code-block" + }, + { + "include": "#razor-codeblock-body" + } + ], + "end": "(?<=})" + }, + "foreach-statement": { + "name": "meta.statement.foreach.razor", + "begin": "(?:^\\s*|(@)(await\\s+)?)(foreach)\\b\\s*(?=\\()", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#transition" + } + ] + }, + "2": { + "patterns": [ + { + "include": "#await-prefix" + } + ] + }, + "3": { + "name": "keyword.control.loop.foreach.cs" + } + }, + "patterns": [ + { + "include": "#foreach-condition" + }, + { + "include": "#csharp-code-block" + }, + { + "include": "#razor-codeblock-body" + } + ], + "end": "(?<=})" + }, + "foreach-condition": { + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "punctuation.parenthesis.open.cs" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.parenthesis.close.cs" + } + }, + "patterns": [ + { + "match": "(?x)\n(?:\n (\\bvar\\b)|\n (?\n (?:\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\\b(in)\\b", + "captures": { + "1": { + "name": "keyword.other.var.cs" + }, + "2": { + "patterns": [ + { + "include": "source.cs#type" + } + ] + }, + "7": { + "name": "entity.name.variable.local.cs" + }, + "8": { + "name": "keyword.control.loop.in.cs" + } + } + }, + { + "match": "(?x) # match foreach (var (x, y) in ...)\n(?:\\b(var)\\b\\s*)?\n(?\\((?:[^\\(\\)]|\\g)+\\))\\s+\n\\b(in)\\b", + "captures": { + "1": { + "name": "keyword.other.var.cs" + }, + "2": { + "patterns": [ + { + "include": "source.cs#tuple-declaration-deconstruction-element-list" + } + ] + }, + "3": { + "name": "keyword.control.loop.in.cs" + } + } + }, + { + "include": "source.cs#expression" + } + ] + }, + "do-statement": { + "name": "meta.statement.do.razor", + "begin": "(?:^\\s*|(@))(do)\\b\\s*", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#transition" + } + ] + }, + "2": { + "name": "keyword.control.loop.do.cs" + } + }, + "patterns": [ + { + "include": "#csharp-condition" + }, + { + "include": "#csharp-code-block" + }, + { + "include": "#razor-codeblock-body" + } + ], + "end": "(?<=})" + }, + "while-statement": { + "name": "meta.statement.while.razor", + "begin": "(?:(@)|^\\s*|(?<=})\\s*)(while)\\b\\s*(?=\\()", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#transition" + } + ] + }, + "2": { + "name": "keyword.control.loop.while.cs" + } + }, + "patterns": [ + { + "include": "#csharp-condition" + }, + { + "include": "#csharp-code-block" + }, + { + "include": "#razor-codeblock-body" + } + ], + "end": "(?<=})|(;)", + "endCaptures": { + "1": { + "name": "punctuation.terminator.statement.cs" + } + } + }, + "switch-statement": { + "name": "meta.statement.switch.razor", + "begin": "(?:^\\s*|(@))(switch)\\b\\s*(?=\\()", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#transition" + } + ] + }, + "2": { + "name": "keyword.control.switch.cs" + } + }, + "patterns": [ + { + "include": "#csharp-condition" + }, + { + "include": "#switch-code-block" + }, + { + "include": "#razor-codeblock-body" + } + ], + "end": "(?<=})" + }, + "switch-code-block": { + "name": "meta.structure.razor.csharp.codeblock.switch", + "begin": "(\\{)", + "beginCaptures": { + "1": { + "name": "punctuation.curlybrace.open.cs" + } + }, + "patterns": [ + { + "include": "source.cs#switch-label" + }, + { + "include": "#razor-codeblock-body" + } + ], + "end": "(\\})", + "endCaptures": { + "1": { + "name": "punctuation.curlybrace.close.cs" + } + } + }, + "lock-statement": { + "name": "meta.statement.lock.razor", + "begin": "(?:^\\s*|(@))(lock)\\b\\s*(?=\\()", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#transition" + } + ] + }, + "2": { + "name": "keyword.other.lock.cs" + } + }, + "patterns": [ + { + "include": "#csharp-condition" + }, + { + "include": "#csharp-code-block" + }, + { + "include": "#razor-codeblock-body" + } + ], + "end": "(?<=})" + }, + "try-statement": { + "patterns": [ + { + "include": "#try-block" + }, + { + "include": "#catch-clause" + }, + { + "include": "#finally-clause" + } + ] + }, + "try-block": { + "name": "meta.statement.try.razor", + "begin": "(?:^\\s*|(@))(try)\\b\\s*", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#transition" + } + ] + }, + "2": { + "name": "keyword.control.try.cs" + } + }, + "patterns": [ + { + "include": "#csharp-condition" + }, + { + "include": "#csharp-code-block" + }, + { + "include": "#razor-codeblock-body" + } + ], + "end": "(?<=})" + }, + "catch-clause": { + "name": "meta.statement.catch.razor", + "begin": "(?:^|(?<=}))\\s*(catch)\\b\\s*?(?=[\\n\\(\\{])", + "beginCaptures": { + "1": { + "name": "keyword.control.try.catch.cs" + } + }, + "patterns": [ + { + "include": "#catch-condition" + }, + { + "include": "source.cs#when-clause" + }, + { + "include": "#csharp-code-block" + }, + { + "include": "#razor-codeblock-body" + } + ], + "end": "(?<=})" + }, + "catch-condition": { + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "punctuation.parenthesis.open.cs" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.parenthesis.close.cs" + } + }, + "patterns": [ + { + "match": "(?x)\n(?\n (?:\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)\\s*\n(?:(\\g)\\b)?", + "captures": { + "1": { + "patterns": [ + { + "include": "source.cs#type" + } + ] + }, + "6": { + "name": "entity.name.variable.local.cs" + } + } + } + ] + }, + "finally-clause": { + "name": "meta.statement.finally.razor", + "begin": "(?:^|(?<=}))\\s*(finally)\\b\\s*?(?=[\\n\\{])", + "beginCaptures": { + "1": { + "name": "keyword.control.try.finally.cs" + } + }, + "patterns": [ + { + "include": "#csharp-code-block" + }, + { + "include": "#razor-codeblock-body" + } + ], + "end": "(?<=})" + }, + "await-prefix": { + "name": "keyword.other.await.cs", + "match": "(await)\\s+" + }, + "csharp-code-block": { + "name": "meta.structure.razor.csharp.codeblock", + "begin": "(\\{)", + "beginCaptures": { + "1": { + "name": "punctuation.curlybrace.open.cs" + } + }, + "patterns": [ + { + "include": "#razor-codeblock-body" + } + ], + "end": "(\\})", + "endCaptures": { + "1": { + "name": "punctuation.curlybrace.close.cs" + } + } + }, + "csharp-condition": { + "begin": "(\\()", + "beginCaptures": { + "1": { + "name": "punctuation.parenthesis.open.cs" + } + }, + "patterns": [ + { + "include": "source.cs#local-variable-declaration" + }, + { + "include": "source.cs#expression" + }, + { + "include": "source.cs#punctuation-comma" + }, + { + "include": "source.cs#punctuation-semicolon" + } + ], + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.parenthesis.close.cs" + } + } } } } \ No newline at end of file diff --git a/extensions/references-view/package.json b/extensions/references-view/package.json index 4176cda3d1a..d3fcadcbffd 100644 --- a/extensions/references-view/package.json +++ b/extensions/references-view/package.json @@ -407,7 +407,7 @@ "watch": "npx gulp watch-extension:references-view" }, "dependencies": { - "vscode-nls": "^5.1.0" + "vscode-nls": "^5.2.0" }, "devDependencies": { "@types/node": "16.x" diff --git a/extensions/references-view/yarn.lock b/extensions/references-view/yarn.lock index 76eaba8dd6f..1661be7c5d2 100644 --- a/extensions/references-view/yarn.lock +++ b/extensions/references-view/yarn.lock @@ -7,7 +7,7 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.33.tgz#566713b1b626f781c5c58fe3531307283e00720c" integrity sha512-0PJ0vg+JyU0MIan58IOIFRtSvsb7Ri+7Wltx2qAg94eMOrpg4+uuP3aUHCpxXc1i0jCXiC+zIamSZh3l9AbcQA== -vscode-nls@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.1.0.tgz#443b301a7465d88c81c0f4e1914f9857f0dce1e4" - integrity sha512-37Ha44QrLFwR2IfSSYdOArzUvOyoWbOYTwQC+wS0NfqKjhW7s0WQ1lMy5oJXgSZy9sAiZS5ifELhbpXodeMR8w== +vscode-nls@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.2.0.tgz#3cb6893dd9bd695244d8a024bdf746eea665cc3f" + integrity sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng== diff --git a/extensions/scss/cgmanifest.json b/extensions/scss/cgmanifest.json index a67a4f54609..12247769ce2 100644 --- a/extensions/scss/cgmanifest.json +++ b/extensions/scss/cgmanifest.json @@ -6,12 +6,12 @@ "git": { "name": "atom/language-sass", "repositoryUrl": "https://github.com/atom/language-sass", - "commitHash": "303bbf0c250fe380b9e57375598cfd916110758b" + "commitHash": "f52ab12f7f9346cc2568129d8c4419bd3d506b47" } }, "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.61.4" + "version": "0.62.1" } ], "version": 1 diff --git a/extensions/shared.webpack.config.js b/extensions/shared.webpack.config.js index a8f24c1a572..8be35296ddf 100644 --- a/extensions/shared.webpack.config.js +++ b/extensions/shared.webpack.config.js @@ -113,15 +113,6 @@ function withBrowserDefaults(/**@type WebpackConfig*/extConfig, /** @type Additi test: /\.ts$/, exclude: /node_modules/, use: [ - // TODO: bring this back once vscode-nls-dev supports browser - // { - // // vscode-nls-dev loader: - // // * rewrite nls-calls - // loader: 'vscode-nls-dev/lib/webpack-loader', - // options: { - // base: path.join(extConfig.context, 'src') - // } - // }, { // configure TypeScript loader: // * enable sources maps for end-to-end source maps @@ -137,7 +128,6 @@ function withBrowserDefaults(/**@type WebpackConfig*/extConfig, /** @type Additi }, externals: { 'vscode': 'commonjs vscode', // ignored because it doesn't exist, - 'vscode-nls-web-data': 'commonjs vscode-nls-web-data', // ignored because this is injected by the webworker extension host 'applicationinsights-native-metrics': 'commonjs applicationinsights-native-metrics', // ignored because we don't ship native module '@opentelemetry/tracing': 'commonjs @opentelemetry/tracing' // ignored because we don't ship this module }, diff --git a/extensions/simple-browser/package.json b/extensions/simple-browser/package.json index fe3f4de2a5f..bcd83d483d3 100644 --- a/extensions/simple-browser/package.json +++ b/extensions/simple-browser/package.json @@ -11,7 +11,7 @@ "license": "MIT", "aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255", "engines": { - "vscode": "^1.53.0" + "vscode": "^1.70.0" }, "main": "./out/extension", "browser": "./dist/browser/extension", @@ -68,7 +68,7 @@ }, "dependencies": { "@vscode/extension-telemetry": "0.6.2", - "vscode-nls": "^5.1.0" + "vscode-nls": "^5.2.0" }, "devDependencies": { "@types/vscode-webview": "^1.57.0", diff --git a/extensions/simple-browser/src/extension.ts b/extensions/simple-browser/src/extension.ts index 63131a43bcf..6d3ded5131b 100644 --- a/extensions/simple-browser/src/extension.ts +++ b/extensions/simple-browser/src/extension.ts @@ -23,18 +23,15 @@ const enabledHosts = new Set([ // localhost IPv4 '127.0.0.1', // localhost IPv6 - '0:0:0:0:0:0:0:1', - '::1', + '[0:0:0:0:0:0:0:1]', + '[::1]', // all interfaces IPv4 '0.0.0.0', // all interfaces IPv6 - '0:0:0:0:0:0:0:0', - '::' + '[0:0:0:0:0:0:0:0]', + '[::]' ]); -const IPv6Localhost = /0\:0\:0\:0\:0\:0\:0\:1|\:\:1/; -const IPv6AllInterfaces = /0\:0\:0\:0\:0\:0\:0\:0|\:\:/; - const openerId = 'simpleBrowser.open'; export function activate(context: vscode.ExtensionContext) { @@ -65,13 +62,13 @@ export function activate(context: vscode.ExtensionContext) { preserveFocus?: boolean; viewColumn: vscode.ViewColumn; }) => { - manager.show(url.toString(), showOptions); + manager.show(url, showOptions); })); context.subscriptions.push(vscode.window.registerExternalUriOpener(openerId, { canOpenExternalUri(uri: vscode.Uri) { // We have to replace the IPv6 hosts with IPv4 because URL can't handle IPv6. - const originalUri = new URL(uri.toString().replace(IPv6Localhost, '127.0.0.1').replace(IPv6AllInterfaces, '0.0.0.0')); + const originalUri = new URL(uri.toString(true)); if (enabledHosts.has(originalUri.hostname)) { return isWeb() ? vscode.ExternalUriOpenerPriority.Default @@ -81,7 +78,7 @@ export function activate(context: vscode.ExtensionContext) { return vscode.ExternalUriOpenerPriority.None; }, openExternalUri(resolveUri: vscode.Uri) { - return manager.show(resolveUri.toString(), { + return manager.show(resolveUri, { viewColumn: vscode.window.activeTextEditor ? vscode.ViewColumn.Beside : vscode.ViewColumn.Active }); } diff --git a/extensions/simple-browser/src/simpleBrowserManager.ts b/extensions/simple-browser/src/simpleBrowserManager.ts index 9d59c1816ab..ef485916411 100644 --- a/extensions/simple-browser/src/simpleBrowserManager.ts +++ b/extensions/simple-browser/src/simpleBrowserManager.ts @@ -19,7 +19,8 @@ export class SimpleBrowserManager { this._activeView = undefined; } - public show(url: string, options?: ShowOptions): void { + public show(inputUri: string | vscode.Uri, options?: ShowOptions): void { + const url = typeof inputUri === 'string' ? inputUri : inputUri.toString(true); if (this._activeView) { this._activeView.show(url, options); } else { @@ -34,7 +35,7 @@ export class SimpleBrowserManager { const url = state?.url ?? ''; const view = SimpleBrowserView.restore(this.extensionUri, url, panel); this.registerWebviewListeners(view); - return; + this._activeView ??= view; } private registerWebviewListeners(view: SimpleBrowserView) { @@ -46,4 +47,3 @@ export class SimpleBrowserManager { } } - diff --git a/extensions/simple-browser/yarn.lock b/extensions/simple-browser/yarn.lock index 32215e9059b..97902582dea 100644 --- a/extensions/simple-browser/yarn.lock +++ b/extensions/simple-browser/yarn.lock @@ -56,7 +56,7 @@ vscode-codicons@^0.0.14: resolved "https://registry.yarnpkg.com/vscode-codicons/-/vscode-codicons-0.0.14.tgz#e0d05418e2e195564ff6f6a2199d70415911c18f" integrity sha512-6CEH5KT9ct5WMw7n5dlX7rB8ya4CUI2FSq1Wk36XaW+c5RglFtAanUV0T+gvZVVFhl/WxfjTvFHq06Hz9c1SLA== -vscode-nls@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.1.0.tgz#443b301a7465d88c81c0f4e1914f9857f0dce1e4" - integrity sha512-37Ha44QrLFwR2IfSSYdOArzUvOyoWbOYTwQC+wS0NfqKjhW7s0WQ1lMy5oJXgSZy9sAiZS5ifELhbpXodeMR8w== +vscode-nls@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.2.0.tgz#3cb6893dd9bd695244d8a024bdf746eea665cc3f" + integrity sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng== diff --git a/extensions/theme-abyss/themes/abyss-color-theme.json b/extensions/theme-abyss/themes/abyss-color-theme.json index 3157d39b234..7ce14d2de22 100644 --- a/extensions/theme-abyss/themes/abyss-color-theme.json +++ b/extensions/theme-abyss/themes/abyss-color-theme.json @@ -409,6 +409,7 @@ // "activityBar.foreground": "", // "activityBarBadge.background": "", // "activityBarBadge.foreground": "", + "activityBarItem.settingsProfilesBackground": "#082877", // Workbench: Panel // "panel.background": "", diff --git a/extensions/theme-defaults/themes/light_vs.json b/extensions/theme-defaults/themes/light_vs.json index 7a80ebf00e0..9a5092043d9 100644 --- a/extensions/theme-defaults/themes/light_vs.json +++ b/extensions/theme-defaults/themes/light_vs.json @@ -2,6 +2,7 @@ "$schema": "vscode://schemas/color-theme", "name": "Light (Visual Studio)", "colors": { + "activityBarItem.settingsProfilesBackground": "#4d4d4d", "editor.background": "#FFFFFF", "editor.foreground": "#000000", "editor.inactiveSelectionBackground": "#E5EBF1", @@ -103,7 +104,7 @@ { "scope": "entity.other.attribute-name", "settings": { - "foreground": "#ff0000" + "foreground": "#e50000" } }, { @@ -328,7 +329,7 @@ "source.coffee.embedded" ], "settings": { - "foreground": "#ff0000" + "foreground": "#e50000" } }, { 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 76cdb8a9753..8208d90f102 100644 --- a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json +++ b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json @@ -32,6 +32,7 @@ "ports.iconRunningProcessForeground": "#369432", "activityBar.background": "#221a0f", "activityBar.foreground": "#d3af86", + "activityBarItem.settingsProfilesBackground": "#47351d", "sideBar.background": "#362712", "menu.background": "#362712", "menu.foreground": "#CCCCCC", diff --git a/extensions/theme-seti/build/update-icon-theme.js b/extensions/theme-seti/build/update-icon-theme.js index c55af7ab7fe..ab2a1c81e81 100644 --- a/extensions/theme-seti/build/update-icon-theme.js +++ b/extensions/theme-seti/build/update-icon-theme.js @@ -47,7 +47,11 @@ const inheritIconFromLanguage = { "postcss": 'css', "django-html": 'html', "blade": 'php' -} +}; + +const ignoreExtAssociation = { + "properties": true +}; const FROM_DISK = true; // set to true to take content from a repo checked out next to the vscode repo @@ -399,7 +403,7 @@ exports.update = function () { if (!nonBuiltInLanguages[lang] && !inheritIconFromLanguage[lang]) { for (let i2 = 0; i2 < exts.length; i2++) { // remove the extension association, unless it is different from the preferred - if (ext2Def[exts[i2]] === preferredDef) { + if (ext2Def[exts[i2]] === preferredDef || ignoreExtAssociation[exts[i2]]) { delete ext2Def[exts[i2]]; } } diff --git a/extensions/theme-seti/cgmanifest.json b/extensions/theme-seti/cgmanifest.json index 919b27b7c91..b2b36944416 100644 --- a/extensions/theme-seti/cgmanifest.json +++ b/extensions/theme-seti/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "seti-ui", "repositoryUrl": "https://github.com/jesseweed/seti-ui", - "commitHash": "2d10473b7575ec00c47eda751ea9caeec6b0b606" + "commitHash": "fd20793e5a75b350eab8d489165fb9b420df3f62" } }, "version": "0.1.0" diff --git a/extensions/theme-seti/icons/seti.woff b/extensions/theme-seti/icons/seti.woff index f0e47486995..88cb75699ed 100644 Binary files a/extensions/theme-seti/icons/seti.woff and b/extensions/theme-seti/icons/seti.woff differ diff --git a/extensions/theme-seti/icons/vs-seti-icon-theme.json b/extensions/theme-seti/icons/vs-seti-icon-theme.json index abf5c8a0a7e..f5b78299680 100644 --- a/extensions/theme-seti/icons/vs-seti-icon-theme.json +++ b/extensions/theme-seti/icons/vs-seti-icon-theme.json @@ -1575,8 +1575,6 @@ "cfm": "_coldfusion", "litcoffee": "_coffee", "config": "_config", - "cfg": "_config", - "conf": "_config", "cr": "_crystal", "ecr": "_crystal_embedded", "slang": "_crystal_embedded", @@ -1731,7 +1729,6 @@ "webp": "_image", "sublime-project": "_sublime", "sublime-workspace": "_sublime", - "fish": "_shell", "mov": "_video", "ogv": "_video", "webm": "_video", @@ -1774,7 +1771,6 @@ "direnv": "_config", "env": "_config", "static": "_config", - "editorconfig": "_config", "slugignore": "_config", "tmp": "_clock_1", "htaccess": "_config", @@ -1835,14 +1831,19 @@ "yarn.lock": "_yarn", "webpack.config.js": "_webpack", "webpack.config.cjs": "_webpack", + "webpack.config.ts": "_webpack", "webpack.config.build.js": "_webpack", "webpack.config.build.cjs": "_webpack", + "webpack.config.build.ts": "_webpack", "webpack.common.js": "_webpack", "webpack.common.cjs": "_webpack", + "webpack.common.ts": "_webpack", "webpack.dev.js": "_webpack", "webpack.dev.cjs": "_webpack", + "webpack.dev.ts": "_webpack", "webpack.prod.js": "_webpack", "webpack.prod.cjs": "_webpack", + "webpack.prod.ts": "_webpack", "license": "_license", "licence": "_license", "license.txt": "_license", @@ -1887,7 +1888,7 @@ "groovy": "_grails", "handlebars": "_mustache", "html": "_html_3", - "properties": "_java", + "properties": "_config", "java": "_java", "javascriptreact": "_react", "javascript": "_javascript", @@ -1976,8 +1977,6 @@ "cfm": "_coldfusion_light", "litcoffee": "_coffee_light", "config": "_config_light", - "cfg": "_config_light", - "conf": "_config_light", "cr": "_crystal_light", "ecr": "_crystal_embedded_light", "slang": "_crystal_embedded_light", @@ -2132,7 +2131,6 @@ "webp": "_image_light", "sublime-project": "_sublime_light", "sublime-workspace": "_sublime_light", - "fish": "_shell_light", "mov": "_video_light", "ogv": "_video_light", "webm": "_video_light", @@ -2175,7 +2173,6 @@ "direnv": "_config_light", "env": "_config_light", "static": "_config_light", - "editorconfig": "_config_light", "slugignore": "_config_light", "tmp": "_clock_1_light", "htaccess": "_config_light", @@ -2206,7 +2203,7 @@ "groovy": "_grails_light", "handlebars": "_mustache_light", "html": "_html_3_light", - "properties": "_java_light", + "properties": "_config_light", "java": "_java_light", "javascriptreact": "_react_light", "javascript": "_javascript_light", @@ -2314,14 +2311,19 @@ "yarn.lock": "_yarn_light", "webpack.config.js": "_webpack_light", "webpack.config.cjs": "_webpack_light", + "webpack.config.ts": "_webpack_light", "webpack.config.build.js": "_webpack_light", "webpack.config.build.cjs": "_webpack_light", + "webpack.config.build.ts": "_webpack_light", "webpack.common.js": "_webpack_light", "webpack.common.cjs": "_webpack_light", + "webpack.common.ts": "_webpack_light", "webpack.dev.js": "_webpack_light", "webpack.dev.cjs": "_webpack_light", + "webpack.dev.ts": "_webpack_light", "webpack.prod.js": "_webpack_light", "webpack.prod.cjs": "_webpack_light", + "webpack.prod.ts": "_webpack_light", "license": "_license_light", "licence": "_license_light", "license.txt": "_license_light", @@ -2344,5 +2346,5 @@ "npm-debug.log": "_npm_ignored_light" } }, - "version": "https://github.com/jesseweed/seti-ui/commit/2d10473b7575ec00c47eda751ea9caeec6b0b606" + "version": "https://github.com/jesseweed/seti-ui/commit/fd20793e5a75b350eab8d489165fb9b420df3f62" } \ No newline at end of file diff --git a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-color-theme.json b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-color-theme.json index eef990db889..45f1a1d55b0 100644 --- a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-color-theme.json +++ b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-color-theme.json @@ -36,6 +36,7 @@ "statusBar.noFolderBackground": "#001126", "statusBar.debuggingBackground": "#001126", "activityBar.background": "#001733", + "activityBarItem.settingsProfilesBackground": "#003271", "progressBar.background": "#bbdaffcc", "badge.background": "#bbdaffcc", "badge.foreground": "#001733", diff --git a/extensions/typescript-basics/cgmanifest.json b/extensions/typescript-basics/cgmanifest.json index cf253fb2b95..7e93b3d9ab0 100644 --- a/extensions/typescript-basics/cgmanifest.json +++ b/extensions/typescript-basics/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "TypeScript-TmLanguage", "repositoryUrl": "https://github.com/microsoft/TypeScript-TmLanguage", - "commitHash": "73af17bf3e45339df06d92751ab366ce96c38516" + "commitHash": "c2744520e325330d0608fc1d1993d7fe98e66202" } }, "license": "MIT", diff --git a/extensions/typescript-basics/snippets/typescript.code-snippets b/extensions/typescript-basics/snippets/typescript.code-snippets index 0162ef50975..35b2aa1711c 100644 --- a/extensions/typescript-basics/snippets/typescript.code-snippets +++ b/extensions/typescript-basics/snippets/typescript.code-snippets @@ -14,6 +14,7 @@ }, "Class Definition": { "prefix": "class", + "isFileTemplate": true, "body": [ "class ${1:name} {", "\tconstructor(${2:parameters}) {", diff --git a/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json b/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json index 681581e93d6..636fad5e441 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/73af17bf3e45339df06d92751ab366ce96c38516", + "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/c2744520e325330d0608fc1d1993d7fe98e66202", "name": "TypeScript", "scopeName": "source.ts", "patterns": [ @@ -2671,7 +2671,7 @@ { "name": "meta.object.member.ts meta.object-literal.key.ts", "begin": "(?=[\\'\\\"\\`])", - "end": "(?=:)|((?<=[\\'\\\"\\`])(?=((\\s*[\\(\\<,}])|(\\s+(as)\\s+))))", + "end": "(?=:)|((?<=[\\'\\\"\\`])(?=((\\s*[\\(\\<,}])|(\\s+(as|satisifies)\\s+))))", "patterns": [ { "include": "#comment" @@ -2684,7 +2684,7 @@ { "name": "meta.object.member.ts meta.object-literal.key.ts", "begin": "(?x)(?=(\\b(?]|\\|\\||\\&\\&|\\!\\=\\=|$|^|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|^|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|^|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|^|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|((? ...)\n \n```\nRequires using TypeScript 4.4+ in the workspace.", - "comment": "The text inside the ``` block is code and should not be localized." + "comment": ["The text inside the ``` block is code and should not be localized."] }, "configuration.inlayHints.variableTypes.enabled": { "message": "Enable/disable inlay hints for implicit variable types:\n```typescript\n\nconst foo /* :number */ = Date.now();\n \n```\nRequires using TypeScript 4.4+ in the workspace.", - "comment": "The text inside the ``` block is code and should not be localized." + "comment": ["The text inside the ``` block is code and should not be localized."] }, "configuration.inlayHints.variableTypes.suppressWhenTypeMatchesName": "Suppress type hints on variables whose name is identical to the type name. Requires using TypeScript 4.8+ in the workspace.", "configuration.inlayHints.propertyDeclarationTypes.enabled": { "message": "Enable/disable inlay hints for implicit types on property declarations:\n```typescript\n\nclass Foo {\n\tprop /* :number */ = Date.now();\n}\n \n```\nRequires using TypeScript 4.4+ in the workspace.", - "comment": "The text inside the ``` block is code and should not be localized." + "comment": ["The text inside the ``` block is code and should not be localized."] }, "configuration.inlayHints.functionLikeReturnTypes.enabled": { "message": "Enable/disable inlay hints for implicit return types on function signatures:\n```typescript\n\nfunction foo() /* :number */ {\n\treturn Date.now();\n} \n \n```\nRequires using TypeScript 4.4+ in the workspace.", - "comment": "The text inside the ``` block is code and should not be localized." + "comment": ["The text inside the ``` block is code and should not be localized."] }, "configuration.inlayHints.enumMemberValues.enabled": { "message": "Enable/disable inlay hints for member values in enum declarations:\n```typescript\n\nenum MyValue {\n\tA /* = 0 */;\n\tB /* = 1 */;\n}\n \n```\nRequires using TypeScript 4.4+ in the workspace.", - "comment": "The text inside the ``` block is code and should not be localized." + "comment": ["The text inside the ``` block is code and should not be localized."] }, "taskDefinition.tsconfig.description": "The tsconfig file that defines the TS build.", "javascript.suggestionActions.enabled": "Enable/disable suggestion diagnostics for JavaScript files in the editor.", diff --git a/extensions/typescript-language-features/src/experimentTelemetryReporter.ts b/extensions/typescript-language-features/src/experimentTelemetryReporter.ts new file mode 100644 index 00000000000..d7561300761 --- /dev/null +++ b/extensions/typescript-language-features/src/experimentTelemetryReporter.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import VsCodeTelemetryReporter from '@vscode/extension-telemetry'; +import * as tas from 'vscode-tas-client'; + +export interface IExperimentationTelemetryReporter extends tas.IExperimentationTelemetry, vscode.Disposable { + postEventObj(eventName: string, props: { [prop: string]: string }): void; +} + +/** + * This reporter *supports* experimentation telemetry, + * but will only do so when passed to an {@link ExperimentationService}. + */ + +export class ExperimentationTelemetryReporter + implements IExperimentationTelemetryReporter { + private _sharedProperties: Record = {}; + private _reporter: VsCodeTelemetryReporter; + constructor(reporter: VsCodeTelemetryReporter) { + this._reporter = reporter; + } + + setSharedProperty(name: string, value: string): void { + this._sharedProperties[name] = value; + } + + postEvent(eventName: string, props: Map): void { + const propsObject = { + ...this._sharedProperties, + ...Object.fromEntries(props), + }; + this._reporter.sendTelemetryEvent(eventName, propsObject); + } + + postEventObj(eventName: string, props: { [prop: string]: string }) { + this._reporter.sendTelemetryEvent(eventName, { + ...this._sharedProperties, + ...props, + }); + } + + dispose() { + this._reporter.dispose(); + } +} + diff --git a/extensions/typescript-language-features/src/experimentationService.ts b/extensions/typescript-language-features/src/experimentationService.ts index f739872a824..1f3b1fbd73b 100644 --- a/extensions/typescript-language-features/src/experimentationService.ts +++ b/extensions/typescript-language-features/src/experimentationService.ts @@ -4,20 +4,21 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import VsCodeTelemetryReporter from '@vscode/extension-telemetry'; import * as tas from 'vscode-tas-client'; +import { IExperimentationTelemetryReporter } from './experimentTelemetryReporter'; + interface ExperimentTypes { // None for now. } -export class ExperimentationService implements vscode.Disposable { +export class ExperimentationService { private _experimentationServicePromise: Promise; - private _telemetryReporter: ExperimentTelemetryReporter; + private _telemetryReporter: IExperimentationTelemetryReporter; - constructor(private readonly _extensionContext: vscode.ExtensionContext) { - this._telemetryReporter = new ExperimentTelemetryReporter(_extensionContext); - this._experimentationServicePromise = this.createExperimentationService(); + constructor(telemetryReporter: IExperimentationTelemetryReporter, id: string, version: string, globalState: vscode.Memento) { + this._telemetryReporter = telemetryReporter; + this._experimentationServicePromise = createExperimentationService(this._telemetryReporter, id, version, globalState); } public async getTreatmentVariable(name: K, defaultValue: ExperimentTypes[K]): Promise { @@ -29,70 +30,33 @@ export class ExperimentationService implements vscode.Disposable { return defaultValue; } } - - private async createExperimentationService(): Promise { - let targetPopulation: tas.TargetPopulation; - switch (vscode.env.uriScheme) { - case 'vscode': - targetPopulation = tas.TargetPopulation.Public; - break; - case 'vscode-insiders': - targetPopulation = tas.TargetPopulation.Insiders; - break; - case 'vscode-exploration': - targetPopulation = tas.TargetPopulation.Internal; - break; - case 'code-oss': - targetPopulation = tas.TargetPopulation.Team; - break; - default: - targetPopulation = tas.TargetPopulation.Public; - break; - } - - const id = this._extensionContext.extension.id; - const version = this._extensionContext.extension.packageJSON.version || ''; - const experimentationService = tas.getExperimentationService(id, version, targetPopulation, this._telemetryReporter, this._extensionContext.globalState); - await experimentationService.initialFetch; - return experimentationService; - } - - - /** - * @inheritdoc - */ - public dispose() { - this._telemetryReporter.dispose(); - } } -export class ExperimentTelemetryReporter - implements tas.IExperimentationTelemetry, vscode.Disposable { - private _sharedProperties: Record = {}; - private _reporter: VsCodeTelemetryReporter; - constructor(ctxt: vscode.ExtensionContext) { - const extension = ctxt.extension; - const packageJSON = extension.packageJSON; - this._reporter = new VsCodeTelemetryReporter( - extension.id, - packageJSON.version || '', - packageJSON.aiKey || ''); - +export async function createExperimentationService( + reporter: IExperimentationTelemetryReporter, + id: string, + version: string, + globalState: vscode.Memento): Promise { + let targetPopulation: tas.TargetPopulation; + switch (vscode.env.uriScheme) { + case 'vscode': + targetPopulation = tas.TargetPopulation.Public; + break; + case 'vscode-insiders': + targetPopulation = tas.TargetPopulation.Insiders; + break; + case 'vscode-exploration': + targetPopulation = tas.TargetPopulation.Internal; + break; + case 'code-oss': + targetPopulation = tas.TargetPopulation.Team; + break; + default: + targetPopulation = tas.TargetPopulation.Public; + break; } - setSharedProperty(name: string, value: string): void { - this._sharedProperties[name] = value; - } - - postEvent(eventName: string, props: Map): void { - const propsObject = { - ...this._sharedProperties, - ...Object.fromEntries(props), - }; - this._reporter.sendTelemetryEvent(eventName, propsObject); - } - - dispose() { - this._reporter.dispose(); - } + const experimentationService = tas.getExperimentationService(id, version, targetPopulation, reporter, globalState); + await experimentationService.initialFetch; + return experimentationService; } diff --git a/extensions/typescript-language-features/src/extension.browser.ts b/extensions/typescript-language-features/src/extension.browser.ts index 06b650d2375..ad947fc0302 100644 --- a/extensions/typescript-language-features/src/extension.browser.ts +++ b/extensions/typescript-language-features/src/extension.browser.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import VsCodeTelemetryReporter from '@vscode/extension-telemetry'; import { Api, getExtensionApi } from './api'; import { CommandManager } from './commands/commandManager'; import { registerBaseCommands } from './commands/index'; @@ -17,6 +18,8 @@ import API from './utils/api'; import { TypeScriptServiceConfiguration } from './utils/configuration'; import { BrowserServiceConfigurationProvider } from './utils/configuration.browser'; import { PluginManager } from './utils/plugins'; +import { ExperimentationTelemetryReporter, IExperimentationTelemetryReporter } from './experimentTelemetryReporter'; +import { getPackageInfo } from './utils/packageInfo'; class StaticVersionProvider implements ITypeScriptVersionProvider { @@ -57,6 +60,15 @@ export function activate( vscode.Uri.joinPath(context.extensionUri, 'dist/browser/typescript/tsserver.web.js').toString(), API.fromSimpleString('4.8.2'))); + let experimentTelemetryReporter: IExperimentationTelemetryReporter | undefined; + const packageInfo = getPackageInfo(context); + if (packageInfo) { + const { name: id, version, aiKey } = packageInfo; + const vscTelemetryReporter = new VsCodeTelemetryReporter(id, version, aiKey); + experimentTelemetryReporter = new ExperimentationTelemetryReporter(vscTelemetryReporter); + context.subscriptions.push(experimentTelemetryReporter); + } + const lazyClientHost = createLazyClientHost(context, false, { pluginManager, commandManager, @@ -66,6 +78,7 @@ export function activate( processFactory: WorkerServerProcess, activeJsTsEditorTracker, serviceConfigurationProvider: new BrowserServiceConfigurationProvider(), + experimentTelemetryReporter, }, item => { onCompletionAccepted.fire(item); }); diff --git a/extensions/typescript-language-features/src/extension.ts b/extensions/typescript-language-features/src/extension.ts index f6576dabc0d..e6b971260d3 100644 --- a/extensions/typescript-language-features/src/extension.ts +++ b/extensions/typescript-language-features/src/extension.ts @@ -5,10 +5,12 @@ import * as fs from 'fs'; import * as vscode from 'vscode'; +import VsCodeTelemetryReporter from '@vscode/extension-telemetry'; import { Api, getExtensionApi } from './api'; import { CommandManager } from './commands/commandManager'; import { registerBaseCommands } from './commands/index'; import { ExperimentationService } from './experimentationService'; +import { ExperimentationTelemetryReporter, IExperimentationTelemetryReporter } from './experimentTelemetryReporter'; import { createLazyClientHost, lazilyActivateClient } from './lazyClientHost'; import { nodeRequestCancellerFactory } from './tsServer/cancellation.electron'; import { NodeLogDirectoryProvider } from './tsServer/logDirectoryProvider.electron'; @@ -20,6 +22,7 @@ import { ElectronServiceConfigurationProvider } from './utils/configuration.elec import { onCaseInsensitiveFileSystem } from './utils/fileSystem.electron'; import { PluginManager } from './utils/plugins'; import * as temp from './utils/temp.electron'; +import { getPackageInfo } from './utils/packageInfo'; export function activate( context: vscode.ExtensionContext @@ -42,6 +45,19 @@ export function activate( const jsWalkthroughState = new JsWalkthroughState(); context.subscriptions.push(jsWalkthroughState); + let experimentTelemetryReporter: IExperimentationTelemetryReporter | undefined; + const packageInfo = getPackageInfo(context); + if (packageInfo) { + const { name: id, version, aiKey } = packageInfo; + const vscTelemetryReporter = new VsCodeTelemetryReporter(id, version, aiKey); + experimentTelemetryReporter = new ExperimentationTelemetryReporter(vscTelemetryReporter); + context.subscriptions.push(experimentTelemetryReporter); + + // Currently we have no experiments, but creating the service adds the appropriate + // shared properties to the ExperimentationTelemetryReporter we just created. + new ExperimentationService(experimentTelemetryReporter, id, version, context.globalState); + } + const lazyClientHost = createLazyClientHost(context, onCaseInsensitiveFileSystem(), { pluginManager, commandManager, @@ -51,6 +67,7 @@ export function activate( processFactory: new ElectronServiceProcessFactory(), activeJsTsEditorTracker, serviceConfigurationProvider: new ElectronServiceConfigurationProvider(), + experimentTelemetryReporter, }, item => { onCompletionAccepted.fire(item); }); @@ -58,9 +75,6 @@ export function activate( registerBaseCommands(commandManager, lazyClientHost, pluginManager, activeJsTsEditorTracker); registerJsNodeWalkthrough(commandManager, jsWalkthroughState); - // Currently no variables in use. - context.subscriptions.push(new ExperimentationService(context)); - import('./task/taskProvider').then(module => { context.subscriptions.push(module.register(lazyClientHost.map(x => x.serviceClient))); }); diff --git a/extensions/typescript-language-features/src/languageFeatures/completions.ts b/extensions/typescript-language-features/src/languageFeatures/completions.ts index a7b1e6c061c..2547b92e044 100644 --- a/extensions/typescript-language-features/src/languageFeatures/completions.ts +++ b/extensions/typescript-language-features/src/languageFeatures/completions.ts @@ -117,13 +117,9 @@ class MyCompletionItem extends vscode.CompletionItem { if (tsEntry.kindModifiers) { const kindModifiers = parseKindModifier(tsEntry.kindModifiers); if (kindModifiers.has(PConst.KindModifiers.optional)) { - if (!this.insertText) { - this.insertText = this.textLabel; - } + this.insertText ??= this.textLabel; + this.filterText ??= this.textLabel; - if (!this.filterText) { - this.filterText = this.textLabel; - } if (typeof this.label === 'string') { this.label += '?'; } else { @@ -521,7 +517,7 @@ class MyCompletionItem extends vscode.CompletionItem { } } -function getScriptKindDetails(tsEntry: protocol.CompletionEntry,): string | undefined { +function getScriptKindDetails(tsEntry: Proto.CompletionEntry,): string | undefined { if (!tsEntry.kindModifiers || tsEntry.kind !== PConst.Kind.script) { return; } diff --git a/extensions/typescript-language-features/src/languageFeatures/fileReferences.ts b/extensions/typescript-language-features/src/languageFeatures/fileReferences.ts index 68685848035..b9e9db47fe5 100644 --- a/extensions/typescript-language-features/src/languageFeatures/fileReferences.ts +++ b/extensions/typescript-language-features/src/languageFeatures/fileReferences.ts @@ -30,10 +30,7 @@ class FileReferencesCommand implements Command { return; } - if (!resource) { - resource = vscode.window.activeTextEditor?.document.uri; - } - + resource ??= vscode.window.activeTextEditor?.document.uri; if (!resource) { vscode.window.showErrorMessage(localize('error.noResource', "Find file references failed. No resource provided.")); return; diff --git a/extensions/typescript-language-features/src/languageFeatures/updatePathsOnRename.ts b/extensions/typescript-language-features/src/languageFeatures/updatePathsOnRename.ts index 4882ae3ca59..7c6995ec7d5 100644 --- a/extensions/typescript-language-features/src/languageFeatures/updatePathsOnRename.ts +++ b/extensions/typescript-language-features/src/languageFeatures/updatePathsOnRename.ts @@ -147,62 +147,47 @@ class UpdateImportsOnFileRenameHandler extends Disposable { return false; } - const enum Choice { - None = 0, - Accept = 1, - Reject = 2, - Always = 3, - Never = 4, - } + const rejectItem: vscode.MessageItem = { + title: localize('reject.title', "No"), + isCloseAffordance: true, + }; - interface Item extends vscode.MessageItem { - readonly choice: Choice; - } + const acceptItem: vscode.MessageItem = { + title: localize('accept.title', "Yes"), + }; + const alwaysItem: vscode.MessageItem = { + title: localize('always.title', "Always automatically update imports"), + }; - const response = await vscode.window.showInformationMessage( + const neverItem: vscode.MessageItem = { + title: localize('never.title', "Never automatically update imports"), + }; + + const response = await vscode.window.showInformationMessage( newResources.length === 1 ? localize('prompt', "Update imports for '{0}'?", path.basename(newResources[0].fsPath)) : this.getConfirmMessage(localize('promptMoreThanOne', "Update imports for the following {0} files?", newResources.length), newResources), { modal: true, - }, { - title: localize('reject.title', "No"), - choice: Choice.Reject, - isCloseAffordance: true, - }, { - title: localize('accept.title', "Yes"), - choice: Choice.Accept, - }, { - title: localize('always.title', "Always automatically update imports"), - choice: Choice.Always, - }, { - title: localize('never.title', "Never automatically update imports"), - choice: Choice.Never, - }); + }, rejectItem, acceptItem, alwaysItem, neverItem); - if (!response) { - return false; - } - switch (response.choice) { - case Choice.Accept: - { - return true; - } - case Choice.Reject: - { - return false; - } - case Choice.Always: - { - const config = this.getConfiguration(newResources[0]); - config.update( - updateImportsOnFileMoveName, - UpdateImportsOnFileMoveSetting.Always, - this.getConfigTargetScope(config, updateImportsOnFileMoveName)); - return true; - } - case Choice.Never: + switch (response) { + case acceptItem: { + return true; + } + case rejectItem: { + return false; + } + case alwaysItem: { + const config = this.getConfiguration(newResources[0]); + config.update( + updateImportsOnFileMoveName, + UpdateImportsOnFileMoveSetting.Always, + this.getConfigTargetScope(config, updateImportsOnFileMoveName)); + return true; + } + case neverItem: { const config = this.getConfiguration(newResources[0]); config.update( @@ -211,9 +196,10 @@ class UpdateImportsOnFileRenameHandler extends Disposable { this.getConfigTargetScope(config, updateImportsOnFileMoveName)); return false; } + default: { + return false; + } } - - return false; } private async getJsTsFileBeingMoved(resource: vscode.Uri): Promise { diff --git a/extensions/typescript-language-features/src/lazyClientHost.ts b/extensions/typescript-language-features/src/lazyClientHost.ts index 8ad2efac84c..377d9c09fa1 100644 --- a/extensions/typescript-language-features/src/lazyClientHost.ts +++ b/extensions/typescript-language-features/src/lazyClientHost.ts @@ -5,6 +5,7 @@ import * as vscode from 'vscode'; import { CommandManager } from './commands/commandManager'; +import { IExperimentationTelemetryReporter } from './experimentTelemetryReporter'; import { OngoingRequestCancellerFactory } from './tsServer/cancellation'; import { ILogDirectoryProvider } from './tsServer/logDirectoryProvider'; import { TsServerProcessFactory } from './tsServer/server'; @@ -30,6 +31,7 @@ export function createLazyClientHost( processFactory: TsServerProcessFactory; activeJsTsEditorTracker: ActiveJsTsEditorTracker; serviceConfigurationProvider: ServiceConfigurationProvider; + experimentTelemetryReporter: IExperimentationTelemetryReporter | undefined; }, onCompletionAccepted: (item: vscode.CompletionItem) => void, ): Lazy { diff --git a/extensions/typescript-language-features/src/protocol.d.ts b/extensions/typescript-language-features/src/protocol.d.ts index 7e9e45f2575..1aec9e082ed 100644 --- a/extensions/typescript-language-features/src/protocol.d.ts +++ b/extensions/typescript-language-features/src/protocol.d.ts @@ -1,13 +1,19 @@ -import * as Proto from 'typescript/lib/protocol'; -export = Proto; +import * as ts from 'typescript/lib/tsserverlibrary'; +export = ts.server.protocol; + declare enum ServerType { Syntax = 'syntax', Semantic = 'semantic', } -declare module 'typescript/lib/protocol' { - interface Response { - readonly _serverType?: ServerType; +declare module 'typescript/lib/tsserverlibrary' { + namespace server.protocol { + type TextInsertion = ts.TextInsertion; + type ScriptElementKind = ts.ScriptElementKind; + + interface Response { + readonly _serverType?: ServerType; + } } } diff --git a/extensions/typescript-language-features/src/test/unit/previewer.test.ts b/extensions/typescript-language-features/src/test/unit/previewer.test.ts index 2f7af37383b..75a4464610c 100644 --- a/extensions/typescript-language-features/src/test/unit/previewer.test.ts +++ b/extensions/typescript-language-features/src/test/unit/previewer.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import 'mocha'; -import { SymbolDisplayPart } from 'typescript/lib/protocol'; +import { SymbolDisplayPart } from '../../protocol'; import { Uri } from 'vscode'; import { IFilePathToResourceConverter, markdownDocumentation, plainWithLinks, tagsMarkdownPreview } from '../../utils/previewer'; diff --git a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts index c8635b2f668..6c2168a380c 100644 --- a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts +++ b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts @@ -10,6 +10,7 @@ import * as vscode from 'vscode'; import { CommandManager } from './commands/commandManager'; +import { IExperimentationTelemetryReporter } from './experimentTelemetryReporter'; import { DiagnosticKind } from './languageFeatures/diagnostics'; import FileConfigurationManager from './languageFeatures/fileConfigurationManager'; import LanguageProvider from './languageProvider'; @@ -72,6 +73,7 @@ export default class TypeScriptServiceClientHost extends Disposable { processFactory: TsServerProcessFactory; activeJsTsEditorTracker: ActiveJsTsEditorTracker; serviceConfigurationProvider: ServiceConfigurationProvider; + experimentTelemetryReporter: IExperimentationTelemetryReporter | undefined; }, onCompletionAccepted: (item: vscode.CompletionItem) => void, ) { diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index a42f5c29f06..169df72d203 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -6,6 +6,7 @@ import * as path from 'path'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; +import { IExperimentationTelemetryReporter } from './experimentTelemetryReporter'; import { DiagnosticKind, DiagnosticsManager } from './languageFeatures/diagnostics'; import * as Proto from './protocol'; import { EventName } from './protocol.const'; @@ -137,6 +138,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType versionProvider: ITypeScriptVersionProvider; processFactory: TsServerProcessFactory; serviceConfigurationProvider: ServiceConfigurationProvider; + experimentTelemetryReporter: IExperimentationTelemetryReporter | undefined; }, allModeIds: readonly string[] ) { @@ -205,14 +207,14 @@ export default class TypeScriptServiceClient extends Disposable implements IType } }, this, this._disposables); - this.telemetryReporter = this._register(new VSCodeTelemetryReporter(() => { + this.telemetryReporter = new VSCodeTelemetryReporter(services.experimentTelemetryReporter, () => { if (this.serverState.type === ServerState.Type.Running) { if (this.serverState.tsserverVersion) { return this.serverState.tsserverVersion; } } return this.apiVersion.fullVersionString; - })); + }); this.typescriptServerSpawner = new TypeScriptServerSpawner(this.versionProvider, this._versionManager, this.logDirectoryProvider, this.pluginPathsProvider, this.logger, this.telemetryReporter, this.tracer, this.processFactory); diff --git a/extensions/typescript-language-features/src/utils/configuration.ts b/extensions/typescript-language-features/src/utils/configuration.ts index efa442a5d0d..d1bfe62f738 100644 --- a/extensions/typescript-language-features/src/utils/configuration.ts +++ b/extensions/typescript-language-features/src/utils/configuration.ts @@ -5,6 +5,7 @@ import * as vscode from 'vscode'; import * as objects from '../utils/objects'; +import * as Proto from '../protocol'; export enum TsServerLogLevel { Off, @@ -112,7 +113,7 @@ export interface TypeScriptServiceConfiguration { readonly enableProjectDiagnostics: boolean; readonly maxTsServerMemory: number; readonly enablePromptUseWorkspaceTsdk: boolean; - readonly watchOptions: protocol.WatchOptions | undefined; + readonly watchOptions: Proto.WatchOptions | undefined; readonly includePackageJsonAutoImports: 'auto' | 'on' | 'off' | undefined; readonly enableTsServerTracing: boolean; } @@ -196,8 +197,8 @@ export abstract class BaseServiceConfigurationProvider implements ServiceConfigu return configuration.get('typescript.tsserver.experimental.enableProjectDiagnostics', false); } - protected readWatchOptions(configuration: vscode.WorkspaceConfiguration): protocol.WatchOptions | undefined { - return configuration.get('typescript.tsserver.watchOptions'); + protected readWatchOptions(configuration: vscode.WorkspaceConfiguration): Proto.WatchOptions | undefined { + return configuration.get('typescript.tsserver.watchOptions'); } protected readIncludePackageJsonAutoImports(configuration: vscode.WorkspaceConfiguration): 'auto' | 'on' | 'off' | undefined { diff --git a/extensions/typescript-language-features/src/utils/dependentRegistration.ts b/extensions/typescript-language-features/src/utils/dependentRegistration.ts index 3d97b8a8e7f..c53d553625c 100644 --- a/extensions/typescript-language-features/src/utils/dependentRegistration.ts +++ b/extensions/typescript-language-features/src/utils/dependentRegistration.ts @@ -54,14 +54,10 @@ class ConditionalRegistration { private update() { const enabled = this.conditions.every(condition => condition.value); if (enabled) { - if (!this.registration) { - this.registration = this.doRegister(); - } + this.registration ??= this.doRegister(); } else { - if (this.registration) { - this.registration.dispose(); - this.registration = undefined; - } + this.registration?.dispose(); + this.registration = undefined; } } } diff --git a/extensions/typescript-language-features/src/utils/packageInfo.ts b/extensions/typescript-language-features/src/utils/packageInfo.ts new file mode 100644 index 00000000000..09536ab4141 --- /dev/null +++ b/extensions/typescript-language-features/src/utils/packageInfo.ts @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; + +export interface PackageInfo { + name: string; + version: string; + aiKey: string; +} + +export function getPackageInfo(context: vscode.ExtensionContext) { + const packageJSON = context.extension.packageJSON; + if (packageJSON && typeof packageJSON === 'object') { + return { + name: packageJSON.name ?? '', + version: packageJSON.version ?? '', + aiKey: packageJSON.aiKey ?? '', + }; + } + return null; +} diff --git a/extensions/typescript-language-features/src/utils/plugins.ts b/extensions/typescript-language-features/src/utils/plugins.ts index 697e5422445..814f0e12016 100644 --- a/extensions/typescript-language-features/src/utils/plugins.ts +++ b/extensions/typescript-language-features/src/utils/plugins.ts @@ -28,7 +28,7 @@ namespace TypeScriptServerPlugin { export class PluginManager extends Disposable { private readonly _pluginConfigurations = new Map(); - private _plugins: Map> | undefined; + private _plugins?: Map>; constructor() { super(); @@ -37,6 +37,7 @@ export class PluginManager extends Disposable { if (!this._plugins) { return; } + const newPlugins = this.readPlugins(); if (!arrays.equals(Array.from(this._plugins.values()).flat(), Array.from(newPlugins.values()).flat(), TypeScriptServerPlugin.equals)) { this._plugins = newPlugins; @@ -46,9 +47,7 @@ export class PluginManager extends Disposable { } public get plugins(): ReadonlyArray { - if (!this._plugins) { - this._plugins = this.readPlugins(); - } + this._plugins ??= this.readPlugins(); return Array.from(this._plugins.values()).flat(); } diff --git a/extensions/typescript-language-features/src/utils/previewer.ts b/extensions/typescript-language-features/src/utils/previewer.ts index a4a42086d17..1564cdd92dd 100644 --- a/extensions/typescript-language-features/src/utils/previewer.ts +++ b/extensions/typescript-language-features/src/utils/previewer.ts @@ -84,7 +84,7 @@ function getTagDocumentation( case 'extends': case 'param': case 'template': { - const body = (convertLinkTags(tag.text, filePathConverter)).split(/^(\S+)\s*-?\s*/); + const body = getTagBody(tag, filePathConverter); if (body?.length === 3) { const param = body[1]; const doc = body[2]; @@ -106,6 +106,18 @@ function getTagDocumentation( return label + (text.match(/\r\n|\n/g) ? ' \n' + text : ` \u2014 ${text}`); } +function getTagBody(tag: Proto.JSDocTagInfo, filePathConverter: IFilePathToResourceConverter): Array | undefined { + if (tag.name === 'template') { + const parts = tag.text; + if (parts && typeof (parts) !== 'string') { + const params = parts.filter(p => p.kind === 'typeParameterName').map(p => p.text).join(', '); + const docs = parts.filter(p => p.kind === 'text').map(p => convertLinkTags(p.text.replace(/^\s*-?\s*/, ''), filePathConverter)).join(' '); + return params ? ['', params, docs] : undefined; + } + } + return (convertLinkTags(tag.text, filePathConverter)).split(/^(\S+)\s*-?\s*/); +} + export function plainWithLinks( parts: readonly Proto.SymbolDisplayPart[] | string, filePathConverter: IFilePathToResourceConverter, diff --git a/extensions/typescript-language-features/src/utils/telemetry.ts b/extensions/typescript-language-features/src/utils/telemetry.ts index da7ee73cad0..4b2d3867f6d 100644 --- a/extensions/typescript-language-features/src/utils/telemetry.ts +++ b/extensions/typescript-language-features/src/utils/telemetry.ts @@ -3,15 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; -import VsCodeTelemetryReporter from '@vscode/extension-telemetry'; -import { memoize } from './memoize'; - -interface PackageInfo { - readonly name: string; - readonly version: string; - readonly aiKey: string; -} +import { IExperimentationTelemetryReporter } from '../experimentTelemetryReporter'; export interface TelemetryProperties { readonly [prop: string]: string | number | boolean | undefined; @@ -19,14 +11,11 @@ export interface TelemetryProperties { export interface TelemetryReporter { logTelemetry(eventName: string, properties?: TelemetryProperties): void; - - dispose(): void; } export class VSCodeTelemetryReporter implements TelemetryReporter { - private _reporter: VsCodeTelemetryReporter | null = null; - constructor( + private readonly reporter: IExperimentationTelemetryReporter | undefined, private readonly clientVersionDelegate: () => string ) { } @@ -43,38 +32,6 @@ export class VSCodeTelemetryReporter implements TelemetryReporter { */ properties['version'] = this.clientVersionDelegate(); - reporter.sendTelemetryEvent(eventName, properties); - } - - public dispose() { - if (this._reporter) { - this._reporter.dispose(); - this._reporter = null; - } - } - - @memoize - private get reporter(): VsCodeTelemetryReporter | null { - if (this.packageInfo?.aiKey) { - this._reporter = new VsCodeTelemetryReporter( - this.packageInfo.name, - this.packageInfo.version, - this.packageInfo.aiKey); - return this._reporter; - } - return null; - } - - @memoize - private get packageInfo(): PackageInfo | null { - const { packageJSON } = vscode.extensions.getExtension('vscode.typescript-language-features')!; - if (packageJSON) { - return { - name: packageJSON.name, - version: packageJSON.version, - aiKey: packageJSON.aiKey - }; - } - return null; + reporter.postEventObj(eventName, properties); } } diff --git a/extensions/typescript-language-features/src/utils/temp.electron.ts b/extensions/typescript-language-features/src/utils/temp.electron.ts index 84fc21ff885..886c8f014fe 100644 --- a/extensions/typescript-language-features/src/utils/temp.electron.ts +++ b/extensions/typescript-language-features/src/utils/temp.electron.ts @@ -34,9 +34,7 @@ const getRootTempDir = (() => { export const getInstanceTempDir = (() => { let dir: string | undefined; return () => { - if (!dir) { - dir = path.join(getRootTempDir(), makeRandomHexString(20)); - } + dir ??= path.join(getRootTempDir(), makeRandomHexString(20)); if (!fs.existsSync(dir)) { fs.mkdirSync(dir); } diff --git a/extensions/typescript-language-features/src/utils/tsconfig.ts b/extensions/typescript-language-features/src/utils/tsconfig.ts index baf1d4fd59b..1c71d659126 100644 --- a/extensions/typescript-language-features/src/utils/tsconfig.ts +++ b/extensions/typescript-language-features/src/utils/tsconfig.ts @@ -163,7 +163,7 @@ export async function openProjectConfigForFile( return; } - let res: ServerResponse.Response | undefined; + let res: ServerResponse.Response | undefined; try { res = await client.execute('projectInfo', { file, needFileNameList: false }, nulToken); } catch { diff --git a/extensions/typescript-language-features/yarn.lock b/extensions/typescript-language-features/yarn.lock index 08e22bf4c15..89f3521b9b6 100644 --- a/extensions/typescript-language-features/yarn.lock +++ b/extensions/typescript-language-features/yarn.lock @@ -85,10 +85,10 @@ tas-client@0.1.58: dependencies: axios "^0.26.1" -vscode-nls@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.1.0.tgz#443b301a7465d88c81c0f4e1914f9857f0dce1e4" - integrity sha512-37Ha44QrLFwR2IfSSYdOArzUvOyoWbOYTwQC+wS0NfqKjhW7s0WQ1lMy5oJXgSZy9sAiZS5ifELhbpXodeMR8w== +vscode-nls@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.2.0.tgz#3cb6893dd9bd695244d8a024bdf746eea665cc3f" + integrity sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng== vscode-tas-client@^0.1.63: version "0.1.63" diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index 07e71c1daaf..b38c61cd6d2 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -20,7 +20,6 @@ "notebookCellExecutionState", "notebookContentProvider", "notebookControllerKind", - "notebookDebugOptions", "notebookDeprecated", "notebookLiveShare", "notebookMessaging", @@ -34,6 +33,7 @@ "taskPresentationGroup", "terminalDataWriteEvent", "terminalDimensions", + "tunnels", "envShellEvent", "testCoverage", "testObserver", @@ -44,6 +44,7 @@ "treeViewReveal", "workspaceTrust", "telemetry", + "extensionLog", "localization" ], "private": 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 a445753483d..d02e5a44442 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/env.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/env.test.ts @@ -32,7 +32,7 @@ suite('vscode API - env', () => { test('env.remoteName', function () { const remoteName = env.remoteName; const knownWorkspaceExtension = extensions.getExtension('vscode.git'); - const knownUiAndWorkspaceExtension = extensions.getExtension('vscode.image-preview'); + const knownUiAndWorkspaceExtension = extensions.getExtension('vscode.media-preview'); if (typeof remoteName === 'undefined') { // not running in remote, so we expect both extensions assert.ok(knownWorkspaceExtension); diff --git a/extensions/vscode-colorize-tests/test/colorize-results/12750_html.json b/extensions/vscode-colorize-tests/test/colorize-results/12750_html.json index 75021ecfe32..3989f39d021 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/12750_html.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/12750_html.json @@ -40,9 +40,9 @@ "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -479,4 +479,4 @@ "hc_light": "punctuation.definition.tag: #0F4A85" } } -] \ No newline at end of file +] diff --git a/extensions/vscode-colorize-tests/test/colorize-results/14119_less.json b/extensions/vscode-colorize-tests/test/colorize-results/14119_less.json index dae875b1f49..b7b57e957c9 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/14119_less.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/14119_less.json @@ -40,9 +40,9 @@ "t": "source.css.less variable.other.less punctuation.definition.variable.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -52,9 +52,9 @@ "t": "source.css.less variable.other.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -251,4 +251,4 @@ "hc_light": "default: #292929" } } -] \ No newline at end of file +] diff --git a/extensions/vscode-colorize-tests/test/colorize-results/25920_html.json b/extensions/vscode-colorize-tests/test/colorize-results/25920_html.json index d80f8d84182..ed95595fd58 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/25920_html.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/25920_html.json @@ -76,9 +76,9 @@ "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -196,9 +196,9 @@ "t": "text.html.derivative meta.embedded.block.html text.html.basic meta.tag.structure.div.start.html meta.attribute.class.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -388,9 +388,9 @@ "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -652,9 +652,9 @@ "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -772,9 +772,9 @@ "t": "text.html.derivative meta.embedded.block.html text.html.basic meta.tag.structure.div.start.html meta.attribute.class.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -964,9 +964,9 @@ "t": "text.html.derivative meta.tag.structure.body.start.html meta.attribute.class.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test-4287_pug.json b/extensions/vscode-colorize-tests/test/colorize-results/test-4287_pug.json index 98c382df18b..4ec8289167e 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test-4287_pug.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test-4287_pug.json @@ -4,9 +4,9 @@ "t": "text.pug entity.other.attribute-name.class.pug", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -23,4 +23,4 @@ "hc_light": "string.comment.buffered.block.pug: #0F4A85" } } -] \ No newline at end of file +] diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test-7115_xml.json b/extensions/vscode-colorize-tests/test/colorize-results/test-7115_xml.json index 95e8817adcb..e2b4189dbc2 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test-7115_xml.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test-7115_xml.json @@ -28,9 +28,9 @@ "t": "text.xml meta.tag.preprocessor.xml entity.other.attribute-name.xml", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -88,9 +88,9 @@ "t": "text.xml meta.tag.preprocessor.xml entity.other.attribute-name.xml", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -148,9 +148,9 @@ "t": "text.xml meta.tag.preprocessor.xml entity.other.attribute-name.xml", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -316,9 +316,9 @@ "t": "text.xml meta.tag.xml entity.other.attribute-name.localname.xml", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -388,9 +388,9 @@ "t": "text.xml meta.tag.xml entity.other.attribute-name.localname.xml", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -635,4 +635,4 @@ "hc_light": "punctuation.definition.tag: #0F4A85" } } -] \ No newline at end of file +] diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test-cssvariables_less.json b/extensions/vscode-colorize-tests/test/colorize-results/test-cssvariables_less.json index d13d4367a82..419443b11de 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test-cssvariables_less.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test-cssvariables_less.json @@ -64,9 +64,9 @@ "t": "source.css.less meta.property-list.css variable.other.less punctuation.definition.variable.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -76,9 +76,9 @@ "t": "source.css.less meta.property-list.css variable.other.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -160,9 +160,9 @@ "t": "source.css.less meta.property-list.css variable.other.less punctuation.definition.variable.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -172,9 +172,9 @@ "t": "source.css.less meta.property-list.css variable.other.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -400,9 +400,9 @@ "t": "source.css.less meta.property-list.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test-cssvariables_scss.json b/extensions/vscode-colorize-tests/test/colorize-results/test-cssvariables_scss.json index 6e922a532cd..ee81512b5ca 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test-cssvariables_scss.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test-cssvariables_scss.json @@ -64,9 +64,9 @@ "t": "source.css.scss meta.property-list.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -148,9 +148,9 @@ "t": "source.css.scss meta.property-list.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -268,9 +268,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -376,9 +376,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -508,9 +508,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test-embedding_html.json b/extensions/vscode-colorize-tests/test/colorize-results/test-embedding_html.json index a48d921ebaa..b22ea1d7793 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test-embedding_html.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test-embedding_html.json @@ -268,9 +268,9 @@ "t": "text.html.derivative meta.embedded.block.html source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -424,9 +424,9 @@ "t": "text.html.derivative meta.tag.inline.a.start.html meta.attribute.event-handler.blur.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -508,9 +508,9 @@ "t": "text.html.derivative meta.tag.inline.a.start.html meta.attribute.event-handler.click.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -592,9 +592,9 @@ "t": "text.html.derivative meta.tag.inline.a.start.html meta.attribute.event-handler.drag.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -724,9 +724,9 @@ "t": "text.html.derivative meta.tag.structure.div.start.html meta.attribute.style.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -868,9 +868,9 @@ "t": "text.html.derivative meta.tag.structure.div.start.html meta.attribute.style.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1012,9 +1012,9 @@ "t": "text.html.derivative meta.tag.structure.div.start.html meta.attribute.style.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test-regex_coffee.json b/extensions/vscode-colorize-tests/test/colorize-results/test-regex_coffee.json index 4140c9f0b45..e05badae2cf 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test-regex_coffee.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test-regex_coffee.json @@ -508,9 +508,9 @@ "t": "source.coffee string.regexp.multiline.coffee source.coffee.embedded.source", "r": { "dark_plus": "source.coffee.embedded: #9CDCFE", - "light_plus": "source.coffee.embedded: #FF0000", + "light_plus": "source.coffee.embedded: #E50000", "dark_vs": "source.coffee.embedded: #9CDCFE", - "light_vs": "source.coffee.embedded: #FF0000", + "light_vs": "source.coffee.embedded: #E50000", "hc_black": "source.coffee.embedded: #D4D4D4", "hc_light": "source.coffee.embedded: #264F78" } diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test-variables_css.json b/extensions/vscode-colorize-tests/test/colorize-results/test-variables_css.json index 4bc08534cae..f2bb411df60 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test-variables_css.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test-variables_css.json @@ -64,9 +64,9 @@ "t": "source.css meta.property-list.css variable.css", "r": { "dark_plus": "variable.css: #9CDCFE", - "light_plus": "variable.css: #FF0000", + "light_plus": "variable.css: #E50000", "dark_vs": "variable.css: #9CDCFE", - "light_vs": "variable.css: #FF0000", + "light_vs": "variable.css: #E50000", "hc_black": "variable.css: #D4D4D4", "hc_light": "variable.css: #264F78" } @@ -148,9 +148,9 @@ "t": "source.css meta.property-list.css variable.css", "r": { "dark_plus": "variable.css: #9CDCFE", - "light_plus": "variable.css: #FF0000", + "light_plus": "variable.css: #E50000", "dark_vs": "variable.css: #9CDCFE", - "light_vs": "variable.css: #FF0000", + "light_vs": "variable.css: #E50000", "hc_black": "variable.css: #D4D4D4", "hc_light": "variable.css: #264F78" } @@ -352,9 +352,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test_coffee.json b/extensions/vscode-colorize-tests/test/colorize-results/test_coffee.json index 37474def82e..c173079cd2a 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test_coffee.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_coffee.json @@ -354,7 +354,7 @@ "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", "dark_vs": "source.coffee.embedded: #9CDCFE", - "light_vs": "source.coffee.embedded: #FF0000", + "light_vs": "source.coffee.embedded: #E50000", "hc_black": "variable: #9CDCFE", "hc_light": "variable: #001080" } @@ -642,7 +642,7 @@ "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", "dark_vs": "source.coffee.embedded: #9CDCFE", - "light_vs": "source.coffee.embedded: #FF0000", + "light_vs": "source.coffee.embedded: #E50000", "hc_black": "variable: #9CDCFE", "hc_light": "variable: #001080" } diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test_cshtml.json b/extensions/vscode-colorize-tests/test/colorize-results/test_cshtml.json index eefa012ecba..16e42371716 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test_cshtml.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_cshtml.json @@ -1,7 +1,19 @@ [ { - "c": "@{", - "t": "text.html.cshtml keyword.control.cshtml", + "c": "@", + "t": "text.html.cshtml meta.structure.razor.codeblock keyword.control.cshtml.transition", + "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", + "hc_light": "keyword.control: #B5200D" + } + }, + { + "c": "{", + "t": "text.html.cshtml meta.structure.razor.codeblock keyword.control.razor.directive.codeblock.open", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -13,7 +25,7 @@ }, { "c": " ", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -25,7 +37,7 @@ }, { "c": "var", - "t": "text.html.cshtml keyword.other.var.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs keyword.other.var.cs", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -37,7 +49,7 @@ }, { "c": " ", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -49,7 +61,7 @@ }, { "c": "total", - "t": "text.html.cshtml entity.name.variable.local.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs entity.name.variable.local.cs", "r": { "dark_plus": "entity.name.variable: #9CDCFE", "light_plus": "entity.name.variable: #001080", @@ -61,7 +73,7 @@ }, { "c": " ", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -73,7 +85,7 @@ }, { "c": "=", - "t": "text.html.cshtml keyword.operator.assignment.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs keyword.operator.assignment.cs", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -85,7 +97,7 @@ }, { "c": " ", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -97,7 +109,7 @@ }, { "c": "0", - "t": "text.html.cshtml constant.numeric.decimal.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs constant.numeric.decimal.cs", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -109,7 +121,7 @@ }, { "c": ";", - "t": "text.html.cshtml punctuation.terminator.statement.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs punctuation.terminator.statement.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -121,7 +133,7 @@ }, { "c": " ", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -133,7 +145,7 @@ }, { "c": "var", - "t": "text.html.cshtml keyword.other.var.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs keyword.other.var.cs", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -145,7 +157,7 @@ }, { "c": " ", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -157,7 +169,7 @@ }, { "c": "totalMessage", - "t": "text.html.cshtml entity.name.variable.local.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs entity.name.variable.local.cs", "r": { "dark_plus": "entity.name.variable: #9CDCFE", "light_plus": "entity.name.variable: #001080", @@ -169,7 +181,7 @@ }, { "c": " ", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -181,7 +193,7 @@ }, { "c": "=", - "t": "text.html.cshtml keyword.operator.assignment.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs keyword.operator.assignment.cs", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -193,7 +205,7 @@ }, { "c": " ", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -205,7 +217,7 @@ }, { "c": "\"", - "t": "text.html.cshtml string.quoted.double.cs punctuation.definition.string.begin.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs string.quoted.double.cs punctuation.definition.string.begin.cs", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -217,7 +229,7 @@ }, { "c": "\"", - "t": "text.html.cshtml string.quoted.double.cs punctuation.definition.string.end.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs string.quoted.double.cs punctuation.definition.string.end.cs", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -229,235 +241,7 @@ }, { "c": ";", - "t": "text.html.cshtml punctuation.terminator.statement.cs", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" - } - }, - { - "c": " @", - "t": "text.html.cshtml", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" - } - }, - { - "c": "*", - "t": "text.html.cshtml keyword.operator.arithmetic.cs", - "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", - "hc_light": "keyword.operator: #000000" - } - }, - { - "c": " ", - "t": "text.html.cshtml", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" - } - }, - { - "c": "a", - "t": "text.html.cshtml variable.other.readwrite.cs", - "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE", - "hc_light": "variable: #001080" - } - }, - { - "c": " ", - "t": "text.html.cshtml", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" - } - }, - { - "c": "multiline", - "t": "text.html.cshtml variable.other.readwrite.cs", - "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE", - "hc_light": "variable: #001080" - } - }, - { - "c": " ", - "t": "text.html.cshtml", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" - } - }, - { - "c": "razor", - "t": "text.html.cshtml variable.other.readwrite.cs", - "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE", - "hc_light": "variable: #001080" - } - }, - { - "c": " ", - "t": "text.html.cshtml", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" - } - }, - { - "c": "comment", - "t": "text.html.cshtml variable.other.readwrite.cs", - "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE", - "hc_light": "variable: #001080" - } - }, - { - "c": " ", - "t": "text.html.cshtml", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" - } - }, - { - "c": "embedded", - "t": "text.html.cshtml variable.other.readwrite.cs", - "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE", - "hc_light": "variable: #001080" - } - }, - { - "c": " ", - "t": "text.html.cshtml", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" - } - }, - { - "c": "in", - "t": "text.html.cshtml variable.other.readwrite.cs", - "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE", - "hc_light": "variable: #001080" - } - }, - { - "c": " ", - "t": "text.html.cshtml", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" - } - }, - { - "c": "csharp", - "t": "text.html.cshtml variable.other.readwrite.cs", - "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE", - "hc_light": "variable: #001080" - } - }, - { - "c": " ", - "t": "text.html.cshtml", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" - } - }, - { - "c": "*", - "t": "text.html.cshtml keyword.operator.arithmetic.cs", - "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", - "hc_light": "keyword.operator: #000000" - } - }, - { - "c": "@", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs punctuation.terminator.statement.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -469,7 +253,91 @@ }, { "c": " ", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "hc_light": "default: #292929" + } + }, + { + "c": "@", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.comment.razor keyword.control.cshtml.transition", + "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", + "hc_light": "keyword.control: #B5200D" + } + }, + { + "c": "*", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.comment.razor keyword.control.razor.comment.star", + "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", + "hc_light": "keyword.control: #B5200D" + } + }, + { + "c": " a multiline", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.comment.razor comment.block.razor", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "hc_light": "comment: #515151" + } + }, + { + "c": " razor comment embedded in csharp ", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.comment.razor comment.block.razor", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "hc_light": "comment: #515151" + } + }, + { + "c": "*", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.comment.razor keyword.control.razor.comment.star", + "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", + "hc_light": "keyword.control: #B5200D" + } + }, + { + "c": "@", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.comment.razor keyword.control.cshtml.transition", + "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", + "hc_light": "keyword.control: #B5200D" + } + }, + { + "c": " ", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -481,7 +349,7 @@ }, { "c": "if", - "t": "text.html.cshtml keyword.control.conditional.if.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor keyword.control.conditional.if.cs", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -493,7 +361,7 @@ }, { "c": " ", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -505,7 +373,7 @@ }, { "c": "(", - "t": "text.html.cshtml punctuation.parenthesis.open.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor punctuation.parenthesis.open.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -517,7 +385,7 @@ }, { "c": "IsPost", - "t": "text.html.cshtml variable.other.readwrite.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor variable.other.readwrite.cs", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -529,7 +397,7 @@ }, { "c": ")", - "t": "text.html.cshtml punctuation.parenthesis.close.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor punctuation.parenthesis.close.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -541,7 +409,7 @@ }, { "c": " ", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -553,7 +421,7 @@ }, { "c": "{", - "t": "text.html.cshtml punctuation.curlybrace.open.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock punctuation.curlybrace.open.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -565,7 +433,7 @@ }, { "c": " ", - "t": "text.html.cshtml punctuation.whitespace.comment.leading.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock punctuation.whitespace.comment.leading.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -577,7 +445,7 @@ }, { "c": "//", - "t": "text.html.cshtml comment.line.double-slash.cs punctuation.definition.comment.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock comment.line.double-slash.cs punctuation.definition.comment.cs", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -589,7 +457,7 @@ }, { "c": " Retrieve the numbers that the user entered.", - "t": "text.html.cshtml comment.line.double-slash.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock comment.line.double-slash.cs", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -601,7 +469,7 @@ }, { "c": " ", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -613,7 +481,7 @@ }, { "c": "var", - "t": "text.html.cshtml keyword.other.var.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock keyword.other.var.cs", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -625,7 +493,7 @@ }, { "c": " ", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -637,7 +505,7 @@ }, { "c": "num1", - "t": "text.html.cshtml entity.name.variable.local.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock entity.name.variable.local.cs", "r": { "dark_plus": "entity.name.variable: #9CDCFE", "light_plus": "entity.name.variable: #001080", @@ -649,7 +517,7 @@ }, { "c": " ", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -661,7 +529,7 @@ }, { "c": "=", - "t": "text.html.cshtml keyword.operator.assignment.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock keyword.operator.assignment.cs", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -673,7 +541,7 @@ }, { "c": " ", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -685,7 +553,7 @@ }, { "c": "Request", - "t": "text.html.cshtml variable.other.object.property.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock variable.other.object.property.cs", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -697,7 +565,7 @@ }, { "c": "[", - "t": "text.html.cshtml punctuation.squarebracket.open.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock punctuation.squarebracket.open.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -709,7 +577,7 @@ }, { "c": "\"", - "t": "text.html.cshtml string.quoted.double.cs punctuation.definition.string.begin.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock string.quoted.double.cs punctuation.definition.string.begin.cs", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -721,7 +589,7 @@ }, { "c": "text1", - "t": "text.html.cshtml string.quoted.double.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock string.quoted.double.cs", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -733,7 +601,7 @@ }, { "c": "\"", - "t": "text.html.cshtml string.quoted.double.cs punctuation.definition.string.end.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock string.quoted.double.cs punctuation.definition.string.end.cs", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -745,7 +613,7 @@ }, { "c": "]", - "t": "text.html.cshtml punctuation.squarebracket.close.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock punctuation.squarebracket.close.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -757,7 +625,7 @@ }, { "c": ";", - "t": "text.html.cshtml punctuation.terminator.statement.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock punctuation.terminator.statement.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -769,7 +637,7 @@ }, { "c": " ", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -781,7 +649,7 @@ }, { "c": "var", - "t": "text.html.cshtml keyword.other.var.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock keyword.other.var.cs", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -793,7 +661,7 @@ }, { "c": " ", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -805,7 +673,7 @@ }, { "c": "num2", - "t": "text.html.cshtml entity.name.variable.local.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock entity.name.variable.local.cs", "r": { "dark_plus": "entity.name.variable: #9CDCFE", "light_plus": "entity.name.variable: #001080", @@ -817,7 +685,7 @@ }, { "c": " ", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -829,7 +697,7 @@ }, { "c": "=", - "t": "text.html.cshtml keyword.operator.assignment.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock keyword.operator.assignment.cs", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -841,7 +709,7 @@ }, { "c": " ", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -853,7 +721,7 @@ }, { "c": "Request", - "t": "text.html.cshtml variable.other.object.property.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock variable.other.object.property.cs", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -865,7 +733,7 @@ }, { "c": "[", - "t": "text.html.cshtml punctuation.squarebracket.open.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock punctuation.squarebracket.open.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -877,7 +745,7 @@ }, { "c": "\"", - "t": "text.html.cshtml string.quoted.double.cs punctuation.definition.string.begin.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock string.quoted.double.cs punctuation.definition.string.begin.cs", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -889,7 +757,7 @@ }, { "c": "text2", - "t": "text.html.cshtml string.quoted.double.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock string.quoted.double.cs", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -901,7 +769,7 @@ }, { "c": "\"", - "t": "text.html.cshtml string.quoted.double.cs punctuation.definition.string.end.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock string.quoted.double.cs punctuation.definition.string.end.cs", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -913,7 +781,7 @@ }, { "c": "]", - "t": "text.html.cshtml punctuation.squarebracket.close.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock punctuation.squarebracket.close.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -925,7 +793,7 @@ }, { "c": ";", - "t": "text.html.cshtml punctuation.terminator.statement.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock punctuation.terminator.statement.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -937,7 +805,7 @@ }, { "c": " ", - "t": "text.html.cshtml punctuation.whitespace.comment.leading.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock punctuation.whitespace.comment.leading.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -949,7 +817,7 @@ }, { "c": "//", - "t": "text.html.cshtml comment.line.double-slash.cs punctuation.definition.comment.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock comment.line.double-slash.cs punctuation.definition.comment.cs", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -961,7 +829,7 @@ }, { "c": " Convert the entered strings into integers numbers and add.", - "t": "text.html.cshtml comment.line.double-slash.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock comment.line.double-slash.cs", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -973,7 +841,7 @@ }, { "c": " ", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -985,7 +853,7 @@ }, { "c": "total", - "t": "text.html.cshtml variable.other.readwrite.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock variable.other.readwrite.cs", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -997,7 +865,7 @@ }, { "c": " ", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1009,7 +877,7 @@ }, { "c": "=", - "t": "text.html.cshtml keyword.operator.assignment.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock keyword.operator.assignment.cs", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1021,7 +889,7 @@ }, { "c": " ", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1033,7 +901,7 @@ }, { "c": "num1", - "t": "text.html.cshtml variable.other.object.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock variable.other.object.cs", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1045,7 +913,7 @@ }, { "c": ".", - "t": "text.html.cshtml punctuation.accessor.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock punctuation.accessor.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1057,7 +925,7 @@ }, { "c": "AsInt", - "t": "text.html.cshtml entity.name.function.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock entity.name.function.cs", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -1069,7 +937,7 @@ }, { "c": "(", - "t": "text.html.cshtml punctuation.parenthesis.open.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock punctuation.parenthesis.open.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1081,7 +949,7 @@ }, { "c": ")", - "t": "text.html.cshtml punctuation.parenthesis.close.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock punctuation.parenthesis.close.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1093,7 +961,7 @@ }, { "c": " ", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1105,7 +973,7 @@ }, { "c": "+", - "t": "text.html.cshtml keyword.operator.arithmetic.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock keyword.operator.arithmetic.cs", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1117,7 +985,7 @@ }, { "c": " ", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1129,7 +997,7 @@ }, { "c": "num2", - "t": "text.html.cshtml variable.other.object.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock variable.other.object.cs", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1141,7 +1009,7 @@ }, { "c": ".", - "t": "text.html.cshtml punctuation.accessor.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock punctuation.accessor.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1153,7 +1021,7 @@ }, { "c": "AsInt", - "t": "text.html.cshtml entity.name.function.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock entity.name.function.cs", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -1165,7 +1033,7 @@ }, { "c": "(", - "t": "text.html.cshtml punctuation.parenthesis.open.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock punctuation.parenthesis.open.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1177,7 +1045,7 @@ }, { "c": ")", - "t": "text.html.cshtml punctuation.parenthesis.close.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock punctuation.parenthesis.close.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1189,7 +1057,7 @@ }, { "c": ";", - "t": "text.html.cshtml punctuation.terminator.statement.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock punctuation.terminator.statement.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1201,7 +1069,7 @@ }, { "c": "\t\t", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1213,295 +1081,163 @@ }, { "c": "<", - "t": "text.html.cshtml keyword.operator.relational.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock punctuation.definition.tag.begin.html", "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", - "hc_light": "keyword.operator: #000000" + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080", + "hc_light": "punctuation.definition.tag: #0F4A85" } }, { "c": "italic", - "t": "text.html.cshtml variable.other.readwrite.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock entity.name.tag.html", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE", - "hc_light": "variable: #001080" - } - }, - { - "c": "><", - "t": "text.html.cshtml keyword.operator.relational.cs", - "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", - "hc_light": "keyword.operator: #000000" - } - }, - { - "c": "bold", - "t": "text.html.cshtml variable.other.readwrite.cs", - "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE", - "hc_light": "variable: #001080" + "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", + "hc_light": "entity.name.tag: #0F4A85" } }, { "c": ">", - "t": "text.html.cshtml keyword.operator.relational.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock punctuation.definition.tag.end.html", "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", - "hc_light": "keyword.operator: #000000" - } - }, - { - "c": "totalMessage", - "t": "text.html.cshtml variable.other.readwrite.cs", - "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE", - "hc_light": "variable: #001080" - } - }, - { - "c": " ", - "t": "text.html.cshtml", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" - } - }, - { - "c": "=", - "t": "text.html.cshtml keyword.operator.assignment.cs", - "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", - "hc_light": "keyword.operator: #000000" - } - }, - { - "c": " ", - "t": "text.html.cshtml", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" - } - }, - { - "c": "\"", - "t": "text.html.cshtml string.quoted.double.cs punctuation.definition.string.begin.cs", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178", - "hc_light": "string: #0F4A85" - } - }, - { - "c": "Total = ", - "t": "text.html.cshtml string.quoted.double.cs", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178", - "hc_light": "string: #0F4A85" - } - }, - { - "c": "\"", - "t": "text.html.cshtml string.quoted.double.cs punctuation.definition.string.end.cs", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178", - "hc_light": "string: #0F4A85" - } - }, - { - "c": " ", - "t": "text.html.cshtml", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" - } - }, - { - "c": "+", - "t": "text.html.cshtml keyword.operator.arithmetic.cs", - "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", - "hc_light": "keyword.operator: #000000" - } - }, - { - "c": " ", - "t": "text.html.cshtml", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" - } - }, - { - "c": "total", - "t": "text.html.cshtml variable.other.readwrite.cs", - "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE", - "hc_light": "variable: #001080" - } - }, - { - "c": ";", - "t": "text.html.cshtml punctuation.terminator.statement.cs", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080", + "hc_light": "punctuation.definition.tag: #0F4A85" } }, { "c": "<", - "t": "text.html.cshtml keyword.operator.relational.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock punctuation.definition.tag.begin.html", "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", - "hc_light": "keyword.operator: #000000" - } - }, - { - "c": "/", - "t": "text.html.cshtml keyword.operator.arithmetic.cs", - "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", - "hc_light": "keyword.operator: #000000" + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080", + "hc_light": "punctuation.definition.tag: #0F4A85" } }, { "c": "bold", - "t": "text.html.cshtml variable.other.readwrite.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock entity.name.tag.html", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE", - "hc_light": "variable: #001080" - } - }, - { - "c": "><", - "t": "text.html.cshtml keyword.operator.relational.cs", - "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", - "hc_light": "keyword.operator: #000000" - } - }, - { - "c": "/", - "t": "text.html.cshtml keyword.operator.arithmetic.cs", - "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", - "hc_light": "keyword.operator: #000000" - } - }, - { - "c": "italic", - "t": "text.html.cshtml variable.other.readwrite.cs", - "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE", - "hc_light": "variable: #001080" + "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", + "hc_light": "entity.name.tag: #0F4A85" } }, { "c": ">", - "t": "text.html.cshtml keyword.operator.relational.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock punctuation.definition.tag.end.html", "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", - "hc_light": "keyword.operator: #000000" + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080", + "hc_light": "punctuation.definition.tag: #0F4A85" + } + }, + { + "c": "totalMessage = \"Total = \" + total;", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "hc_light": "default: #292929" + } + }, + { + "c": "", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080", + "hc_light": "punctuation.definition.tag: #0F4A85" + } + }, + { + "c": "", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080", + "hc_light": "punctuation.definition.tag: #0F4A85" } }, { "c": " ", - "t": "text.html.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1513,7 +1249,7 @@ }, { "c": "}", - "t": "text.html.cshtml punctuation.curlybrace.close.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock punctuation.curlybrace.close.cs", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1525,7 +1261,7 @@ }, { "c": "}", - "t": "text.html.cshtml keyword.control.cshtml", + "t": "text.html.cshtml meta.structure.razor.codeblock keyword.control.razor.directive.codeblock.close", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -1576,9 +1312,9 @@ "t": "text.html.cshtml meta.tag.metadata.doctype.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1636,9 +1372,9 @@ "t": "text.html.cshtml meta.tag.structure.html.start.html meta.attribute.lang.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1900,9 +1636,9 @@ "t": "text.html.cshtml meta.tag.metadata.meta.void.html meta.attribute.charset.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -2308,9 +2044,9 @@ "t": "text.html.cshtml meta.tag.structure.form.start.html meta.attribute.action.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -2368,9 +2104,9 @@ "t": "text.html.cshtml meta.tag.structure.form.start.html meta.attribute.method.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -2524,9 +2260,9 @@ "t": "text.html.cshtml meta.tag.structure.label.start.html meta.attribute.for.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -2692,9 +2428,9 @@ "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.type.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -2764,9 +2500,9 @@ "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.name.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -2980,9 +2716,9 @@ "t": "text.html.cshtml meta.tag.structure.label.start.html meta.attribute.for.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -3148,9 +2884,9 @@ "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.type.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -3220,9 +2956,9 @@ "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.name.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -3436,9 +3172,9 @@ "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.type.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -3508,9 +3244,9 @@ "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.value.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -3672,7 +3408,7 @@ } }, { - "c": "\t@* now we call the totalMessage method", + "c": "\t", "t": "text.html.cshtml", "r": { "dark_plus": "default: #D4D4D4", @@ -3684,68 +3420,8 @@ } }, { - "c": "\t (a multi line razor comment outside code) *@", - "t": "text.html.cshtml", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" - } - }, - { - "c": " ", - "t": "text.html.cshtml", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" - } - }, - { - "c": "<", - "t": "text.html.cshtml meta.tag.structure.p.start.html punctuation.definition.tag.begin.html", - "r": { - "dark_plus": "punctuation.definition.tag: #808080", - "light_plus": "punctuation.definition.tag: #800000", - "dark_vs": "punctuation.definition.tag: #808080", - "light_vs": "punctuation.definition.tag: #800000", - "hc_black": "punctuation.definition.tag: #808080", - "hc_light": "punctuation.definition.tag: #0F4A85" - } - }, - { - "c": "p", - "t": "text.html.cshtml meta.tag.structure.p.start.html entity.name.tag.html", - "r": { - "dark_plus": "entity.name.tag: #569CD6", - "light_plus": "entity.name.tag: #800000", - "dark_vs": "entity.name.tag: #569CD6", - "light_vs": "entity.name.tag: #800000", - "hc_black": "entity.name.tag: #569CD6", - "hc_light": "entity.name.tag: #0F4A85" - } - }, - { - "c": ">", - "t": "text.html.cshtml meta.tag.structure.p.start.html punctuation.definition.tag.end.html", - "r": { - "dark_plus": "punctuation.definition.tag: #808080", - "light_plus": "punctuation.definition.tag: #800000", - "dark_vs": "punctuation.definition.tag: #808080", - "light_vs": "punctuation.definition.tag: #800000", - "hc_black": "punctuation.definition.tag: #808080", - "hc_light": "punctuation.definition.tag: #0F4A85" - } - }, - { - "c": "@totalMessage", - "t": "text.html.cshtml meta.expression.implicit.cshtml keyword.control.cshtml", + "c": "@", + "t": "text.html.cshtml meta.comment.razor keyword.control.cshtml.transition", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -3756,15 +3432,63 @@ } }, { - "c": "

", - "t": "text.html.cshtml meta.expression.implicit.cshtml", + "c": "*", + "t": "text.html.cshtml meta.comment.razor keyword.control.razor.comment.star", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "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", + "hc_light": "keyword.control: #B5200D" + } + }, + { + "c": " now we call the totalMessage method", + "t": "text.html.cshtml meta.comment.razor comment.block.razor", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "hc_light": "comment: #515151" + } + }, + { + "c": "\t (a multi line razor comment outside code) ", + "t": "text.html.cshtml meta.comment.razor comment.block.razor", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "hc_light": "comment: #515151" + } + }, + { + "c": "*", + "t": "text.html.cshtml meta.comment.razor keyword.control.razor.comment.star", + "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", + "hc_light": "keyword.control: #B5200D" + } + }, + { + "c": "@", + "t": "text.html.cshtml meta.comment.razor keyword.control.cshtml.transition", + "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", + "hc_light": "keyword.control: #B5200D" } }, { @@ -3816,7 +3540,127 @@ } }, { - "c": "@(", + "c": "@", + "t": "text.html.cshtml meta.expression.implicit.cshtml keyword.control.cshtml.transition", + "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", + "hc_light": "keyword.control: #B5200D" + } + }, + { + "c": "totalMessage", + "t": "text.html.cshtml meta.expression.implicit.cshtml source.cs variable.other.object.cs", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "hc_light": "variable: #001080" + } + }, + { + "c": "", + "t": "text.html.cshtml meta.tag.structure.p.end.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080", + "hc_light": "punctuation.definition.tag: #0F4A85" + } + }, + { + "c": " ", + "t": "text.html.cshtml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "hc_light": "default: #292929" + } + }, + { + "c": "<", + "t": "text.html.cshtml meta.tag.structure.p.start.html punctuation.definition.tag.begin.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080", + "hc_light": "punctuation.definition.tag: #0F4A85" + } + }, + { + "c": "p", + "t": "text.html.cshtml meta.tag.structure.p.start.html entity.name.tag.html", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6", + "hc_light": "entity.name.tag: #0F4A85" + } + }, + { + "c": ">", + "t": "text.html.cshtml meta.tag.structure.p.start.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080", + "hc_light": "punctuation.definition.tag: #0F4A85" + } + }, + { + "c": "@", + "t": "text.html.cshtml meta.expression.explicit.cshtml keyword.control.cshtml.transition", + "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", + "hc_light": "keyword.control: #B5200D" + } + }, + { + "c": "(", "t": "text.html.cshtml meta.expression.explicit.cshtml keyword.control.cshtml", "r": { "dark_plus": "keyword.control: #C586C0", @@ -3936,7 +3780,7 @@ } }, { - "c": " An email address (with escaped at character): name@", + "c": " An email address (with escaped at character): name", "t": "text.html.cshtml", "r": { "dark_plus": "default: #D4D4D4", @@ -3948,15 +3792,27 @@ } }, { - "c": "@domain.com", - "t": "text.html.cshtml meta.expression.implicit.cshtml keyword.control.cshtml", + "c": "@@", + "t": "text.html.cshtml constant.character.escape.razor.transition", "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", - "hc_light": "keyword.control: #B5200D" + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "domain.com", + "t": "text.html.cshtml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "hc_light": "default: #292929" } }, { @@ -4031,4 +3887,4 @@ "hc_light": "punctuation.definition.tag: #0F4A85" } } -] \ No newline at end of file +] diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test_css.json b/extensions/vscode-colorize-tests/test/colorize-results/test_css.json index 3278b1768d1..1d023e0aba3 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test_css.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_css.json @@ -700,9 +700,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -772,9 +772,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -892,9 +892,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -1036,9 +1036,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -1108,9 +1108,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -1192,9 +1192,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -1408,9 +1408,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -1480,9 +1480,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -1552,9 +1552,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -1708,9 +1708,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -2068,9 +2068,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -2224,9 +2224,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -2452,9 +2452,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -2524,9 +2524,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -2644,9 +2644,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -2836,9 +2836,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -2920,9 +2920,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -2992,9 +2992,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -3148,9 +3148,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -3220,9 +3220,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -3292,9 +3292,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -3448,9 +3448,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -3520,9 +3520,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -3592,9 +3592,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -3676,9 +3676,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -3916,9 +3916,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -3988,9 +3988,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -4120,9 +4120,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -4288,9 +4288,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -4468,9 +4468,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -4636,9 +4636,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -4708,9 +4708,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -4840,9 +4840,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -4924,9 +4924,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -5080,9 +5080,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -5284,9 +5284,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -5368,9 +5368,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -5440,9 +5440,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -5524,9 +5524,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -5608,9 +5608,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -5680,9 +5680,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -5764,9 +5764,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -5836,9 +5836,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -5956,9 +5956,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -6040,9 +6040,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -6184,9 +6184,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -6256,9 +6256,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -6448,9 +6448,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -6532,9 +6532,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -6688,9 +6688,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -6844,9 +6844,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -6976,9 +6976,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -7048,9 +7048,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -7288,9 +7288,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -7372,9 +7372,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -7516,9 +7516,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -7744,9 +7744,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -7888,9 +7888,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -7972,9 +7972,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -8044,9 +8044,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -8116,9 +8116,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -8284,9 +8284,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -8428,9 +8428,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -8632,9 +8632,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -8716,9 +8716,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -8800,9 +8800,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -9016,9 +9016,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -9232,9 +9232,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -9388,9 +9388,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -9592,9 +9592,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -9664,9 +9664,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -9748,9 +9748,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -9820,9 +9820,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -9904,9 +9904,9 @@ "t": "source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test_handlebars.json b/extensions/vscode-colorize-tests/test/colorize-results/test_handlebars.json index e73ac0561eb..64eaa2eef81 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test_handlebars.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_handlebars.json @@ -40,9 +40,9 @@ "t": "text.html.handlebars meta.tag.block.any.html entity.other.attribute-name.html entity.other.attribute-name.generic.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -52,9 +52,9 @@ "t": "text.html.handlebars meta.tag.block.any.html entity.other.attribute-name.html punctuation.separator.key-value.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -856,9 +856,9 @@ "t": "text.html.handlebars meta.tag.block.any.html entity.other.attribute-name.html entity.other.attribute-name.generic.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -868,9 +868,9 @@ "t": "text.html.handlebars meta.tag.block.any.html entity.other.attribute-name.html punctuation.separator.key-value.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1048,9 +1048,9 @@ "t": "text.html.handlebars meta.tag.block.any.html entity.other.attribute-name.html entity.other.attribute-name.generic.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1060,9 +1060,9 @@ "t": "text.html.handlebars meta.tag.block.any.html entity.other.attribute-name.html punctuation.separator.key-value.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1612,9 +1612,9 @@ "t": "text.html.handlebars meta.tag.block.any.html meta.attribute-with-value.id.html entity.other.attribute-name.id.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1840,9 +1840,9 @@ "t": "text.html.handlebars meta.tag.inline.any.html entity.other.attribute-name.html entity.other.attribute-name.generic.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1852,9 +1852,9 @@ "t": "text.html.handlebars meta.tag.inline.any.html entity.other.attribute-name.html punctuation.separator.key-value.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -2303,4 +2303,4 @@ "hc_light": "punctuation.definition.tag: #0F4A85" } } -] \ No newline at end of file +] diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test_hbs.json b/extensions/vscode-colorize-tests/test/colorize-results/test_hbs.json index 428e98c1118..99d47827659 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test_hbs.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_hbs.json @@ -124,9 +124,9 @@ "t": "text.html.handlebars meta.tag.block.any.html meta.attribute-with-value.id.html entity.other.attribute-name.id.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -352,9 +352,9 @@ "t": "text.html.handlebars meta.tag.inline.any.html entity.other.attribute-name.html entity.other.attribute-name.generic.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -364,9 +364,9 @@ "t": "text.html.handlebars meta.tag.inline.any.html entity.other.attribute-name.html punctuation.separator.key-value.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1072,9 +1072,9 @@ "t": "text.html.handlebars meta.tag.block.any.html entity.other.attribute-name.html entity.other.attribute-name.generic.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1084,9 +1084,9 @@ "t": "text.html.handlebars meta.tag.block.any.html entity.other.attribute-name.html punctuation.separator.key-value.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1528,9 +1528,9 @@ "t": "text.html.handlebars meta.tag.block.any.html entity.other.attribute-name.html entity.other.attribute-name.generic.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1540,9 +1540,9 @@ "t": "text.html.handlebars meta.tag.block.any.html entity.other.attribute-name.html punctuation.separator.key-value.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1662,7 +1662,7 @@ "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "variable: #9CDCFE", "hc_light": "variable: #001080" } @@ -1672,9 +1672,9 @@ "t": "text.html.handlebars meta.function.inline.other.handlebars entity.other.attribute-name.handlebars", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1974,7 +1974,7 @@ "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "variable: #9CDCFE", "hc_light": "variable: #001080" } @@ -1984,9 +1984,9 @@ "t": "text.html.handlebars meta.function.inline.other.handlebars entity.other.attribute-name.handlebars", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -2135,4 +2135,4 @@ "hc_light": "punctuation.definition.tag: #0F4A85" } } -] \ No newline at end of file +] diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test_html.json b/extensions/vscode-colorize-tests/test/colorize-results/test_html.json index a622ecb8611..2070e202ee8 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test_html.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_html.json @@ -124,9 +124,9 @@ "t": "text.html.derivative meta.tag.metadata.meta.void.html meta.attribute.charset.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -340,9 +340,9 @@ "t": "text.html.derivative meta.tag.metadata.link.void.html meta.attribute.href.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -412,9 +412,9 @@ "t": "text.html.derivative meta.tag.metadata.link.void.html meta.attribute.rel.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -544,9 +544,9 @@ "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.style.start.html meta.attribute.type.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -676,9 +676,9 @@ "t": "text.html.derivative meta.embedded.block.html source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -748,9 +748,9 @@ "t": "text.html.derivative meta.embedded.block.html source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -1024,9 +1024,9 @@ "t": "text.html.derivative meta.tag.structure.div.start.html meta.attribute.id.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1240,9 +1240,9 @@ "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.src.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1408,9 +1408,9 @@ "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.src.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -2512,9 +2512,9 @@ "t": "text.html.derivative meta.tag.structure.div.start.html meta.attribute.class.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -2632,9 +2632,9 @@ "t": "text.html.derivative meta.tag.inline.span.start.html meta.attribute.class.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -2764,9 +2764,9 @@ "t": "text.html.derivative meta.tag.inline.span.start.html meta.attribute.class.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -2884,9 +2884,9 @@ "t": "text.html.derivative meta.tag.inline.a.start.html meta.attribute.href.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -3088,9 +3088,9 @@ "t": "text.html.derivative meta.tag.inline.span.start.html meta.attribute.class.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -3208,9 +3208,9 @@ "t": "text.html.derivative meta.tag.inline.a.start.html meta.attribute.href.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test_jsx.json b/extensions/vscode-colorize-tests/test/colorize-results/test_jsx.json index bc89c8dd8b4..1b2f32d6270 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test_jsx.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_jsx.json @@ -1948,9 +1948,9 @@ "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx entity.other.attribute-name.js.jsx", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -2008,9 +2008,9 @@ "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx entity.other.attribute-name.js.jsx", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -2380,9 +2380,9 @@ "t": "source.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx entity.other.attribute-name.js.jsx", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -2452,9 +2452,9 @@ "t": "source.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx entity.other.attribute-name.js.jsx", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -2615,4 +2615,4 @@ "hc_light": "default: #292929" } } -] \ No newline at end of file +] diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test_less.json b/extensions/vscode-colorize-tests/test/colorize-results/test_less.json index 6ac176bdbbd..f26403edc50 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test_less.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_less.json @@ -340,9 +340,9 @@ "t": "source.css.less variable.other.less punctuation.definition.variable.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -352,9 +352,9 @@ "t": "source.css.less variable.other.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -436,9 +436,9 @@ "t": "source.css.less variable.other.less punctuation.definition.variable.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -448,9 +448,9 @@ "t": "source.css.less variable.other.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -484,9 +484,9 @@ "t": "source.css.less variable.other.less punctuation.definition.variable.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -496,9 +496,9 @@ "t": "source.css.less variable.other.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -592,9 +592,9 @@ "t": "source.css.less variable.other.less punctuation.definition.variable.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -604,9 +604,9 @@ "t": "source.css.less variable.other.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -664,9 +664,9 @@ "t": "source.css.less meta.property-list.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -700,9 +700,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css variable.other.less punctuation.definition.variable.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -712,9 +712,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css variable.other.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -736,9 +736,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css variable.other.less punctuation.definition.variable.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -748,9 +748,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css variable.other.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -808,9 +808,9 @@ "t": "source.css.less variable.other.less punctuation.definition.variable.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -820,9 +820,9 @@ "t": "source.css.less variable.other.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -856,9 +856,9 @@ "t": "source.css.less variable.other.less punctuation.definition.variable.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -868,9 +868,9 @@ "t": "source.css.less variable.other.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -1012,9 +1012,9 @@ "t": "source.css.less variable.other.less punctuation.definition.variable.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -1024,9 +1024,9 @@ "t": "source.css.less variable.other.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -1108,9 +1108,9 @@ "t": "source.css.less meta.property-list.css variable.other.less punctuation.definition.variable.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -1120,9 +1120,9 @@ "t": "source.css.less meta.property-list.css variable.other.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -1408,9 +1408,9 @@ "t": "source.css.less meta.property-list.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -1468,9 +1468,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css variable.other.less punctuation.definition.variable.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -1480,9 +1480,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css variable.other.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -1576,9 +1576,9 @@ "t": "source.css.less meta.property-list.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -1636,9 +1636,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css variable.other.less punctuation.definition.variable.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -1648,9 +1648,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css variable.other.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -2116,9 +2116,9 @@ "t": "source.css.less meta.property-list.css meta.property-list.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -2200,9 +2200,9 @@ "t": "source.css.less meta.property-list.css meta.property-list.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -2344,9 +2344,9 @@ "t": "source.css.less meta.property-list.css meta.property-list.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -2476,9 +2476,9 @@ "t": "source.css.less meta.property-list.css meta.property-list.css meta.property-list.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -2620,9 +2620,9 @@ "t": "source.css.less meta.property-list.css meta.property-list.css meta.property-list.css meta.property-list.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -2764,9 +2764,9 @@ "t": "source.css.less variable.other.less punctuation.definition.variable.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -2776,9 +2776,9 @@ "t": "source.css.less variable.other.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -2848,9 +2848,9 @@ "t": "source.css.less variable.other.less punctuation.definition.variable.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -2860,9 +2860,9 @@ "t": "source.css.less variable.other.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -2920,9 +2920,9 @@ "t": "source.css.less variable.other.less punctuation.definition.variable.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -2932,9 +2932,9 @@ "t": "source.css.less variable.other.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -3052,9 +3052,9 @@ "t": "source.css.less meta.property-list.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -3100,9 +3100,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css variable.other.less punctuation.definition.variable.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -3112,9 +3112,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css variable.other.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -3208,9 +3208,9 @@ "t": "source.css.less meta.property-list.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -3244,9 +3244,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css variable.other.less punctuation.definition.variable.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -3256,9 +3256,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css variable.other.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -3292,9 +3292,9 @@ "t": "source.css.less meta.property-list.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -3340,9 +3340,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css variable.other.less punctuation.definition.variable.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -3352,9 +3352,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css variable.other.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -3508,9 +3508,9 @@ "t": "source.css.less meta.property-list.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -3556,9 +3556,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css variable.other.less punctuation.definition.variable.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -3568,9 +3568,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css variable.other.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -3664,9 +3664,9 @@ "t": "source.css.less meta.property-list.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -3724,9 +3724,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css variable.other.less punctuation.definition.variable.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } @@ -3736,9 +3736,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css variable.other.less", "r": { "dark_plus": "variable.other.less: #9CDCFE", - "light_plus": "variable.other.less: #FF0000", + "light_plus": "variable.other.less: #E50000", "dark_vs": "variable.other.less: #9CDCFE", - "light_vs": "variable.other.less: #FF0000", + "light_vs": "variable.other.less: #E50000", "hc_black": "variable.other.less: #D4D4D4", "hc_light": "variable.other.less: #264F78" } diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test_md.json b/extensions/vscode-colorize-tests/test/colorize-results/test_md.json index 66549f6d2d4..78de60b2f92 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test_md.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_md.json @@ -424,9 +424,9 @@ "t": "text.html.markdown meta.tag.structure.div.start.html meta.attribute.class.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -496,9 +496,9 @@ "t": "text.html.markdown meta.tag.structure.div.start.html meta.attribute.unrecognized.markdown.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -724,9 +724,9 @@ "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -916,9 +916,9 @@ "t": "text.html.markdown meta.tag.inline.b.start.html meta.attribute.class.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1156,9 +1156,9 @@ "t": "text.html.markdown meta.embedded.block.html source.css meta.property-list.css meta.property-name.css support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test_pug.json b/extensions/vscode-colorize-tests/test/colorize-results/test_pug.json index ef09f64c9fd..e0e47e6d326 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test_pug.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_pug.json @@ -112,9 +112,9 @@ "t": "text.pug meta.tag.other entity.other.attribute-name.tag.pug", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -436,9 +436,9 @@ "t": "text.pug meta.tag.other entity.other.attribute-name.tag.pug", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -964,9 +964,9 @@ "t": "text.pug entity.other.attribute-name.class.pug", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1132,9 +1132,9 @@ "t": "text.pug entity.other.attribute-name.class.pug", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1180,9 +1180,9 @@ "t": "text.pug meta.tag.other entity.other.attribute-name.tag.pug", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1228,9 +1228,9 @@ "t": "text.pug meta.tag.other entity.other.attribute-name.tag.pug", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1276,9 +1276,9 @@ "t": "text.pug meta.tag.other entity.other.attribute-name.tag.pug", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1360,9 +1360,9 @@ "t": "text.pug meta.tag.other entity.other.attribute-name.tag.pug", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1408,9 +1408,9 @@ "t": "text.pug meta.tag.other entity.other.attribute-name.tag.pug", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1492,9 +1492,9 @@ "t": "text.pug meta.tag.other entity.other.attribute-name.tag.pug", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1540,9 +1540,9 @@ "t": "text.pug meta.tag.other entity.other.attribute-name.tag.pug", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1624,9 +1624,9 @@ "t": "text.pug meta.tag.other entity.other.attribute-name.tag.pug", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1672,9 +1672,9 @@ "t": "text.pug meta.tag.other entity.other.attribute-name.tag.pug", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1859,4 +1859,4 @@ "hc_light": "default: #292929" } } -] \ No newline at end of file +] diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test_scss.json b/extensions/vscode-colorize-tests/test/colorize-results/test_scss.json index 4e86712fa61..3d5c91da983 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test_scss.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_scss.json @@ -280,9 +280,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -436,9 +436,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -568,9 +568,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -736,9 +736,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -940,9 +940,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -1060,9 +1060,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -1204,9 +1204,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -1540,9 +1540,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -1588,9 +1588,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -1672,9 +1672,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -2776,9 +2776,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -2980,9 +2980,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -3100,9 +3100,9 @@ "t": "source.css.scss meta.definition.variable.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -3172,9 +3172,9 @@ "t": "source.css.scss meta.definition.variable.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -3340,9 +3340,9 @@ "t": "source.css.scss meta.property-list.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -3424,9 +3424,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -3460,9 +3460,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -3496,9 +3496,9 @@ "t": "source.css.scss meta.property-list.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -3580,9 +3580,9 @@ "t": "source.css.scss meta.property-list.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -3712,9 +3712,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss variable.interpolation.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -3760,9 +3760,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss variable.interpolation.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -3808,9 +3808,9 @@ "t": "source.css.scss meta.definition.variable.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -3856,9 +3856,9 @@ "t": "source.css.scss meta.definition.variable.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -3952,9 +3952,9 @@ "t": "source.css.scss entity.other.attribute-name.class.css variable.interpolation.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -4024,9 +4024,9 @@ "t": "source.css.scss meta.property-list.scss variable.interpolation.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -4180,9 +4180,9 @@ "t": "source.css.scss meta.definition.variable.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -4504,9 +4504,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -4720,9 +4720,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -4864,9 +4864,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -4996,9 +4996,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -5356,9 +5356,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -5560,9 +5560,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -5620,9 +5620,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -5692,9 +5692,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -5776,9 +5776,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -5908,9 +5908,9 @@ "t": "source.css.scss meta.definition.variable.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -5980,9 +5980,9 @@ "t": "source.css.scss meta.definition.variable.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -6112,9 +6112,9 @@ "t": "source.css.scss meta.at-rule.function.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -6208,9 +6208,9 @@ "t": "source.css.scss meta.property-list.scss meta.at-rule.return.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -6256,9 +6256,9 @@ "t": "source.css.scss meta.property-list.scss meta.at-rule.return.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -6316,9 +6316,9 @@ "t": "source.css.scss meta.property-list.scss meta.at-rule.return.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -6424,9 +6424,9 @@ "t": "source.css.scss meta.property-list.scss meta.at-rule.return.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -6520,9 +6520,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -6760,9 +6760,9 @@ "t": "source.css.scss meta.definition.variable.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -7024,9 +7024,9 @@ "t": "source.css.scss meta.at-rule.import.scss string.quoted.double.scss variable.interpolation.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -7336,9 +7336,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -7516,9 +7516,9 @@ "t": "source.css.scss meta.property-list.scss meta.at-rule.media.scss meta.property-list.media-query.scss meta.property-name.media-query.scss support.type.property-name.media.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -7612,9 +7612,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -7816,9 +7816,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -7936,9 +7936,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -8164,9 +8164,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -8284,9 +8284,9 @@ "t": "source.css.scss entity.other.attribute-name.placeholder.css punctuation.definition.entity.css", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -8296,9 +8296,9 @@ "t": "source.css.scss entity.other.attribute-name.placeholder.css", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -8344,9 +8344,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -8416,9 +8416,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -8488,9 +8488,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -8668,9 +8668,9 @@ "t": "source.css.scss meta.property-list.scss meta.at-rule.extend.scss entity.other.attribute-name.placeholder.css punctuation.definition.entity.css", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -8680,9 +8680,9 @@ "t": "source.css.scss meta.property-list.scss meta.at-rule.extend.scss entity.other.attribute-name.placeholder.css", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -8884,9 +8884,9 @@ "t": "source.css.scss meta.at-rule.mixin.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -8908,9 +8908,9 @@ "t": "source.css.scss meta.at-rule.mixin.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -9028,9 +9028,9 @@ "t": "source.css.scss meta.property-list.scss meta.at-rule.if.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -9160,9 +9160,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.at-rule.warn.scss string.quoted.double.scss variable.interpolation.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -9232,9 +9232,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -9328,9 +9328,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -9448,9 +9448,9 @@ "t": "source.css.scss meta.property-list.scss meta.at-rule.if.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -9580,9 +9580,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.at-rule.warn.scss string.quoted.double.scss variable.interpolation.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -9652,9 +9652,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -9748,9 +9748,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -9808,9 +9808,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -9880,9 +9880,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -9916,9 +9916,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -9952,9 +9952,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -9988,9 +9988,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -10324,9 +10324,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -10588,9 +10588,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -10780,9 +10780,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -10948,9 +10948,9 @@ "t": "source.css.scss meta.definition.variable.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -11080,9 +11080,9 @@ "t": "source.css.scss meta.property-list.scss meta.at-rule.if.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -11152,9 +11152,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -11296,9 +11296,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -11464,9 +11464,9 @@ "t": "source.css.scss meta.at-rule.for.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -11644,9 +11644,9 @@ "t": "source.css.scss meta.property-list.scss entity.other.attribute-name.class.css variable.interpolation.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -11704,9 +11704,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -11800,9 +11800,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -11932,9 +11932,9 @@ "t": "source.css.scss meta.at-rule.each.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -12052,9 +12052,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.property-list.scss entity.other.attribute-name.class.css variable.interpolation.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -12124,9 +12124,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.property-list.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -12220,9 +12220,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss string.quoted.single.scss variable.interpolation.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -12364,9 +12364,9 @@ "t": "source.css.scss meta.at-rule.each.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -12448,9 +12448,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -12580,9 +12580,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss entity.other.attribute-name.class.css variable.interpolation.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -12640,9 +12640,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -12736,9 +12736,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -12796,9 +12796,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -12832,9 +12832,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-value.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -13012,9 +13012,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.at-rule.function.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -13048,9 +13048,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.at-rule.function.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -13144,9 +13144,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.for.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -13240,9 +13240,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.for.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -13360,9 +13360,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.at-rule.if.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -13492,9 +13492,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.at-rule.if.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -13528,9 +13528,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.at-rule.if.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -13636,9 +13636,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.property-list.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -13900,9 +13900,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.return.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -14104,9 +14104,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -14176,9 +14176,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -14260,9 +14260,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -14356,9 +14356,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -14572,9 +14572,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -14752,9 +14752,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.at-rule.mixin.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -14776,9 +14776,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.at-rule.mixin.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -14884,9 +14884,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -14932,9 +14932,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -14968,9 +14968,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -15004,9 +15004,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -15040,9 +15040,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -15436,9 +15436,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.at-rule.mixin.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -15508,9 +15508,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-name.scss support.type.vendored.property-name.css", "r": { "dark_plus": "support.type.vendored.property-name: #9CDCFE", - "light_plus": "support.type.vendored.property-name: #FF0000", + "light_plus": "support.type.vendored.property-name: #E50000", "dark_vs": "support.type.vendored.property-name: #9CDCFE", - "light_vs": "support.type.vendored.property-name: #FF0000", + "light_vs": "support.type.vendored.property-name: #E50000", "hc_black": "support.type.vendored.property-name: #D4D4D4", "hc_light": "support.type.vendored.property-name: #264F78" } @@ -15544,9 +15544,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-value.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -15580,9 +15580,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-name.scss support.type.vendored.property-name.css", "r": { "dark_plus": "support.type.vendored.property-name: #9CDCFE", - "light_plus": "support.type.vendored.property-name: #FF0000", + "light_plus": "support.type.vendored.property-name: #E50000", "dark_vs": "support.type.vendored.property-name: #9CDCFE", - "light_vs": "support.type.vendored.property-name: #FF0000", + "light_vs": "support.type.vendored.property-name: #E50000", "hc_black": "support.type.vendored.property-name: #D4D4D4", "hc_light": "support.type.vendored.property-name: #264F78" } @@ -15616,9 +15616,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-value.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -15652,9 +15652,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -15688,9 +15688,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-value.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -16252,9 +16252,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.at-rule.mixin.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -16276,9 +16276,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.at-rule.mixin.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -16300,9 +16300,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.at-rule.mixin.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -16360,9 +16360,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -16396,9 +16396,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-value.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -16432,9 +16432,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -16468,9 +16468,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-value.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -16504,9 +16504,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -16540,9 +16540,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-value.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -16576,9 +16576,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -16828,9 +16828,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.include.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -17284,9 +17284,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -17464,9 +17464,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.at-rule.if.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -17668,9 +17668,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.attribute-selector.scss entity.other.attribute-name.attribute.scss", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -18016,9 +18016,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -18100,9 +18100,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -18364,9 +18364,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -19036,9 +19036,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.at-rule.extend.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -19084,9 +19084,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -19144,9 +19144,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -19324,9 +19324,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.mixin.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -19468,9 +19468,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.at-rule.extend.scss entity.other.attribute-name.class.css variable.interpolation.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -19564,9 +19564,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.at-rule.extend.scss variable.interpolation.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -20152,9 +20152,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -20224,9 +20224,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -20440,9 +20440,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss entity.other.attribute-name.class.css variable.interpolation.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -20524,9 +20524,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss variable.interpolation.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -20644,9 +20644,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss variable.interpolation.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -20740,9 +20740,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss variable.interpolation.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -20788,9 +20788,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss variable.interpolation.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -20896,9 +20896,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss variable.interpolation.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -21136,9 +21136,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.keyframes.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -21280,9 +21280,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.keyframes.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -21484,9 +21484,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.keyframes.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -21628,9 +21628,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.keyframes.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -21736,9 +21736,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-name.scss support.type.vendored.property-name.css", "r": { "dark_plus": "support.type.vendored.property-name: #9CDCFE", - "light_plus": "support.type.vendored.property-name: #FF0000", + "light_plus": "support.type.vendored.property-name: #E50000", "dark_vs": "support.type.vendored.property-name: #9CDCFE", - "light_vs": "support.type.vendored.property-name: #FF0000", + "light_vs": "support.type.vendored.property-name: #E50000", "hc_black": "support.type.vendored.property-name: #D4D4D4", "hc_light": "support.type.vendored.property-name: #264F78" } @@ -21832,9 +21832,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -21952,9 +21952,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -22180,9 +22180,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.keyframes.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -22324,9 +22324,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.keyframes.scss meta.property-list.scss meta.property-name.scss support.type.property-name.css", "r": { "dark_plus": "support.type.property-name: #9CDCFE", - "light_plus": "support.type.property-name: #FF0000", + "light_plus": "support.type.property-name: #E50000", "dark_vs": "support.type.property-name: #9CDCFE", - "light_vs": "support.type.property-name: #FF0000", + "light_vs": "support.type.property-name: #E50000", "hc_black": "support.type.property-name: #D4D4D4", "hc_light": "support.type.property-name: #264F78" } @@ -22468,9 +22468,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.attribute-selector.scss entity.other.attribute-name.attribute.scss", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -22720,9 +22720,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } @@ -22804,9 +22804,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss variable.scss", "r": { "dark_plus": "variable.scss: #9CDCFE", - "light_plus": "variable.scss: #FF0000", + "light_plus": "variable.scss: #E50000", "dark_vs": "variable.scss: #9CDCFE", - "light_vs": "variable.scss: #FF0000", + "light_vs": "variable.scss: #E50000", "hc_black": "variable.scss: #D4D4D4", "hc_light": "variable.scss: #264F78" } diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test_xml.json b/extensions/vscode-colorize-tests/test/colorize-results/test_xml.json index 4b149ad534e..64b2f7bc6e8 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test_xml.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_xml.json @@ -88,9 +88,9 @@ "t": "text.xml meta.tag.xml entity.other.attribute-name.localname.xml", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -208,9 +208,9 @@ "t": "text.xml meta.tag.xml entity.other.attribute-name.localname.xml", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -328,9 +328,9 @@ "t": "text.xml meta.tag.xml entity.other.attribute-name.localname.xml", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -400,9 +400,9 @@ "t": "text.xml meta.tag.xml entity.other.attribute-name.localname.xml", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -568,9 +568,9 @@ "t": "text.xml meta.tag.xml entity.other.attribute-name.localname.xml", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -640,9 +640,9 @@ "t": "text.xml meta.tag.xml entity.other.attribute-name.localname.xml", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -952,9 +952,9 @@ "t": "text.xml meta.tag.xml entity.other.attribute-name.localname.xml", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1552,9 +1552,9 @@ "t": "text.xml meta.tag.xml entity.other.attribute-name.localname.xml", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1624,9 +1624,9 @@ "t": "text.xml meta.tag.xml entity.other.attribute-name.localname.xml", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1744,9 +1744,9 @@ "t": "text.xml meta.tag.xml entity.other.attribute-name.localname.xml", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1816,9 +1816,9 @@ "t": "text.xml meta.tag.xml entity.other.attribute-name.localname.xml", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", - "light_plus": "entity.other.attribute-name: #FF0000", + "light_plus": "entity.other.attribute-name: #E50000", "dark_vs": "entity.other.attribute-name: #9CDCFE", - "light_vs": "entity.other.attribute-name: #FF0000", + "light_vs": "entity.other.attribute-name: #E50000", "hc_black": "entity.other.attribute-name: #9CDCFE", "hc_light": "entity.other.attribute-name: #264F78" } @@ -1967,4 +1967,4 @@ "hc_light": "punctuation.definition.tag: #0F4A85" } } -] \ No newline at end of file +] diff --git a/extensions/vscode-test-resolver/package.json b/extensions/vscode-test-resolver/package.json index da90e7e3809..167275aa275 100644 --- a/extensions/vscode-test-resolver/package.json +++ b/extensions/vscode-test-resolver/package.json @@ -6,7 +6,8 @@ "license": "MIT", "enableProposedApi": true, "enabledApiProposals": [ - "resolvers" + "resolvers", + "tunnels" ], "private": true, "engines": { diff --git a/extensions/vscode-test-resolver/tsconfig.json b/extensions/vscode-test-resolver/tsconfig.json index f9a183ef9e1..4a8025df9b5 100644 --- a/extensions/vscode-test-resolver/tsconfig.json +++ b/extensions/vscode-test-resolver/tsconfig.json @@ -9,6 +9,7 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", + "../../src/vscode-dts/vscode.proposed.tunnels.d.ts", "../../src/vscode-dts/vscode.proposed.resolvers.d.ts" ] } diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 919cf0aa6a8..619417cb66c 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -42,10 +42,10 @@ node-gyp-build@^4.3.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== -typescript@4.8.3: - version "4.8.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.3.tgz#d59344522c4bc464a65a730ac695007fdb66dd88" - integrity sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig== +typescript@4.8.4: + version "4.8.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" + integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== vscode-grammar-updater@^1.1.0: version "1.1.0" diff --git a/package.json b/package.json index 9957d764a7f..6a5bbde783b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", - "version": "1.72.0", - "distro": "d9fc5ec0abd44d6d2eab4ad5c0cf4ca9c4e839e1", + "version": "1.73.0", + "distro": "cac14b226f101a8186316eb3dc74f1de73c0a5be", "author": { "name": "Microsoft Corporation" }, @@ -86,13 +86,13 @@ "vscode-proxy-agent": "^0.12.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "7.0.1", - "xterm": "5.1.0-beta.1", - "xterm-addon-canvas": "0.3.0-beta.1", - "xterm-addon-search": "0.11.0-beta.1", - "xterm-addon-serialize": "0.9.0-beta.1", + "xterm": "5.1.0-beta.29", + "xterm-addon-canvas": "0.3.0-beta.13", + "xterm-addon-search": "0.11.0-beta.4", + "xterm-addon-serialize": "0.9.0-beta.2", "xterm-addon-unicode11": "0.5.0-beta.1", - "xterm-addon-webgl": "0.14.0-beta.2", - "xterm-headless": "5.1.0-beta.1", + "xterm-addon-webgl": "0.14.0-beta.20", + "xterm-headless": "5.1.0-beta.29", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, @@ -125,9 +125,10 @@ "@types/winreg": "^1.2.30", "@types/yauzl": "^2.9.1", "@types/yazl": "^2.4.2", - "@typescript-eslint/eslint-plugin": "^5.10.0", - "@typescript-eslint/experimental-utils": "^5.10.0", - "@typescript-eslint/parser": "^5.10.0", + "@typescript-eslint/eslint-plugin": "^5.39.0", + "@typescript-eslint/experimental-utils": "^5.39.0", + "@typescript-eslint/parser": "^5.39.0", + "@vscode/l10n-dev": "0.0.15", "@vscode/telemetry-extractor": "^1.9.8", "@vscode/test-web": "^0.0.29", "ansi-colors": "^3.2.3", @@ -206,7 +207,7 @@ "ts-loader": "^9.2.7", "ts-node": "^10.9.1", "tsec": "0.1.4", - "typescript": "^4.9.0-dev.20220921", + "typescript": "^4.9.0-dev.20221011", "typescript-formatter": "7.1.0", "underscore": "^1.12.1", "util": "^0.12.4", diff --git a/product.json b/product.json index d97f6cc80b5..c76943372f1 100644 --- a/product.json +++ b/product.json @@ -11,6 +11,7 @@ "serverLicensePrompt": "", "serverApplicationName": "code-server-oss", "serverDataFolderName": ".vscode-server-oss", + "tunnelApplicationName": "code-tunnel-oss", "win32DirName": "Microsoft Code OSS", "win32NameVersion": "Microsoft Code OSS", "win32RegValueName": "CodeOSS", @@ -46,7 +47,7 @@ }, { "name": "ms-vscode.js-debug", - "version": "1.71.1", + "version": "1.72.0", "repo": "https://github.com/microsoft/vscode-js-debug", "metadata": { "id": "25629058-ddac-4e17-abba-74678e126c5d", diff --git a/remote/package.json b/remote/package.json index c6d2d2547ad..a08e298af45 100644 --- a/remote/package.json +++ b/remote/package.json @@ -24,13 +24,13 @@ "vscode-proxy-agent": "^0.12.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "7.0.1", - "xterm": "5.1.0-beta.1", - "xterm-addon-canvas": "0.3.0-beta.1", - "xterm-addon-search": "0.11.0-beta.1", - "xterm-addon-serialize": "0.9.0-beta.1", + "xterm": "5.1.0-beta.29", + "xterm-addon-canvas": "0.3.0-beta.13", + "xterm-addon-search": "0.11.0-beta.4", + "xterm-addon-serialize": "0.9.0-beta.2", "xterm-addon-unicode11": "0.5.0-beta.1", - "xterm-addon-webgl": "0.14.0-beta.2", - "xterm-headless": "5.1.0-beta.1", + "xterm-addon-webgl": "0.14.0-beta.20", + "xterm-headless": "5.1.0-beta.29", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/web/package.json b/remote/web/package.json index d7e6c49c66f..962f1cc0dc3 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -11,10 +11,10 @@ "tas-client-umd": "0.1.6", "vscode-oniguruma": "1.6.1", "vscode-textmate": "7.0.1", - "xterm": "5.1.0-beta.1", - "xterm-addon-canvas": "0.3.0-beta.1", - "xterm-addon-search": "0.11.0-beta.1", + "xterm": "5.1.0-beta.29", + "xterm-addon-canvas": "0.3.0-beta.13", + "xterm-addon-search": "0.11.0-beta.4", "xterm-addon-unicode11": "0.5.0-beta.1", - "xterm-addon-webgl": "0.14.0-beta.2" + "xterm-addon-webgl": "0.14.0-beta.20" } } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index c9a0cd1bacf..679924f51d6 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -68,27 +68,27 @@ vscode-textmate@7.0.1: resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-7.0.1.tgz#8118a32b02735dccd14f893b495fa5389ad7de79" integrity sha512-zQ5U/nuXAAMsh691FtV0wPz89nSkHbs+IQV8FDk+wew9BlSDhf4UmWGlWJfTR2Ti6xZv87Tj5fENzKf6Qk7aLw== -xterm-addon-canvas@0.3.0-beta.1: - version "0.3.0-beta.1" - resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.3.0-beta.1.tgz#17a65f5da65416b01d620ddef6247ff5013ffc15" - integrity sha512-34PKhrkvK1RtlOOmni4i5GUIyoFKGMph8fWFvA2d52IDTKmX9YoLzZfU73D/sUAx+/GKobCE8sr14CuBZctgNw== +xterm-addon-canvas@0.3.0-beta.13: + version "0.3.0-beta.13" + resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.3.0-beta.13.tgz#6fd23f4c8d4d013a540cb8ff1e4965ff6bf248b5" + integrity sha512-gNl2wG5zv5AttbTs1OgMRFcARs/dt0k2snFpMmrnaTKlgpi3S0k+Sh3C4nbdMuqbCZd+n7gtpAYP+F6ZXC1pdA== -xterm-addon-search@0.11.0-beta.1: - version "0.11.0-beta.1" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.11.0-beta.1.tgz#fe7178d70246cde73550447c5524672575467499" - integrity sha512-fKj8KnnhH1nC4oZpKsgnhtgxkTctoa9kGLMpTJjsNzFu0VvXvLGIRezTPI75UEIQdEdaxcwB7/aKelQTO+72LA== +xterm-addon-search@0.11.0-beta.4: + version "0.11.0-beta.4" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.11.0-beta.4.tgz#35c01a98b092adbbd1828b0d2eea28485a9a1538" + integrity sha512-rtDBg9CWDw6c0tMeBmaKan2UWwSY88UkfkLv+g50EopDyx6J337nxgjKGioUjF0pZ1VwFOaP1+rFRk8XwLiUHA== xterm-addon-unicode11@0.5.0-beta.1: version "0.5.0-beta.1" resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.5.0-beta.1.tgz#8a9e9356018e082318abbe2be1f9599fcc6b46a2" integrity sha512-uAErX4gwhW6N524stLG6oZR3yBGgPnFmZ2Tv4vyYy7tcgDuHRoc22xYSCDgO1ohz1FLlOm8JGXRjXliwO9ic3A== -xterm-addon-webgl@0.14.0-beta.2: - version "0.14.0-beta.2" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.14.0-beta.2.tgz#832c31b52b78fb67a65bbd23c9fb850caceb43ae" - integrity sha512-1ccbkJiUZ5ojnoAEJsbdV0jMZaYSnZ02wfV8yBU243u6TTgvCzZ7nq5BR9bT+5K/ESFWiekobfybxHwuYnylmQ== +xterm-addon-webgl@0.14.0-beta.20: + version "0.14.0-beta.20" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.14.0-beta.20.tgz#1bd723a58ab6414c4773d181bbae51751827768c" + integrity sha512-inUnGzw+ewMszMQY8PqO97/t6hh/qg712uyuHLLoinDOjzXN5x2IiPMrt4LL70P6YmmTU65m/Gmfp+rd2u4yWg== -xterm@5.1.0-beta.1: - version "5.1.0-beta.1" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.1.0-beta.1.tgz#a6617c6887066d166632d1e69b6eb83a179d8b63" - integrity sha512-ml7bqjO23bh4yu7qXKogXtCy4SbDTV21rfDXUvLPPaxrlQus6NoN1byy1eFH4ONWpv5ZHGeItRdQ/X00et9Pcw== +xterm@5.1.0-beta.29: + version "5.1.0-beta.29" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.1.0-beta.29.tgz#76368661b0503800c3f4374ab380e806290038e6" + integrity sha512-qirPDkX99thKjd7bkZMBQfP6EMOWTigYQz9lFU5tIkqsoYCu7zakaakkQuC2xbVRue9nftHZItzYKeO1/ldvTA== diff --git a/remote/yarn.lock b/remote/yarn.lock index 7b384ed44b9..056cc5a71f6 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -788,40 +788,40 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -xterm-addon-canvas@0.3.0-beta.1: - version "0.3.0-beta.1" - resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.3.0-beta.1.tgz#17a65f5da65416b01d620ddef6247ff5013ffc15" - integrity sha512-34PKhrkvK1RtlOOmni4i5GUIyoFKGMph8fWFvA2d52IDTKmX9YoLzZfU73D/sUAx+/GKobCE8sr14CuBZctgNw== +xterm-addon-canvas@0.3.0-beta.13: + version "0.3.0-beta.13" + resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.3.0-beta.13.tgz#6fd23f4c8d4d013a540cb8ff1e4965ff6bf248b5" + integrity sha512-gNl2wG5zv5AttbTs1OgMRFcARs/dt0k2snFpMmrnaTKlgpi3S0k+Sh3C4nbdMuqbCZd+n7gtpAYP+F6ZXC1pdA== -xterm-addon-search@0.11.0-beta.1: - version "0.11.0-beta.1" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.11.0-beta.1.tgz#fe7178d70246cde73550447c5524672575467499" - integrity sha512-fKj8KnnhH1nC4oZpKsgnhtgxkTctoa9kGLMpTJjsNzFu0VvXvLGIRezTPI75UEIQdEdaxcwB7/aKelQTO+72LA== +xterm-addon-search@0.11.0-beta.4: + version "0.11.0-beta.4" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.11.0-beta.4.tgz#35c01a98b092adbbd1828b0d2eea28485a9a1538" + integrity sha512-rtDBg9CWDw6c0tMeBmaKan2UWwSY88UkfkLv+g50EopDyx6J337nxgjKGioUjF0pZ1VwFOaP1+rFRk8XwLiUHA== -xterm-addon-serialize@0.9.0-beta.1: - version "0.9.0-beta.1" - resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.9.0-beta.1.tgz#44a8047ec85abe4db232acc58c53355dd314bf6d" - integrity sha512-jVkpU5GC728ko0k190o+M1xubMkhRolKj18160rxlZhd0Sm/1yHUtFneC9pYSsLypynd3Te5LnZnHfEgVmka4g== +xterm-addon-serialize@0.9.0-beta.2: + version "0.9.0-beta.2" + resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.9.0-beta.2.tgz#2f37ba57cabcdbf6dfe56bce8209de04dcfaa8ef" + integrity sha512-oCRHXdlrlzcNmxRxHJlYq6FiJlgQDXft62DcE+WY7Y7R5rjPB8ahrSQHBwjc3ZIK0GRL8fRReuwxL1+Jy4Dt4Q== xterm-addon-unicode11@0.5.0-beta.1: version "0.5.0-beta.1" resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.5.0-beta.1.tgz#8a9e9356018e082318abbe2be1f9599fcc6b46a2" integrity sha512-uAErX4gwhW6N524stLG6oZR3yBGgPnFmZ2Tv4vyYy7tcgDuHRoc22xYSCDgO1ohz1FLlOm8JGXRjXliwO9ic3A== -xterm-addon-webgl@0.14.0-beta.2: - version "0.14.0-beta.2" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.14.0-beta.2.tgz#832c31b52b78fb67a65bbd23c9fb850caceb43ae" - integrity sha512-1ccbkJiUZ5ojnoAEJsbdV0jMZaYSnZ02wfV8yBU243u6TTgvCzZ7nq5BR9bT+5K/ESFWiekobfybxHwuYnylmQ== +xterm-addon-webgl@0.14.0-beta.20: + version "0.14.0-beta.20" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.14.0-beta.20.tgz#1bd723a58ab6414c4773d181bbae51751827768c" + integrity sha512-inUnGzw+ewMszMQY8PqO97/t6hh/qg712uyuHLLoinDOjzXN5x2IiPMrt4LL70P6YmmTU65m/Gmfp+rd2u4yWg== -xterm-headless@5.1.0-beta.1: - version "5.1.0-beta.1" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.1.0-beta.1.tgz#badec2e97e47aa44267a4de2c1b42b4d23ad49a2" - integrity sha512-V3G7l4pN6/HW//vKXryOCdDXVKdrQTQmtHEqkZ8waD68cJdeMdIoGYJuzavd5rHpxCqm/KR5O8ztI41jridong== +xterm-headless@5.1.0-beta.29: + version "5.1.0-beta.29" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.1.0-beta.29.tgz#b3c2bbd934fe5d9111b2206ddaf039d604aefe95" + integrity sha512-hzIzZwYtubZplfnmtx1IQdipp2eTt09zjTT6/JZFSTcTLBUCtePlgtCZPJENUyy5v+aSJgbMTDFrfjARRBCZMw== -xterm@5.1.0-beta.1: - version "5.1.0-beta.1" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.1.0-beta.1.tgz#a6617c6887066d166632d1e69b6eb83a179d8b63" - integrity sha512-ml7bqjO23bh4yu7qXKogXtCy4SbDTV21rfDXUvLPPaxrlQus6NoN1byy1eFH4ONWpv5ZHGeItRdQ/X00et9Pcw== +xterm@5.1.0-beta.29: + version "5.1.0-beta.29" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.1.0-beta.29.tgz#76368661b0503800c3f4374ab380e806290038e6" + integrity sha512-qirPDkX99thKjd7bkZMBQfP6EMOWTigYQz9lFU5tIkqsoYCu7zakaakkQuC2xbVRue9nftHZItzYKeO1/ldvTA== yallist@^4.0.0: version "4.0.0" diff --git a/scripts/test-integration.bat b/scripts/test-integration.bat index feabb6dc4e8..e5b356f3dec 100644 --- a/scripts/test-integration.bat +++ b/scripts/test-integration.bat @@ -9,40 +9,21 @@ set VSCODELOGSDIR=%~dp0\..\.build\logs\integration-tests :: Figure out which Electron to use for running tests if "%INTEGRATION_TEST_ELECTRON_PATH%"=="" ( - :: Run out of sources: no need to compile as code.bat takes care of it chcp 65001 set INTEGRATION_TEST_ELECTRON_PATH=.\scripts\code.bat set VSCODE_BUILD_BUILTIN_EXTENSIONS_SILENCE_PLEASE=1 - echo Storing crash reports into '%VSCODECRASHDIR%'. - echo Storing log files into '%VSCODELOGSDIR%'. echo Running integration tests out of sources. ) else ( - :: Run from a built: need to compile all test extensions - :: because we run extension tests from their source folders - :: and the build bundles extensions into .build webpacked - :: call yarn gulp compile-extension:vscode-api-tests^ - :: compile-extension:vscode-colorize-tests^ - :: compile-extension:markdown-language-features^ - :: compile-extension:typescript-language-features^ - :: compile-extension:emmet^ - :: compile-extension:css-language-features-server^ - :: compile-extension:html-language-features-server^ - :: compile-extension:json-language-features-server^ - :: compile-extension:git^ - :: compile-extension:ipynb^ - :: compile-extension:configuration-editing^ - :: compile-extension-media - - :: Configuration for more verbose output set VSCODE_CLI=1 set ELECTRON_ENABLE_LOGGING=1 - echo Storing crash reports into '%VSCODECRASHDIR%'. - echo Storing log files into '%VSCODELOGSDIR%'. echo Running integration tests with '%INTEGRATION_TEST_ELECTRON_PATH%' as build. ) +echo Storing crash reports into '%VSCODECRASHDIR%'. +echo Storing log files into '%VSCODELOGSDIR%'. + :: Tests standalone (AMD) diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index 4e82d1453d7..e43ef46016c 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -20,38 +20,19 @@ cd $ROOT # Figure out which Electron to use for running tests if [ -z "$INTEGRATION_TEST_ELECTRON_PATH" ] then - # Run out of sources: no need to compile as code.sh takes care of it INTEGRATION_TEST_ELECTRON_PATH="./scripts/code.sh" - echo "Storing crash reports into '$VSCODECRASHDIR'." - echo "Storing log files into '$VSCODELOGSDIR'." echo "Running integration tests out of sources." else - # Run from a built: need to compile all test extensions - # because we run extension tests from their source folders - # and the build bundles extensions into .build webpacked - # yarn gulp compile-extension:vscode-api-tests \ - # compile-extension:vscode-colorize-tests \ - # compile-extension:markdown-language-features \ - # compile-extension:typescript-language-features \ - # compile-extension:emmet \ - # compile-extension:css-language-features-server \ - # compile-extension:html-language-features-server \ - # compile-extension:json-language-features-server \ - # compile-extension:git \ - # compile-extension:ipynb \ - # compile-extension:configuration-editing \ - # compile-extension-media - - # Configuration for more verbose output export VSCODE_CLI=1 export ELECTRON_ENABLE_LOGGING=1 - echo "Storing crash reports into '$VSCODECRASHDIR'." - echo "Storing log files into '$VSCODELOGSDIR'." echo "Running integration tests with '$INTEGRATION_TEST_ELECTRON_PATH' as build." fi +echo "Storing crash reports into '$VSCODECRASHDIR'." +echo "Storing log files into '$VSCODELOGSDIR'." + # Tests standalone (AMD) diff --git a/scripts/test-remote-integration.bat b/scripts/test-remote-integration.bat index a7d0719205f..e0fda5d1c4e 100644 --- a/scripts/test-remote-integration.bat +++ b/scripts/test-remote-integration.bat @@ -19,7 +19,7 @@ IF "%VSCODEUSERDATADIR%" == "" ( set VSCODEUSERDATADIR=%TMP%\vscodeuserfolder-%RANDOM%-%TIME:~6,5% ) -set REMOTE_VSCODE=%AUTHORITY%%EXT_PATH% +set REMOTE_EXT_PATH=%AUTHORITY%%EXT_PATH% set VSCODECRASHDIR=%~dp0\..\.build\crashes set VSCODELOGSDIR=%~dp0\..\.build\logs\integration-tests-remote set TESTRESOLVER_DATA_FOLDER=%TMP%\testresolverdatafolder-%RANDOM%-%TIME:~6,5% @@ -32,45 +32,80 @@ if "%VSCODE_REMOTE_SERVER_PATH%"=="" ( echo Using '%VSCODE_REMOTE_SERVER_PATH%' as server path ) -set API_TESTS_EXTRA_ARGS=--disable-telemetry --skip-welcome --skip-release-notes --crash-reporter-directory=%VSCODECRASHDIR% --logsPath=%VSCODELOGSDIR% --no-cached-data --disable-updates --disable-keytar --disable-inspect --disable-workspace-trust --user-data-dir=%VSCODEUSERDATADIR% - :: Figure out which Electron to use for running tests if "%INTEGRATION_TEST_ELECTRON_PATH%"=="" ( - echo Storing crash reports into '%VSCODECRASHDIR%' - echo Storing log files into '%VSCODELOGSDIR%' + chcp 65001 + set INTEGRATION_TEST_ELECTRON_PATH=.\scripts\code.bat + set API_TESTS_EXTRA_ARGS_BUILT= - :: Tests in the extension host running from sources - call .\scripts\code.bat --folder-uri=%REMOTE_VSCODE%/vscode-api-tests/testWorkspace --extensionDevelopmentPath=%REMOTE_VSCODE%/vscode-api-tests --extensionTestsPath=%REMOTE_VSCODE%/vscode-api-tests/out/singlefolder-tests %API_TESTS_EXTRA_ARGS% - if %errorlevel% neq 0 exit /b %errorlevel% - - call .\scripts\code.bat --file-uri=%REMOTE_VSCODE%/vscode-api-tests/testworkspace.code-workspace --extensionDevelopmentPath=%REMOTE_VSCODE%/vscode-api-tests --extensionTestsPath=%REMOTE_VSCODE%/vscode-api-tests/out/workspace-tests %API_TESTS_EXTRA_ARGS% - if %errorlevel% neq 0 exit /b %errorlevel% + echo Running integration tests out of sources. ) else ( - echo Storing crash reports into '%VSCODECRASHDIR%' - echo Storing log files into '%VSCODELOGSDIR%' - echo Using %INTEGRATION_TEST_ELECTRON_PATH% as Electron path - - :: Run from a built: need to compile all test extensions - :: because we run extension tests from their source folders - :: and the build bundles extensions into .build webpacked - :: call yarn gulp compile-extension:vscode-api-tests^ - :: compile-extension:microsoft-authentication^ - :: compile-extension:github-authentication^ - :: compile-extension:vscode-test-resolver - - :: Configuration for more verbose output set VSCODE_CLI=1 set ELECTRON_ENABLE_LOGGING=1 - set ELECTRON_ENABLE_STACK_DUMPING=1 - :: Tests in the extension host running from built version (both client and server) - call "%INTEGRATION_TEST_ELECTRON_PATH%" --folder-uri=%REMOTE_VSCODE%/vscode-api-tests/testWorkspace --extensionDevelopmentPath=%REMOTE_VSCODE%/vscode-api-tests --extensionTestsPath=%REMOTE_VSCODE%/vscode-api-tests/out/singlefolder-tests %API_TESTS_EXTRA_ARGS% --extensions-dir=%EXT_PATH% --enable-proposed-api=vscode.vscode-test-resolver --enable-proposed-api=vscode.vscode-api-tests - if %errorlevel% neq 0 exit /b %errorlevel% + :: Extra arguments only when running against a built version + set API_TESTS_EXTRA_ARGS_BUILT=--extensions-dir=%EXT_PATH% --enable-proposed-api=vscode.vscode-test-resolver --enable-proposed-api=vscode.vscode-api-tests - call "%INTEGRATION_TEST_ELECTRON_PATH%" --file-uri=%REMOTE_VSCODE%/vscode-api-tests/testworkspace.code-workspace --extensionDevelopmentPath=%REMOTE_VSCODE%/vscode-api-tests --extensionTestsPath=%REMOTE_VSCODE%/vscode-api-tests/out/workspace-tests %API_TESTS_EXTRA_ARGS% --extensions-dir=%EXT_PATH% --enable-proposed-api=vscode.vscode-test-resolver --enable-proposed-api=vscode.vscode-api-tests - if %errorlevel% neq 0 exit /b %errorlevel% + echo Using %INTEGRATION_TEST_ELECTRON_PATH% as Electron path ) +echo Storing crash reports into '%VSCODECRASHDIR%' +echo Storing log files into '%VSCODELOGSDIR%' + + +:: Tests in the extension host + +set API_TESTS_EXTRA_ARGS=--disable-telemetry --skip-welcome --skip-release-notes --crash-reporter-directory=%VSCODECRASHDIR% --logsPath=%VSCODELOGSDIR% --no-cached-data --disable-updates --disable-keytar --disable-inspect --disable-workspace-trust --user-data-dir=%VSCODEUSERDATADIR% + +echo. +echo ### API tests (folder) +call "%INTEGRATION_TEST_ELECTRON_PATH%" --folder-uri=%REMOTE_EXT_PATH%/vscode-api-tests/testWorkspace --extensionDevelopmentPath=%REMOTE_EXT_PATH%/vscode-api-tests --extensionTestsPath=%REMOTE_EXT_PATH%/vscode-api-tests/out/singlefolder-tests %API_TESTS_EXTRA_ARGS% %API_TESTS_EXTRA_ARGS_BUILT% +if %errorlevel% neq 0 exit /b %errorlevel% + +echo. +echo ### API tests (workspace) +call "%INTEGRATION_TEST_ELECTRON_PATH%" --file-uri=%REMOTE_EXT_PATH%/vscode-api-tests/testworkspace.code-workspace --extensionDevelopmentPath=%REMOTE_EXT_PATH%/vscode-api-tests --extensionTestsPath=%REMOTE_EXT_PATH%/vscode-api-tests/out/workspace-tests %API_TESTS_EXTRA_ARGS% %API_TESTS_EXTRA_ARGS_BUILT% +if %errorlevel% neq 0 exit /b %errorlevel% + +echo. +echo ### TypeScript tests +call "%INTEGRATION_TEST_ELECTRON_PATH%" --folder-uri=%REMOTE_EXT_PATH%/typescript-language-features/test-workspace --extensionDevelopmentPath=%REMOTE_EXT_PATH%/typescript-language-features --extensionTestsPath=%REMOTE_EXT_PATH%/typescript-language-features\out\test\unit %API_TESTS_EXTRA_ARGS% %API_TESTS_EXTRA_ARGS_BUILT% +if %errorlevel% neq 0 exit /b %errorlevel% + +echo. +echo ### Markdown tests +call "%INTEGRATION_TEST_ELECTRON_PATH%" --folder-uri=%REMOTE_EXT_PATH%/markdown-language-features/test-workspace --extensionDevelopmentPath=%REMOTE_EXT_PATH%/markdown-language-features --extensionTestsPath=%REMOTE_EXT_PATH%/markdown-language-features/out/test %API_TESTS_EXTRA_ARGS% %API_TESTS_EXTRA_ARGS_BUILT% +if %errorlevel% neq 0 exit /b %errorlevel% + +echo. +echo ### Emmet tests +call "%INTEGRATION_TEST_ELECTRON_PATH%" --folder-uri=%REMOTE_EXT_PATH%/emmet/test-workspace --extensionDevelopmentPath=%REMOTE_EXT_PATH%/emmet --extensionTestsPath=%REMOTE_EXT_PATH%/emmet/out/test %API_TESTS_EXTRA_ARGS% %API_TESTS_EXTRA_ARGS_BUILT% +if %errorlevel% neq 0 exit /b %errorlevel% + +echo. +echo ### Git tests +for /f "delims=" %%i in ('node -p "require('fs').realpathSync.native(require('os').tmpdir())"') do set TEMPDIR=%%i +set GITWORKSPACE=%TEMPDIR%\git-%RANDOM% +mkdir %GITWORKSPACE% +call "%INTEGRATION_TEST_ELECTRON_PATH%" --folder-uri=%AUTHORITY%%GITWORKSPACE% --extensionDevelopmentPath=%REMOTE_EXT_PATH%/git --extensionTestsPath=%REMOTE_EXT_PATH%/git/out/test %API_TESTS_EXTRA_ARGS% %API_TESTS_EXTRA_ARGS_BUILT% +if %errorlevel% neq 0 exit /b %errorlevel% + +echo. +echo ### Ipynb tests +set IPYNBWORKSPACE=%TEMPDIR%\ipynb-%RANDOM% +mkdir %IPYNBWORKSPACE% +call "%INTEGRATION_TEST_ELECTRON_PATH%" --folder-uri=%AUTHORITY%%IPYNBWORKSPACE% --extensionDevelopmentPath=%REMOTE_EXT_PATH%/ipynb --extensionTestsPath=%REMOTE_EXT_PATH%/ipynb/out/test %API_TESTS_EXTRA_ARGS% %API_TESTS_EXTRA_ARGS_BUILT% +if %errorlevel% neq 0 exit /b %errorlevel% + +echo. +echo ### Configuration editing tests +set CFWORKSPACE=%TEMPDIR%\cf-%RANDOM% +mkdir %CFWORKSPACE% +call "%INTEGRATION_TEST_ELECTRON_PATH%" --folder-uri=%AUTHORITY%/%CFWORKSPACE% --extensionDevelopmentPath=%REMOTE_EXT_PATH%/configuration-editing --extensionTestsPath=%REMOTE_EXT_PATH%/configuration-editing/out/test %API_TESTS_EXTRA_ARGS% %API_TESTS_EXTRA_ARGS_BUILT% +if %errorlevel% neq 0 exit /b %errorlevel% + +:: Cleanup + IF "%3" == "" ( rmdir /s /q %VSCODEUSERDATADIR% ) diff --git a/scripts/test-remote-integration.sh b/scripts/test-remote-integration.sh index 6d883f53217..8163e796645 100755 --- a/scripts/test-remote-integration.sh +++ b/scripts/test-remote-integration.sh @@ -34,40 +34,19 @@ export REMOTE_VSCODE=$AUTHORITY$EXT_PATH # Figure out which Electron to use for running tests if [ -z "$INTEGRATION_TEST_ELECTRON_PATH" ] then - # Run out of sources: no need to compile as code.sh takes care of it INTEGRATION_TEST_ELECTRON_PATH="./scripts/code.sh" # No extra arguments when running out of sources EXTRA_INTEGRATION_TEST_ARGUMENTS="" - echo "Storing crash reports into '$VSCODECRASHDIR'." - echo "Storing log files into '$VSCODELOGSDIR'." echo "Running remote integration tests out of sources." else - # Run from a built: need to compile all test extensions - # because we run extension tests from their source folders - # and the build bundles extensions into .build webpacked - # yarn gulp compile-extension:vscode-api-tests \ - # compile-extension:vscode-test-resolver \ - # compile-extension:markdown-language-features \ - # compile-extension:typescript-language-features \ - # compile-extension:emmet \ - # compile-extension:git \ - # compile-extension:ipynb \ - # compile-extension:configuration-editing \ - # compile-extension:microsoft-authentication \ - # compile-extension:github-authentication \ - # compile-extension-media - - # Configuration for more verbose output export VSCODE_CLI=1 export ELECTRON_ENABLE_LOGGING=1 # Running from a build, we need to enable the vscode-test-resolver extension EXTRA_INTEGRATION_TEST_ARGUMENTS="--extensions-dir=$EXT_PATH --enable-proposed-api=vscode.vscode-test-resolver --enable-proposed-api=vscode.vscode-api-tests" - echo "Storing crash reports into '$VSCODECRASHDIR'." - echo "Storing log files into '$VSCODELOGSDIR'." echo "Running remote integration tests with $INTEGRATION_TEST_ELECTRON_PATH as build." fi @@ -91,6 +70,12 @@ fi API_TESTS_EXTRA_ARGS="--disable-telemetry --skip-welcome --skip-release-notes --crash-reporter-directory=$VSCODECRASHDIR --logsPath=$VSCODELOGSDIR --no-cached-data --disable-updates --disable-keytar --disable-workspace-trust --user-data-dir=$VSCODEUSERDATADIR" +echo "Storing crash reports into '$VSCODECRASHDIR'." +echo "Storing log files into '$VSCODELOGSDIR'." + + +# Tests in the extension host + echo echo "### API tests (folder)" echo diff --git a/scripts/test-web-integration.bat b/scripts/test-web-integration.bat index 7be27c00b31..700dd0a045e 100644 --- a/scripts/test-web-integration.bat +++ b/scripts/test-web-integration.bat @@ -18,20 +18,11 @@ IF "%~1" == "" ( set REMOTE_VSCODE=%AUTHORITY%%EXT_PATH% if "%VSCODE_REMOTE_SERVER_PATH%"=="" ( + chcp 65001 + echo Using remote server out of sources for integration web tests ) else ( echo Using '%VSCODE_REMOTE_SERVER_PATH%' as server path for web integration tests - - :: Run from a built: need to compile all test extensions - :: because we run extension tests from their source folders - :: and the build bundles extensions into .build webpacked - :: call yarn gulp compile-extension:vscode-api-tests^ - :: compile-extension:markdown-language-features^ - :: compile-extension:typescript-language-features^ - :: compile-extension:emmet^ - :: compile-extension:configuration-editing^ - :: compile-extension:git^ - :: compile-extension-media ) if not exist ".\test\integration\browser\out\index.js" ( @@ -39,6 +30,9 @@ if not exist ".\test\integration\browser\out\index.js" ( call yarn playwright-install ) + +:: Tests in the extension host + echo. echo ### API tests (folder) call node .\test\integration\browser\out\index.js --workspacePath=.\extensions\vscode-api-tests\testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=.\extensions\vscode-api-tests --extensionTestsPath=.\extensions\vscode-api-tests\out\singlefolder-tests %* @@ -72,9 +66,20 @@ mkdir %GITWORKSPACE% call node .\test\integration\browser\out\index.js --workspacePath=%GITWORKSPACE% --extensionDevelopmentPath=.\extensions\git --extensionTestsPath=.\extensions\git\out\test %* if %errorlevel% neq 0 exit /b %errorlevel% +echo. +echo ### Ipynb tests +set IPYNBWORKSPACE=%TEMPDIR%\ipynb-%RANDOM% +mkdir %IPYNBWORKSPACE% +call node .\test\integration\browser\out\index.js --workspacePath=%IPYNBWORKSPACE% --extensionDevelopmentPath=.\extensions\ipynb --extensionTestsPath=.\extensions\ipynb\out\test %* +if %errorlevel% neq 0 exit /b %errorlevel% + echo. echo ### Configuration editing tests set CFWORKSPACE=%TEMPDIR%\git-%RANDOM% mkdir %CFWORKSPACE% call node .\test\integration\browser\out\index.js --workspacePath=%CFWORKSPACE% --extensionDevelopmentPath=.\extensions\configuration-editing --extensionTestsPath=.\extensions\configuration-editing\out\test %* if %errorlevel% neq 0 exit /b %errorlevel% + +popd + +endlocal diff --git a/scripts/test-web-integration.sh b/scripts/test-web-integration.sh index 95278eec0e3..0648a7f8cd8 100755 --- a/scripts/test-web-integration.sh +++ b/scripts/test-web-integration.sh @@ -15,18 +15,6 @@ then echo "Using remote server out of sources for integration web tests" else echo "Using $VSCODE_REMOTE_SERVER_PATH as server path for web integration tests" - - # Run from a built: need to compile all test extensions - # because we run extension tests from their source folders - # and the build bundles extensions into .build webpacked - # yarn gulp compile-extension:vscode-api-tests \ - # compile-extension:markdown-language-features \ - # compile-extension:typescript-language-features \ - # compile-extension:emmet \ - # compile-extension:git \ - # compile-extension:ipynb \ - # compile-extension:configuration-editing \ - # compile-extension-media fi if [ ! -e 'test/integration/browser/out/index.js' ];then @@ -34,6 +22,7 @@ if [ ! -e 'test/integration/browser/out/index.js' ];then yarn playwright-install fi + # Tests in the extension host echo diff --git a/src/vs/base/browser/defaultWorkerFactory.ts b/src/vs/base/browser/defaultWorkerFactory.ts index 9d2a34494be..87103de1a54 100644 --- a/src/vs/base/browser/defaultWorkerFactory.ts +++ b/src/vs/base/browser/defaultWorkerFactory.ts @@ -43,11 +43,20 @@ export function getWorkerBootstrapUrl(scriptPath: string, label: string): string return URL.createObjectURL(blob); } - const result = new URL(scriptPath); - COI.addSearchParam(result.searchParams, true, true); - result.hash = label; - return result.href; + const start = scriptPath.lastIndexOf('?'); + const end = scriptPath.lastIndexOf('#', start); + const params = start > 0 + ? new URLSearchParams(scriptPath.substring(start + 1, ~end ? end : undefined)) + : new URLSearchParams(); + COI.addSearchParam(params, true, true); + const search = params.toString(); + + if (!search) { + return `${scriptPath}#${label}`; + } else { + return `${scriptPath}?${params.toString()}#${label}`; + } } // ESM-comment-end diff --git a/src/vs/base/browser/globalPointerMoveMonitor.ts b/src/vs/base/browser/globalPointerMoveMonitor.ts index d5c432114d7..0348db2528f 100644 --- a/src/vs/base/browser/globalPointerMoveMonitor.ts +++ b/src/vs/base/browser/globalPointerMoveMonitor.ts @@ -64,7 +64,17 @@ export class GlobalPointerMoveMonitor implements IDisposable { try { initialElement.setPointerCapture(pointerId); this._hooks.add(toDisposable(() => { - initialElement.releasePointerCapture(pointerId); + try { + initialElement.releasePointerCapture(pointerId); + } catch (err) { + // See https://github.com/microsoft/vscode/issues/161731 + // + // `releasePointerCapture` sometimes fails when being invoked with the exception: + // DOMException: Failed to execute 'releasePointerCapture' on 'Element': + // No active pointer with the given id is found. + // + // There's no need to do anything in case of failure + } })); } catch (err) { // See https://github.com/microsoft/vscode/issues/144584 diff --git a/src/vs/base/browser/ui/actionbar/actionViewItems.ts b/src/vs/base/browser/ui/actionbar/actionViewItems.ts index 0e97d180ef1..02e9bc99513 100644 --- a/src/vs/base/browser/ui/actionbar/actionViewItems.ts +++ b/src/vs/base/browser/ui/actionbar/actionViewItems.ts @@ -435,9 +435,11 @@ export class SelectActionViewItem extends BaseActionViewItem { } private registerListeners(): void { - this._register(this.selectBox.onDidSelect(e => { - this.actionRunner.run(this._action, this.getActionContext(e.selected, e.index)); - })); + this._register(this.selectBox.onDidSelect(e => this.runAction(e.selected, e.index))); + } + + protected runAction(option: string, index: number): void { + this.actionRunner.run(this._action, this.getActionContext(option, index)); } protected getActionContext(option: string, index: number) { diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index bf63bee9f9e..a0f509ed0ae 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -10,7 +10,7 @@ import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; import { ActionRunner, IAction, IActionRunner, IRunEvent, Separator } from 'vs/base/common/actions'; import { Emitter } from 'vs/base/common/event'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableMap, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; import * as types from 'vs/base/common/types'; import 'vs/css!./actionbar'; @@ -62,7 +62,7 @@ export class ActionBar extends Disposable implements IActionRunner { private readonly options: IActionBarOptions; private _actionRunner: IActionRunner; - private _actionRunnerDisposables = this._register(new DisposableStore()); + private readonly _actionRunnerDisposables = this._register(new DisposableStore()); private _context: unknown; private readonly _orientation: ActionsOrientation; private readonly _triggerKeys: { @@ -72,7 +72,7 @@ export class ActionBar extends Disposable implements IActionRunner { // View Items viewItems: IActionViewItem[]; - private viewItemDisposables: Map; + private readonly viewItemDisposables = this._register(new DisposableMap()); private previouslyFocusedItem?: number; protected focusedItem?: number; private focusTracker: DOM.IFocusTracker; @@ -121,7 +121,6 @@ export class ActionBar extends Disposable implements IActionRunner { this._actionRunnerDisposables.add(this._actionRunner.onWillRun(e => this._onWillRun.fire(e))); this.viewItems = []; - this.viewItemDisposables = new Map(); this.focusedItem = undefined; this.domNode = document.createElement('div'); @@ -413,18 +412,15 @@ export class ActionBar extends Disposable implements IActionRunner { pull(index: number): void { if (index >= 0 && index < this.viewItems.length) { this.actionsList.removeChild(this.actionsList.childNodes[index]); - this.viewItemDisposables.get(this.viewItems[index])?.dispose(); - this.viewItemDisposables.delete(this.viewItems[index]); + this.viewItemDisposables.deleteAndDispose(this.viewItems[index]); dispose(this.viewItems.splice(index, 1)); this.refreshRole(); } } clear(): void { - dispose(this.viewItems); - dispose(this.viewItemDisposables.values()); - this.viewItemDisposables.clear(); - this.viewItems = []; + this.viewItems = dispose(this.viewItems); + this.viewItemDisposables.clearAndDisposeAll(); DOM.clearNode(this.actionsList); this.refreshRole(); } @@ -579,8 +575,7 @@ export class ActionBar extends Disposable implements IActionRunner { } override dispose(): void { - dispose(this.viewItems); - this.viewItems = []; + this.viewItems = dispose(this.viewItems); this.getContainer().remove(); super.dispose(); } diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf index d8ac87cba86..bab11139af8 100644 Binary files a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf and b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf differ diff --git a/src/vs/base/browser/ui/inputbox/inputBox.css b/src/vs/base/browser/ui/inputbox/inputBox.css index b3dba065a19..55b552b724f 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.css +++ b/src/vs/base/browser/ui/inputbox/inputBox.css @@ -8,6 +8,7 @@ display: block; padding: 0; box-sizing: border-box; + border-radius: 2px; /* Customizable */ font-size: inherit; @@ -21,7 +22,7 @@ .monaco-inputbox > .ibwrapper > .mirror { /* Customizable */ - padding: 4px; + padding: 4px 6px; } .monaco-inputbox > .ibwrapper { diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index ae89cb33f38..a9f2b326c28 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -58,7 +58,7 @@ export interface IInputValidator { } export interface IMessage { - readonly content: string; + readonly content?: string; readonly formatContent?: boolean; // defaults to false readonly type?: MessageType; } @@ -396,7 +396,7 @@ export class InputBox extends Widget { const styles = this.stylesForType(this.message.type); this.element.style.border = styles.border ? `1px solid ${styles.border}` : ''; - if (this.hasFocus() || force) { + if (this.message.content && (this.hasFocus() || force)) { this._showMessage(); } } @@ -477,8 +477,8 @@ export class InputBox extends Widget { }; const spanElement = (this.message.formatContent - ? renderFormattedText(this.message.content, renderOptions) - : renderText(this.message.content, renderOptions)); + ? renderFormattedText(this.message.content!, renderOptions) + : renderText(this.message.content!, renderOptions)); spanElement.classList.add(this.classForType(this.message.type)); const styles = this.stylesForType(this.message.type); diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 577ea7879dc..82fb5b18639 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -406,8 +406,12 @@ export class ListView implements ISpliceable, IDisposable { } } - triggerScrollFromMouseWheelEvent(browserEvent: IMouseWheelEvent) { - this.scrollableElement.triggerScrollFromMouseWheelEvent(browserEvent); + delegateScrollFromMouseWheelEvent(browserEvent: IMouseWheelEvent) { + this.scrollableElement.delegateScrollFromMouseWheelEvent(browserEvent); + } + + delegateVerticalScrollbarPointerDown(browserEvent: PointerEvent) { + this.scrollableElement.delegateVerticalScrollbarPointerDown(browserEvent); } updateElementHeight(index: number, size: number | undefined, anchorIndex: number | null): void { @@ -1031,6 +1035,7 @@ export class ListView implements ISpliceable, IDisposable { setTimeout(() => document.body.removeChild(dragImage), 0); } + this.domNode.classList.add('dragging'); this.currentDragData = new ElementsDragAndDropData(elements); StaticDND.CurrentDragAndDropData = new ExternalElementsDragAndDropData(elements); @@ -1146,6 +1151,7 @@ export class ListView implements ISpliceable, IDisposable { const dragData = this.currentDragData; this.teardownDragAndDropScrollTopAnimation(); this.clearDragOverFeedback(); + this.domNode.classList.remove('dragging'); this.currentDragData = undefined; StaticDND.CurrentDragAndDropData = undefined; @@ -1162,6 +1168,7 @@ export class ListView implements ISpliceable, IDisposable { this.canDrop = false; this.teardownDragAndDropScrollTopAnimation(); this.clearDragOverFeedback(); + this.domNode.classList.remove('dragging'); this.currentDragData = undefined; StaticDND.CurrentDragAndDropData = undefined; diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 8ed5f2d8602..a0d02b2c621 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -869,11 +869,11 @@ export class DefaultStyleController implements IStyleController { } if (styles.listHoverBackground) { - content.push(`.monaco-list${suffix}:not(.drop-target) .monaco-list-row:hover:not(.selected):not(.focused) { background-color: ${styles.listHoverBackground}; }`); + content.push(`.monaco-list${suffix}:not(.drop-target):not(.dragging) .monaco-list-row:hover:not(.selected):not(.focused) { background-color: ${styles.listHoverBackground}; }`); } if (styles.listHoverForeground) { - content.push(`.monaco-list${suffix} .monaco-list-row:hover:not(.selected):not(.focused) { color: ${styles.listHoverForeground}; }`); + content.push(`.monaco-list${suffix}:not(.drop-target):not(.dragging) .monaco-list-row:hover:not(.selected):not(.focused) { color: ${styles.listHoverForeground}; }`); } if (styles.listSelectionOutline) { diff --git a/src/vs/base/browser/ui/sash/sash.ts b/src/vs/base/browser/ui/sash/sash.ts index 4bc8ff1b8ee..5cbf7e6f192 100644 --- a/src/vs/base/browser/ui/sash/sash.ts +++ b/src/vs/base/browser/ui/sash/sash.ts @@ -5,7 +5,7 @@ import { $, append, createStyleSheet, EventHelper, EventLike } from 'vs/base/browser/dom'; import { DomEmitter } from 'vs/base/browser/event'; -import { EventType, Gesture, GestureEvent } from 'vs/base/browser/touch'; +import { EventType, Gesture } from 'vs/base/browser/touch'; import { Delayer } from 'vs/base/common/async'; import { memoize } from 'vs/base/common/decorators'; import { Emitter, Event } from 'vs/base/common/event'; @@ -155,6 +155,7 @@ interface PointerEvent extends EventLike { readonly pageY: number; readonly altKey: boolean; readonly target: EventTarget | null; + readonly initialTarget?: EventTarget | undefined; } interface IPointerEventFactory { @@ -419,17 +420,22 @@ export class Sash extends Disposable { this._register(Gesture.addTarget(this.el)); - const onTouchStart = Event.map(this._register(new DomEmitter(this.el, EventType.Start)).event, e => ({ ...e, target: e.initialTarget ?? null })); + const onTouchStart = this._register(new DomEmitter(this.el, EventType.Start)).event; this._register(onTouchStart(e => this.onPointerStart(e, new GestureEventFactory(this.el)), this)); const onTap = this._register(new DomEmitter(this.el, EventType.Tap)).event; - const onDoubleTap = Event.map( - Event.filter( - Event.debounce(onTap, (res, event) => ({ event, count: (res?.count ?? 0) + 1 }), 250), - ({ count }) => count === 2 - ), - ({ event }) => ({ ...event, target: event.initialTarget ?? null }) - ); - this._register(onDoubleTap(this.onPointerDoublePress, this)); + + let doubleTapTimeout: any = undefined; + this._register(onTap(event => { + if (doubleTapTimeout) { + clearTimeout(doubleTapTimeout); + doubleTapTimeout = undefined; + this.onPointerDoublePress(event); + return; + } + + clearTimeout(doubleTapTimeout); + doubleTapTimeout = setTimeout(() => doubleTapTimeout = undefined, 250); + }, this)); if (typeof options.size === 'number') { this.size = options.size; @@ -645,12 +651,14 @@ export class Sash extends Disposable { } private getOrthogonalSash(e: PointerEvent): Sash | undefined { - if (!e.target || !(e.target instanceof HTMLElement)) { + const target = e.initialTarget ?? e.target; + + if (!target || !(target instanceof HTMLElement)) { return undefined; } - if (e.target.classList.contains('orthogonal-drag-handle')) { - return e.target.classList.contains('start') ? this.orthogonalStartSash : this.orthogonalEndSash; + if (target.classList.contains('orthogonal-drag-handle')) { + return target.classList.contains('start') ? this.orthogonalStartSash : this.orthogonalEndSash; } return undefined; diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts index c7bffd0c8d6..b6b6c32a1dc 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts @@ -334,7 +334,7 @@ export abstract class AbstractScrollableElement extends Widget { this._revealOnScroll = value; } - public triggerScrollFromMouseWheelEvent(browserEvent: IMouseWheelEvent) { + public delegateScrollFromMouseWheelEvent(browserEvent: IMouseWheelEvent) { this._onMouseWheel(new StandardWheelEvent(browserEvent)); } diff --git a/src/vs/base/browser/ui/selectBox/selectBox.css b/src/vs/base/browser/ui/selectBox/selectBox.css index c0a54d83b27..d4a46f25f86 100644 --- a/src/vs/base/browser/ui/selectBox/selectBox.css +++ b/src/vs/base/browser/ui/selectBox/selectBox.css @@ -6,6 +6,7 @@ .monaco-select-box { width: 100%; cursor: pointer; + border-radius: 2px; } .monaco-select-box-dropdown-container { diff --git a/src/vs/base/browser/ui/selectBox/selectBox.ts b/src/vs/base/browser/ui/selectBox/selectBox.ts index fbf46c262fe..d31b8828697 100644 --- a/src/vs/base/browser/ui/selectBox/selectBox.ts +++ b/src/vs/base/browser/ui/selectBox/selectBox.ts @@ -14,6 +14,7 @@ import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import { deepClone } from 'vs/base/common/objects'; import { isMacintosh } from 'vs/base/common/platform'; +import { IThemable } from 'vs/base/common/styler'; import 'vs/css!./selectBox'; @@ -77,7 +78,7 @@ export interface ISelectData { index: number; } -export class SelectBox extends Widget implements ISelectBoxDelegate { +export class SelectBox extends Widget implements ISelectBoxDelegate, IThemable { private selectBoxDelegate: ISelectBoxDelegate; constructor(options: ISelectOptionItem[], selected: number, contextViewProvider: IContextViewProvider, styles: ISelectBoxStyles = deepClone(defaultStyles), selectBoxOptions?: ISelectBoxOptions) { diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index cc09126b530..cfdaca03d75 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -15,7 +15,7 @@ import { ISelectBoxDelegate, ISelectBoxOptions, ISelectBoxStyles, ISelectData, I import * as arrays from 'vs/base/common/arrays'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode, KeyCodeUtils } from 'vs/base/common/keyCodes'; -import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { isMacintosh } from 'vs/base/common/platform'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import 'vs/css!./selectBoxCustom'; @@ -31,7 +31,6 @@ interface ISelectListTemplateData { text: HTMLElement; detail: HTMLElement; decoratorRight: HTMLElement; - disposables: IDisposable[]; } class SelectListRenderer implements IListRenderer { @@ -40,7 +39,6 @@ class SelectListRenderer implements IListRenderer()); readonly onDidChangeExpansionState: Event = this._onDidChangeExpansionState.event; + get ariaHeaderLabel(): string { + return this._ariaHeaderLabel; + } + + set ariaHeaderLabel(newLabel: string) { + this._ariaHeaderLabel = newLabel; + this.header.setAttribute('aria-label', this.ariaHeaderLabel); + } + get draggableElement(): HTMLElement { return this.header; } @@ -127,7 +136,7 @@ export abstract class Pane extends Disposable implements IView { super(); this._expanded = typeof options.expanded === 'undefined' ? true : !!options.expanded; this._orientation = typeof options.orientation === 'undefined' ? Orientation.VERTICAL : options.orientation; - this.ariaHeaderLabel = localize('viewSection', "{0} Section", options.title); + this._ariaHeaderLabel = localize('viewSection', "{0} Section", options.title); this._minimumBodySize = typeof options.minimumBodySize === 'number' ? options.minimumBodySize : this._orientation === Orientation.HORIZONTAL ? 200 : 120; this._maximumBodySize = typeof options.maximumBodySize === 'number' ? options.maximumBodySize : Number.POSITIVE_INFINITY; diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index 159c58a2797..999d2cadfd8 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -556,8 +556,13 @@ export class SplitView extends Disposable { this.onDidScroll = this.scrollableElement.onScroll; this._register(this.onDidScroll(e => { - this.viewContainer.scrollTop = e.scrollTop; - this.viewContainer.scrollLeft = e.scrollLeft; + if (e.scrollTopChanged) { + this.viewContainer.scrollTop = e.scrollTop; + } + + if (e.scrollLeftChanged) { + this.viewContainer.scrollLeft = e.scrollLeft; + } })); append(this.el, this.scrollableElement.getDomNode()); diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index d7f39454a0f..6ebd8c2a853 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -140,6 +140,10 @@ export class ToolBar extends Disposable { return this.element; } + focus(): void { + this.actionBar.focus(); + } + getItemsWidth(): number { let itemsWidth = 0; for (let i = 0; i < this.actionBar.length(); i++) { diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index f592bca1564..c82ff404931 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -298,11 +298,6 @@ interface ITreeRendererOptions { readonly hideTwistiesOfChildlessElements?: boolean; } -interface IRenderData { - templateData: ITreeListTemplateData; - height: number; -} - interface Collection { readonly elements: T[]; readonly onDidChange: Event; @@ -332,12 +327,11 @@ class TreeRenderer implements IListRenderer readonly templateId: string; private renderedElements = new Map>(); - private renderedNodes = new Map, IRenderData>(); + private renderedNodes = new Map, ITreeListTemplateData>(); private indent: number = TreeRenderer.DefaultIndent; private hideTwistiesOfChildlessElements: boolean = false; private shouldRenderIndentGuides: boolean = false; - private renderedIndentGuides = new SetMap, HTMLDivElement>(); private activeIndentNodes = new Set>(); private indentGuidesDisposable: IDisposable = Disposable.None; @@ -348,19 +342,27 @@ class TreeRenderer implements IListRenderer private modelProvider: () => ITreeModel, onDidChangeCollapseState: Event>, private activeNodes: Collection>, + private renderedIndentGuides: SetMap, HTMLDivElement>, options: ITreeRendererOptions = {} ) { this.templateId = renderer.templateId; this.updateOptions(options); Event.map(onDidChangeCollapseState, e => e.node)(this.onDidChangeNodeTwistieState, this, this.disposables); - renderer.onDidChangeTwistieState?.(this.onDidChangeTwistieState, this, this.disposables); } updateOptions(options: ITreeRendererOptions = {}): void { if (typeof options.indent !== 'undefined') { - this.indent = clamp(options.indent, 0, 40); + const indent = clamp(options.indent, 0, 40); + + if (indent !== this.indent) { + this.indent = indent; + + for (const [node, templateData] of this.renderedNodes) { + this.renderTreeElement(node, templateData); + } + } } if (typeof options.renderIndentGuides !== 'undefined') { @@ -396,21 +398,9 @@ class TreeRenderer implements IListRenderer } renderElement(node: ITreeNode, index: number, templateData: ITreeListTemplateData, height: number | undefined): void { - if (typeof height === 'number') { - this.renderedNodes.set(node, { templateData, height }); - this.renderedElements.set(node.element, node); - } - - const indent = TreeRenderer.DefaultIndent + (node.depth - 1) * this.indent; - templateData.twistie.style.paddingLeft = `${indent}px`; - templateData.indent.style.width = `${indent + this.indent - 16}px`; - - this.renderTwistie(node, templateData); - - if (typeof height === 'number') { - this.renderIndentGuides(node, templateData); - } - + this.renderedNodes.set(node, templateData); + this.renderedElements.set(node.element, node); + this.renderTreeElement(node, templateData); this.renderer.renderElement(node, index, templateData.templateData, height); } @@ -440,18 +430,27 @@ class TreeRenderer implements IListRenderer } private onDidChangeNodeTwistieState(node: ITreeNode): void { - const data = this.renderedNodes.get(node); + const templateData = this.renderedNodes.get(node); - if (!data) { + if (!templateData) { return; } - this.renderTwistie(node, data.templateData); this._onDidChangeActiveNodes(this.activeNodes.elements); - this.renderIndentGuides(node, data.templateData); + this.renderTreeElement(node, templateData); } - private renderTwistie(node: ITreeNode, templateData: ITreeListTemplateData) { + private renderTreeElement(node: ITreeNode, templateData: ITreeListTemplateData) { + const indent = TreeRenderer.DefaultIndent + (node.depth - 1) * this.indent; + templateData.twistie.style.paddingLeft = `${indent}px`; + templateData.indent.style.width = `${indent + this.indent - 16}px`; + + if (node.collapsible) { + templateData.container.setAttribute('aria-expanded', String(!node.collapsed)); + } else { + templateData.container.removeAttribute('aria-expanded'); + } + templateData.twistie.classList.remove(...Codicon.treeItemExpanded.classNamesArray); let twistieRendered = false; @@ -471,14 +470,6 @@ class TreeRenderer implements IListRenderer templateData.twistie.classList.remove('collapsible', 'collapsed'); } - if (node.collapsible) { - templateData.container.setAttribute('aria-expanded', String(!node.collapsed)); - } else { - templateData.container.removeAttribute('aria-expanded'); - } - } - - private renderIndentGuides(target: ITreeNode, templateData: ITreeListTemplateData): void { clearNode(templateData.indent); templateData.indentGuidesDisposable.dispose(); @@ -489,8 +480,6 @@ class TreeRenderer implements IListRenderer const disposableStore = new DisposableStore(); const model = this.modelProvider(); - let node = target; - while (true) { const ref = model.getNodeLocation(node); const parentRef = model.getParentNodeLocation(ref); @@ -695,11 +684,20 @@ class FindWidget extends Disposable { this.findInput.inputBox.setPlaceHolder(mode === TreeFindMode.Filter ? localize('type to filter', "Type to filter") : localize('type to search', "Type to search")); } + get value(): string { + return this.findInput.inputBox.value; + } + + set value(value: string) { + this.findInput.inputBox.value = value; + } + private readonly modeToggle: ModeToggle; private readonly findInput: FindInput; private readonly actionbar: ActionBar; private width = 0; private right = 0; + private top = 0; readonly _onDidDisable = new Emitter(); readonly onDidDisable = this._onDidDisable.event; @@ -757,11 +755,18 @@ class FindWidget extends Disposable { const startRight = this.right; const startX = e.pageX; + const startTop = this.top; + const startY = e.pageY; this.elements.grab.classList.add('grabbing'); + const transition = this.elements.root.style.transition; + this.elements.root.style.transition = 'unset'; + const update = (e: MouseEvent) => { const deltaX = e.pageX - startX; this.right = startRight - deltaX; + const deltaY = e.pageY - startY; + this.top = startTop + deltaY; this.layout(); }; @@ -769,6 +774,7 @@ class FindWidget extends Disposable { disposables.add(onWindowMouseUp.event(e => { update(e); this.elements.grab.classList.remove('grabbing'); + this.elements.root.style.transition = transition; disposables.dispose(); })); })); @@ -779,6 +785,7 @@ class FindWidget extends Disposable { this._register(onGrabKeyDown((e): any => { let right: number | undefined; + let top: number | undefined; if (e.keyCode === KeyCode.LeftArrow) { right = Number.POSITIVE_INFINITY; @@ -788,12 +795,30 @@ class FindWidget extends Disposable { right = this.right === 0 ? Number.POSITIVE_INFINITY : 0; } + if (e.keyCode === KeyCode.UpArrow) { + top = 0; + } else if (e.keyCode === KeyCode.DownArrow) { + top = Number.POSITIVE_INFINITY; + } + if (right !== undefined) { e.preventDefault(); e.stopPropagation(); this.right = right; this.layout(); } + + if (top !== undefined) { + e.preventDefault(); + e.stopPropagation(); + this.top = top; + const transition = this.elements.root.style.transition; + this.elements.root.style.transition = 'unset'; + this.layout(); + setTimeout(() => { + this.elements.root.style.transition = transition; + }, 0); + } })); this.onDidChangeValue = this.findInput.onDidChange; @@ -824,6 +849,8 @@ class FindWidget extends Disposable { this.width = width; this.right = clamp(this.right, 0, Math.max(0, width - 212)); this.elements.root.style.right = `${this.right}px`; + this.top = clamp(this.top, 0, 24); + this.elements.root.style.top = `${this.top}px`; } showMessage(message: IMessage): void { @@ -846,6 +873,7 @@ class FindController implements IDisposable { private _pattern = ''; get pattern(): string { return this._pattern; } + private previousPattern = ''; private _mode: TreeFindMode; get mode(): TreeFindMode { return this._mode; } @@ -910,6 +938,9 @@ class FindController implements IDisposable { this.widget.layout(this.width); this.widget.focus(); + this.widget.value = this.previousPattern; + this.widget.select(); + this._onDidChangeOpenState.fire(true); } @@ -923,6 +954,7 @@ class FindController implements IDisposable { this.enabledDisposables.dispose(); this.enabledDisposables = new DisposableStore(); + this.previousPattern = this.pattern; this.onDidChangeValue(''); this.tree.domFocus(); @@ -966,7 +998,11 @@ class FindController implements IDisposable { const noMatches = this.filter.totalCount > 0 && this.filter.matchCount === 0; if (this.pattern && noMatches) { - this.widget?.showMessage({ type: MessageType.WARNING, content: localize('not found', "No elements found.") }); + if (this.tree.options.showNotFoundMessage ?? true) { + this.widget?.showMessage({ type: MessageType.WARNING, content: localize('not found', "No elements found.") }); + } else { + this.widget?.showMessage({ type: MessageType.WARNING }); + } } else { this.widget?.clearMessage(); } @@ -1032,6 +1068,7 @@ export interface IAbstractTreeOptionsUpdate extends ITreeRendererOptions { readonly typeNavigationEnabled?: boolean; readonly typeNavigationMode?: TypeNavigationMode; readonly defaultFindMode?: TreeFindMode; + readonly showNotFoundMessage?: boolean; readonly smoothScrolling?: boolean; readonly horizontalScrolling?: boolean; readonly mouseWheelScrollSensitivity?: number; @@ -1400,7 +1437,8 @@ export abstract class AbstractTree implements IDisposable const onDidChangeCollapseStateRelay = new Relay>(); const onDidChangeActiveNodes = new Relay[]>(); const activeNodes = this.disposables.add(new EventCollection(onDidChangeActiveNodes.event)); - this.renderers = renderers.map(r => new TreeRenderer(r, () => this.model, onDidChangeCollapseStateRelay.event, activeNodes, _options)); + const renderedIndentGuides = new SetMap, HTMLDivElement>(); + this.renderers = renderers.map(r => new TreeRenderer(r, () => this.model, onDidChangeCollapseStateRelay.event, activeNodes, renderedIndentGuides, _options)); for (const r of this.renderers) { this.disposables.add(r); } diff --git a/src/vs/base/browser/ui/tree/media/tree.css b/src/vs/base/browser/ui/tree/media/tree.css index bee620d57ef..9256e1e8077 100644 --- a/src/vs/base/browser/ui/tree/media/tree.css +++ b/src/vs/base/browser/ui/tree/media/tree.css @@ -83,7 +83,7 @@ } .monaco-tree-type-filter.disabled { - top: -40px; + top: -40px !important; } .monaco-tree-type-filter-grab { diff --git a/src/vs/base/common/actions.ts b/src/vs/base/common/actions.ts index 13db4add519..769e8039d7f 100644 --- a/src/vs/base/common/actions.ts +++ b/src/vs/base/common/actions.ts @@ -219,7 +219,7 @@ export class Separator implements IAction { readonly label: string = ''; readonly tooltip: string = ''; - readonly class: string = ''; + readonly class: string = 'separator'; readonly enabled: boolean = false; readonly checked: boolean = false; async run() { } diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index f4f79545b3a..232375b7f10 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -1111,7 +1111,22 @@ export interface IdleDeadline { } /** - * Execute the callback the next time the browser is idle + * Execute the callback the next time the browser is idle, returning an + * {@link IDisposable} that will cancel the callback when disposed. This wraps + * [requestIdleCallback] so it will fallback to [setTimeout] if the environment + * doesn't support it. + * + * @param callback The callback to run when idle, this includes an + * [IdleDeadline] that provides the time alloted for the idle callback by the + * browser. Not respecting this deadline will result in a degraded user + * experience. + * @param timeout A timeout at which point to queue no longer wait for an idle + * callback but queue it on the regular event loop (like setTimeout). Typically + * this should not be used. + * + * [IdleDeadline]: https://developer.mozilla.org/en-US/docs/Web/API/IdleDeadline + * [requestIdleCallback]: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback + * [setTimeout]: https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout */ export let runWhenIdle: (callback: (idle: IdleDeadline) => void, timeout?: number) => IDisposable; diff --git a/src/vs/base/common/color.ts b/src/vs/base/common/color.ts index dcdc00dcee3..750cfe7dc71 100644 --- a/src/vs/base/common/color.ts +++ b/src/vs/base/common/color.ts @@ -259,6 +259,16 @@ export class Color { return Color.Format.CSS.parseHex(hex) || Color.red; } + static equals(a: Color | null, b: Color | null): boolean { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + return a.equals(b); + } + readonly rgba: RGBA; private _hsla?: HSLA; get hsla(): HSLA { diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index 44b25af9e3a..bea757b1ac6 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -123,15 +123,15 @@ export function transformErrorForSerialization(error: any): any { // see https://github.com/v8/v8/wiki/Stack%20Trace%20API#basic-stack-traces export interface V8CallSite { - getThis(): any; - getTypeName(): string; - getFunction(): string; - getFunctionName(): string; - getMethodName(): string; - getFileName(): string; - getLineNumber(): number; - getColumnNumber(): number; - getEvalOrigin(): string; + getThis(): unknown; + getTypeName(): string | null; + getFunction(): Function | undefined; + getFunctionName(): string | null; + getMethodName(): string | null; + getFileName(): string | null; + getLineNumber(): number | null; + getColumnNumber(): number | null; + getEvalOrigin(): string | undefined; isToplevel(): boolean; isEval(): boolean; isNative(): boolean; diff --git a/src/vs/base/common/iterator.ts b/src/vs/base/common/iterator.ts index 2771802f1de..7d6e1da0c0d 100644 --- a/src/vs/base/common/iterator.ts +++ b/src/vs/base/common/iterator.ts @@ -5,7 +5,7 @@ export namespace Iterable { - export function is(thing: any): thing is IterableIterator { + export function is(thing: any): thing is Iterable { return thing && typeof thing === 'object' && typeof thing[Symbol.iterator] === 'function'; } diff --git a/src/vs/base/common/lifecycle.ts b/src/vs/base/common/lifecycle.ts index da910eb4e77..d58b2fa2155 100644 --- a/src/vs/base/common/lifecycle.ts +++ b/src/vs/base/common/lifecycle.ts @@ -6,6 +6,8 @@ import { once } from 'vs/base/common/functional'; import { Iterable } from 'vs/base/common/iterator'; +// #region Disposable Tracking + /** * Enables logging of potentially leaked disposables. * @@ -108,20 +110,37 @@ export function markAsSingleton(singleton: T): T { return singleton; } +// #endregion + +/** + * An object that performs a cleanup operation when `.dispose()` is called. + * + * Some examples of how disposables are used: + * + * - An event listener that removes itself when `.dispose()` is called. + * - A resource such as a file system watcher that cleans up the resource when `.dispose()` is called. + * - The return value from registering a provider. When `.dispose()` is called, the provider is unregistered. + */ export interface IDisposable { dispose(): void; } +/** + * Check if `thing` is {@link IDisposable disposable}. + */ export function isDisposable(thing: E): thing is E & IDisposable { return typeof (thing).dispose === 'function' && (thing).dispose.length === 0; } +/** + * Disposes of the value(s) passed in. + */ export function dispose(disposable: T): T; export function dispose(disposable: T | undefined): T | undefined; -export function dispose = IterableIterator>(disposables: IterableIterator): A; +export function dispose = Iterable>(disposables: A): A; export function dispose(disposables: Array): Array; export function dispose(disposables: ReadonlyArray): ReadonlyArray; -export function dispose(arg: T | IterableIterator | undefined): any { +export function dispose(arg: T | Iterable | undefined): any { if (Iterable.is(arg)) { const errors: any[] = []; @@ -157,12 +176,18 @@ export function disposeIfDisposable(disposables: return []; } +/** + * Combine multiple disposable values into a single {@link IDisposable}. + */ export function combinedDisposable(...disposables: IDisposable[]): IDisposable { const parent = toDisposable(() => dispose(disposables)); setParentOfDisposables(disposables, parent); return parent; } +/** + * Turn a function that implements dispose into an {@link IDisposable}. + */ export function toDisposable(fn: () => void): IDisposable { const self = trackDisposable({ dispose: once(() => { @@ -173,11 +198,18 @@ export function toDisposable(fn: () => void): IDisposable { return self; } +/** + * Manages a collection of disposable values. + * + * This is the preferred way to manage multiple disposables. A `DisposableStore` is safer to work with than an + * `IDisposable[]` as it considers edge cases, such as registering the same value multiple times or adding an item to a + * store that has already been disposed of. + */ export class DisposableStore implements IDisposable { static DISABLE_DISPOSED_WARNING = false; - private _toDispose = new Set(); + private readonly _toDispose = new Set(); private _isDisposed = false; constructor() { @@ -200,7 +232,7 @@ export class DisposableStore implements IDisposable { } /** - * Returns `true` if this object has been disposed + * @return `true` if this object has been disposed of. */ public get isDisposed(): boolean { return this._isDisposed; @@ -210,13 +242,20 @@ export class DisposableStore implements IDisposable { * Dispose of all registered disposables but do not mark this object as disposed. */ public clear(): void { + if (this._toDispose.size === 0) { + return; + } + try { - dispose(this._toDispose.values()); + dispose(this._toDispose); } finally { this._toDispose.clear(); } } + /** + * Add a new {@link IDisposable disposable} to the collection. + */ public add(o: T): T { if (!o) { return o; @@ -238,8 +277,18 @@ export class DisposableStore implements IDisposable { } } +/** + * Abstract base class for a {@link IDisposable disposable} object. + * + * Subclasses can {@linkcode _register} disposables that will be automatically cleaned up when this object is disposed of. + */ export abstract class Disposable implements IDisposable { + /** + * A disposable that does nothing when it is disposed of. + * + * TODO: This should not be a static property. + */ static readonly None = Object.freeze({ dispose() { } }); protected readonly _store = new DisposableStore(); @@ -255,6 +304,9 @@ export abstract class Disposable implements IDisposable { this._store.dispose(); } + /** + * Adds `o` to the collection of disposables managed by this object. + */ protected _register(o: T): T { if ((o as unknown as Disposable) === this) { throw new Error('Cannot register a disposable on itself!'); @@ -293,7 +345,10 @@ export class MutableDisposable implements IDisposable { this._value = value; } - clear() { + /** + * Resets the stored value and disposed of the previously stored value. + */ + clear(): void { this.value = undefined; } @@ -439,3 +494,74 @@ export function disposeOnReturn(fn: (store: DisposableStore) => void): void { store.dispose(); } } + +/** + * A map the manages the lifecycle of the values that it stores. + */ +export class DisposableMap implements IDisposable { + + private readonly _store = new Map(); + private _isDisposed = false; + + constructor() { + trackDisposable(this); + } + + /** + * Disposes of all stored values and mark this object as disposed. + * + * Trying to use this object after it has been disposed of is an error. + */ + dispose(): void { + markAsDisposed(this); + this._isDisposed = true; + this.clearAndDisposeAll(); + } + + /** + * Disposes of all stored values and clear the map, but DO NOT mark this object as disposed. + */ + clearAndDisposeAll(): void { + if (!this._store.size) { + return; + } + + try { + dispose(this._store.values()); + } finally { + this._store.clear(); + } + } + + has(key: K): boolean { + return this._store.has(key); + } + + get(key: K): V | undefined { + return this._store.get(key); + } + + set(key: K, value: V, skipDisposeOnOverwrite = false): void { + if (this._isDisposed) { + console.warn(new Error('Trying to add a disposable to a DisposableMap that has already been disposed of. The added object will be leaked!').stack); + } + + if (!skipDisposeOnOverwrite) { + this._store.get(key)?.dispose(); + } + + this._store.set(key, value); + } + + /** + * Delete the value stored for `key` from this map and also dispose of it. + */ + deleteAndDispose(key: K): void { + this._store.get(key)?.dispose(); + this._store.delete(key); + } + + [Symbol.iterator](): IterableIterator<[K, V]> { + return this._store[Symbol.iterator](); + } +} diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index 2a78f7afb18..1c2e42bba59 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -576,12 +576,16 @@ export class TernarySearchTree { if (!node.mid && !node.value) { if (node.left && node.right) { // full node + // replace deleted-node with the min-node of the right branch. + // If there is no true min-node leave things as they are const min = this._min(node.right); - const { key, value, segment } = min; - this._delete(min.key!, false); - node.key = key; - node.value = value; - node.segment = segment; + if (min.key) { + const { key, value, segment } = min; + this._delete(min.key!, false); + node.key = key; + node.value = value; + node.segment = segment; + } } else { // empty or half empty diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index 243bb3b82d3..648a6391131 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -150,7 +150,7 @@ class RemoteAuthoritiesImpl { } const authority = uri.authority; let host = this._hosts[authority]; - if (host && host.indexOf(':') !== -1) { + if (host && host.indexOf(':') !== -1 && host.indexOf('[') === -1) { host = `[${host}]`; } const port = this._ports[authority]; diff --git a/src/vs/base/common/platform.ts b/src/vs/base/common/platform.ts index 506ee713281..f41a2f77df5 100644 --- a/src/vs/base/common/platform.ts +++ b/src/vs/base/common/platform.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -const LANGUAGE_DEFAULT = 'en'; +export const LANGUAGE_DEFAULT = 'en'; let _isWindows = false; let _isMacintosh = false; diff --git a/src/vs/base/common/product.ts b/src/vs/base/common/product.ts index e07695c9363..c7c8587b9da 100644 --- a/src/vs/base/common/product.ts +++ b/src/vs/base/common/product.ts @@ -130,6 +130,9 @@ export interface IProductConfiguration { readonly serverApplicationName: string; readonly serverDataFolderName?: string; + readonly tunnelApplicationName?: string; + readonly tunnelApplicationConfig?: { authenticationProviders: IStringDictionary<{ scopes: string[] }> }; + readonly npsSurveyUrl?: string; readonly cesSurveyUrl?: string; readonly surveys?: readonly ISurveyData[]; @@ -155,7 +158,6 @@ export interface IProductConfiguration { readonly 'configurationSync.store'?: ConfigurationSyncStore; readonly 'editSessions.store'?: Omit; - readonly darwinUniversalAssetId?: string; // experimental diff --git a/src/vs/base/parts/ipc/node/ipc.net.ts b/src/vs/base/parts/ipc/node/ipc.net.ts index bff8e41d8e5..810ba41d106 100644 --- a/src/vs/base/parts/ipc/node/ipc.net.ts +++ b/src/vs/base/parts/ipc/node/ipc.net.ts @@ -32,6 +32,9 @@ export class NodeSocket implements ISocket { public readonly debugLabel: string; public readonly socket: Socket; private readonly _errorListener: (err: any) => void; + private readonly _closeListener: (hadError: boolean) => void; + private readonly _endListener: () => void; + private _canWrite = true; public traceSocketEvent(type: SocketDiagnosticsEventType, data?: VSBuffer | Uint8Array | ArrayBuffer | ArrayBufferView | any): void { SocketDiagnostics.traceSocketEvent(this.socket, this.debugLabel, type, data); @@ -57,10 +60,24 @@ export class NodeSocket implements ISocket { } }; this.socket.on('error', this._errorListener); + + this._closeListener = (hadError: boolean) => { + this.traceSocketEvent(SocketDiagnosticsEventType.Close, { hadError }); + this._canWrite = false; + }; + this.socket.on('close', this._closeListener); + + this._endListener = () => { + this.traceSocketEvent(SocketDiagnosticsEventType.NodeEndReceived); + this._canWrite = false; + }; + this.socket.on('end', this._endListener); } public dispose(): void { this.socket.off('error', this._errorListener); + this.socket.off('close', this._closeListener); + this.socket.off('end', this._endListener); this.socket.destroy(); } @@ -77,7 +94,6 @@ export class NodeSocket implements ISocket { public onClose(listener: (e: SocketCloseEvent) => void): IDisposable { const adapter = (hadError: boolean) => { - this.traceSocketEvent(SocketDiagnosticsEventType.Close, { hadError }); listener({ type: SocketCloseEventType.NodeSocketCloseEvent, hadError: hadError, @@ -92,7 +108,6 @@ export class NodeSocket implements ISocket { public onEnd(listener: () => void): IDisposable { const adapter = () => { - this.traceSocketEvent(SocketDiagnosticsEventType.NodeEndReceived); listener(); }; this.socket.on('end', adapter); @@ -103,7 +118,7 @@ export class NodeSocket implements ISocket { public write(buffer: VSBuffer): void { // return early if socket has been destroyed in the meantime - if (this.socket.destroyed) { + if (this.socket.destroyed || !this._canWrite) { return; } diff --git a/src/vs/base/parts/quickinput/browser/media/quickInput.css b/src/vs/base/parts/quickinput/browser/media/quickInput.css index e3357d75fe8..46834baf60a 100644 --- a/src/vs/base/parts/quickinput/browser/media/quickInput.css +++ b/src/vs/base/parts/quickinput/browser/media/quickInput.css @@ -10,6 +10,7 @@ left: 50%; margin-left: -300px; -webkit-app-region: no-drag; + border-radius: 4px; } .quick-input-titlebar { @@ -56,7 +57,7 @@ .quick-input-header { display: flex; - padding: 6px 6px 0px 6px; + padding: 8px 6px 0px 6px; margin-bottom: -2px; } @@ -150,11 +151,12 @@ .quick-input-list { line-height: 22px; margin-top: 6px; - padding: 0px 1px 1px 1px; + padding: 1px; } .quick-input-widget.hidden-input .quick-input-list { - margin-top: 0; /* reduce margins when input box hidden */ + margin-top: 4px; /* reduce margins when input box hidden */ + padding-bottom: 4px; } .quick-input-list .monaco-list { @@ -162,6 +164,10 @@ max-height: calc(20 * 22px); } +.quick-input-list .monaco-scrollable-element { + padding: 0px 5px; +} + .quick-input-list .quick-input-list-entry { box-sizing: border-box; overflow: hidden; @@ -175,6 +181,10 @@ border-top-style: solid; } +.quick-input-list .monaco-list-row { + border-radius: 3px; +} + .quick-input-list .monaco-list-row[data-index="0"] .quick-input-list-entry.quick-input-list-separator-border { border-top-style: none; } @@ -246,7 +256,7 @@ } .quick-input-list .quick-input-list-entry .quick-input-list-separator { - margin-right: 8px; /* separate from keybindings or actions */ + margin-right: 4px; /* separate from keybindings or actions */ } .quick-input-list .quick-input-list-entry-action-bar { diff --git a/src/vs/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts index 41e7da28efa..8d2d051b9f4 100644 --- a/src/vs/base/parts/quickinput/browser/quickInput.ts +++ b/src/vs/base/parts/quickinput/browser/quickInput.ts @@ -29,7 +29,7 @@ import { isIOS } from 'vs/base/common/platform'; import Severity from 'vs/base/common/severity'; import { isString, withNullAsUndefined } from 'vs/base/common/types'; import { getIconClass } from 'vs/base/parts/quickinput/browser/quickInputUtils'; -import { IInputBox, IInputOptions, IKeyMods, IPickOptions, IQuickInput, IQuickInputButton, IQuickInputHideEvent, IQuickInputToggle, IQuickNavigateConfiguration, IQuickPick, IQuickPickDidAcceptEvent, IQuickPickItem, IQuickPickItemButtonEvent, IQuickPickSeparator, IQuickPickWillAcceptEvent, ItemActivation, NO_KEY_MODS, QuickInputHideReason, QuickPickInput } from 'vs/base/parts/quickinput/common/quickInput'; +import { IInputBox, IInputOptions, IKeyMods, IPickOptions, IQuickInput, IQuickInputButton, IQuickInputHideEvent, IQuickInputToggle, IQuickNavigateConfiguration, IQuickPick, IQuickPickDidAcceptEvent, IQuickPickItem, IQuickPickItemButtonEvent, IQuickPickSeparator, IQuickPickSeparatorButtonEvent, IQuickPickWillAcceptEvent, ItemActivation, NO_KEY_MODS, QuickInputHideReason, QuickPickInput } from 'vs/base/parts/quickinput/common/quickInput'; import 'vs/css!./media/quickInput'; import { localize } from 'vs/nls'; import { QuickInputBox } from './quickInputBox'; @@ -465,6 +465,7 @@ class QuickPick extends QuickInput implements IQuickPi private selectedItemsToConfirm: T[] | null = []; private readonly onDidChangeSelectionEmitter = this._register(new Emitter()); private readonly onDidTriggerItemButtonEmitter = this._register(new Emitter>()); + private readonly onDidTriggerSeparatorButtonEmitter = this._register(new Emitter()); private _valueSelection: Readonly<[number, number]> | undefined; private valueSelectionUpdated = true; private _ok: boolean | 'default' = 'default'; @@ -753,6 +754,8 @@ class QuickPick extends QuickInput implements IQuickPi onDidTriggerItemButton = this.onDidTriggerItemButtonEmitter.event; + onDidTriggerSeparatorButton = this.onDidTriggerSeparatorButtonEmitter.event; + private trySelectFirst() { if (this.autoFocusOnList) { if (!this.canSelectMany) { @@ -892,6 +895,7 @@ class QuickPick extends QuickInput implements IQuickPi this.onDidChangeSelectionEmitter.fire(checkedItems as T[]); })); this.visibleDisposables.add(this.ui.list.onButtonTriggered(event => this.onDidTriggerItemButtonEmitter.fire(event as IQuickPickItemButtonEvent))); + this.visibleDisposables.add(this.ui.list.onSeparatorButtonTriggered(event => this.onDidTriggerSeparatorButtonEmitter.fire(event))); this.visibleDisposables.add(this.registerQuickNavigation()); this.valueSelectionUpdated = true; } @@ -1166,11 +1170,14 @@ class InputBox extends QuickInput implements IInputBox { if (!this.visible) { return; } + + this.ui.container.classList.remove('hidden-input'); const visibilities: Visibilities = { title: !!this.title || !!this.step || !!this.buttons.length, description: !!this.description || !!this.step, inputBox: true, message: true }; + this.ui.setVisibilities(visibilities); super.update(); if (this.ui.inputBox.value !== this.value) { @@ -1186,7 +1193,6 @@ class InputBox extends QuickInput implements IInputBox { if (this.ui.inputBox.password !== this.password) { this.ui.inputBox.password = this.password; } - } } @@ -1482,6 +1488,7 @@ export class QuickInputController extends Disposable { } } })), + input.onDidTriggerSeparatorButton(event => options.onDidTriggerSeparatorButton?.(event)), input.onDidChangeValue(value => { if (activeItem && !value && (input.activeItems.length !== 1 || input.activeItems[0] !== activeItem)) { input.activeItems = [activeItem]; @@ -1667,7 +1674,7 @@ export class QuickInputController extends Disposable { ui.message.style.display = visibilities.message ? '' : 'none'; ui.progressBar.getContainer().style.display = visibilities.progressBar ? '' : 'none'; ui.list.display(!!visibilities.list); - ui.container.classList[visibilities.checkBox ? 'add' : 'remove']('show-checkboxes'); + ui.container.classList.toggle('show-checkboxes', visibilities.checkBox); this.updateLayout(); // TODO } @@ -1826,6 +1833,9 @@ export class QuickInputController extends Disposable { if (this.styles.list.pickerGroupForeground) { content.push(`.quick-input-list .quick-input-list-separator { color: ${this.styles.list.pickerGroupForeground}; }`); } + if (this.styles.list.pickerGroupForeground) { + content.push(`.quick-input-list .quick-input-list-separator-as-item { color: ${this.styles.list.pickerGroupForeground}; }`); + } if ( this.styles.keybindingLabel.keybindingLabelBackground || diff --git a/src/vs/base/parts/quickinput/browser/quickInputList.ts b/src/vs/base/parts/quickinput/browser/quickInputList.ts index 25058c9de25..515bae093a6 100644 --- a/src/vs/base/parts/quickinput/browser/quickInputList.ts +++ b/src/vs/base/parts/quickinput/browser/quickInputList.ts @@ -25,7 +25,7 @@ import { ltrim } from 'vs/base/common/strings'; import { withNullAsUndefined } from 'vs/base/common/types'; import { IQuickInputOptions } from 'vs/base/parts/quickinput/browser/quickInput'; import { getIconClass } from 'vs/base/parts/quickinput/browser/quickInputUtils'; -import { QuickPickItem, IQuickPickItem, IQuickPickItemButtonEvent, IQuickPickSeparator } from 'vs/base/parts/quickinput/common/quickInput'; +import { QuickPickItem, IQuickPickItem, IQuickPickItemButtonEvent, IQuickPickSeparator, IQuickPickSeparatorButtonEvent } from 'vs/base/parts/quickinput/common/quickInput'; import 'vs/css!./media/quickInput'; import { localize } from 'vs/nls'; @@ -34,7 +34,7 @@ const $ = dom.$; interface IListElement { readonly hasCheckbox: boolean; readonly index: number; - readonly item: IQuickPickItem; + readonly item?: IQuickPickItem; readonly saneLabel: string; readonly saneSortLabel: string; readonly saneMeta?: string; @@ -47,12 +47,13 @@ interface IListElement { readonly checked: boolean; readonly separator?: IQuickPickSeparator; readonly fireButtonTriggered: (event: IQuickPickItemButtonEvent) => void; + readonly fireSeparatorButtonTriggered: (event: IQuickPickSeparatorButtonEvent) => void; } class ListElement implements IListElement, IDisposable { hasCheckbox!: boolean; index!: number; - item!: IQuickPickItem; + item?: IQuickPickItem; saneLabel!: string; saneSortLabel!: string; saneMeta!: string; @@ -77,6 +78,7 @@ class ListElement implements IListElement, IDisposable { descriptionHighlights?: IMatch[]; detailHighlights?: IMatch[]; fireButtonTriggered!: (event: IQuickPickItemButtonEvent) => void; + fireSeparatorButtonTriggered!: (event: IQuickPickSeparatorButtonEvent) => void; constructor(init: IListElement) { Object.assign(this, init); @@ -156,8 +158,9 @@ class ListElementRenderer implements IListRenderer data.checkbox.checked = checked)); @@ -168,13 +171,18 @@ class ListElementRenderer implements IListRenderer { let cssClasses = button.iconClass || (button.iconPath ? getIconClass(button.iconPath) : undefined); @@ -203,10 +213,15 @@ class ListElementRenderer implements IListRenderer { - element.fireButtonTriggered({ - button, - item: element.item - }); + mainItem.type !== 'separator' + ? element.fireButtonTriggered({ + button, + item: mainItem + }) + : element.fireSeparatorButtonTriggered({ + button, + separator: mainItem + }); }); action.tooltip = button.tooltip || ''; return action; @@ -219,6 +234,7 @@ class ListElementRenderer implements IListRenderer { getHeight(element: ListElement): number { + if (!element.item) { + // must be a separator + return 24; + } return element.saneDetail ? 44 : 22; } @@ -255,7 +275,7 @@ export class QuickInputList { private list: List; private inputElements: Array = []; private elements: ListElement[] = []; - private elementsToIndexes = new Map(); + private elementsToIndexes = new Map(); matchOnDescription = false; matchOnDetail = false; matchOnLabel = true; @@ -272,6 +292,8 @@ export class QuickInputList { onChangedCheckedElements: Event = this._onChangedCheckedElements.event; private readonly _onButtonTriggered = new Emitter>(); onButtonTriggered = this._onButtonTriggered.event; + private readonly _onSeparatorButtonTriggered = new Emitter(); + onSeparatorButtonTriggered = this._onSeparatorButtonTriggered.event; private readonly _onKeyDown = new Emitter(); onKeyDown: Event = this._onKeyDown.event; private readonly _onLeave = new Emitter(); @@ -359,6 +381,7 @@ export class QuickInputList { this._onChangedVisibleCount, this._onChangedCheckedElements, this._onButtonTriggered, + this._onSeparatorButtonTriggered, this._onLeave, this._onKeyDown ); @@ -439,48 +462,67 @@ export class QuickInputList { setElements(inputElements: Array): void { this.elementDisposables = dispose(this.elementDisposables); const fireButtonTriggered = (event: IQuickPickItemButtonEvent) => this.fireButtonTriggered(event); + const fireSeparatorButtonTriggered = (event: IQuickPickSeparatorButtonEvent) => this.fireSeparatorButtonTriggered(event); this.inputElements = inputElements; this.elements = inputElements.reduce((result, item, index) => { - if (item.type !== 'separator') { - const previous = index && inputElements[index - 1]; - const saneLabel = item.label && item.label.replace(/\r?\n/g, ' '); - const saneSortLabel = parseLabelWithIcons(saneLabel).text.trim(); - const saneMeta = item.meta && item.meta.replace(/\r?\n/g, ' '); - const saneDescription = item.description && item.description.replace(/\r?\n/g, ' '); - const saneDetail = item.detail && item.detail.replace(/\r?\n/g, ' '); - const saneAriaLabel = item.ariaLabel || [saneLabel, saneDescription, saneDetail] - .map(s => getCodiconAriaLabel(s)) - .filter(s => !!s) - .join(', '); + const previous = index && inputElements[index - 1]; + const saneLabel = item.label ? item.label.replace(/\r?\n/g, ' ') : ''; + const saneSortLabel = parseLabelWithIcons(saneLabel).text.trim(); - const hasCheckbox = this.parent.classList.contains('show-checkboxes'); - result.push(new ListElement({ - hasCheckbox, - index, - item, - saneLabel, - saneSortLabel, - saneMeta, - saneAriaLabel, - saneDescription, - saneDetail, - labelHighlights: item.highlights?.label, - descriptionHighlights: item.highlights?.description, - detailHighlights: item.highlights?.detail, - checked: false, - separator: previous && previous.type === 'separator' ? previous : undefined, - fireButtonTriggered - })); + let saneMeta, saneDescription, saneDetail, labelHighlights, descriptionHighlights, detailHighlights; + if (item.type !== 'separator') { + saneMeta = item.meta && item.meta.replace(/\r?\n/g, ' '); + saneDescription = item.description && item.description.replace(/\r?\n/g, ' '); + saneDetail = item.detail && item.detail.replace(/\r?\n/g, ' '); + labelHighlights = item.highlights?.label; + descriptionHighlights = item.highlights?.description; + detailHighlights = item.highlights?.detail; } + const saneAriaLabel = item.ariaLabel || [saneLabel, saneDescription, saneDetail] + .map(s => getCodiconAriaLabel(s)) + .filter(s => !!s) + .join(', '); + + const hasCheckbox = this.parent.classList.contains('show-checkboxes'); + + let separator: IQuickPickSeparator | undefined; + if (item.type === 'separator') { + if (!item.buttons) { + // This separator will be rendered as a part of the list item + return result; + } + separator = item; + } else if (previous && previous.type === 'separator' && !previous.buttons) { + separator = previous; + } + + result.push(new ListElement({ + hasCheckbox, + index, + item: item.type !== 'separator' ? item : undefined, + saneLabel, + saneSortLabel, + saneMeta, + saneAriaLabel, + saneDescription, + saneDetail, + labelHighlights, + descriptionHighlights, + detailHighlights, + checked: false, + separator, + fireButtonTriggered, + fireSeparatorButtonTriggered + })); return result; }, [] as ListElement[]); this.elementDisposables.push(...this.elements); this.elementDisposables.push(...this.elements.map(element => element.onChecked(() => this.fireCheckedEvents()))); this.elementsToIndexes = this.elements.reduce((map, element, index) => { - map.set(element.item, index); + map.set(element.item ?? element.separator!, index); return map; - }, new Map()); + }, new Map()); this.list.splice(0, this.list.length); // Clear focus and selection first, sending the events when the list is empty. this.list.splice(0, this.list.length, this.elements); this._onChangedVisibleCount.fire(this.elements.length); @@ -524,7 +566,8 @@ export class QuickInputList { getCheckedElements() { return this.elements.filter(e => e.checked) - .map(e => e.item); + .map(e => e.item) + .filter(e => !!e) as IQuickPickItem[]; } setCheckedElements(items: IQuickPickItem[]) { @@ -552,39 +595,44 @@ export class QuickInputList { return; } - if (what === QuickInputListFocus.Next && this.list.getFocus()[0] === this.list.length - 1) { - what = QuickInputListFocus.First; - } - - if (what === QuickInputListFocus.Previous && this.list.getFocus()[0] === 0) { - what = QuickInputListFocus.Last; - } - if (what === QuickInputListFocus.Second && this.list.length < 2) { what = QuickInputListFocus.First; } switch (what) { case QuickInputListFocus.First: - this.list.focusFirst(); + this.list.scrollTop = 0; + this.list.focusFirst(undefined, (e) => !!e.item); break; case QuickInputListFocus.Second: - this.list.focusNth(1); + this.list.scrollTop = 0; + this.list.focusNth(1, undefined, (e) => !!e.item); break; case QuickInputListFocus.Last: - this.list.focusLast(); + this.list.scrollTop = this.list.scrollHeight; + this.list.focusLast(undefined, (e) => !!e.item); break; - case QuickInputListFocus.Next: - this.list.focusNext(); + case QuickInputListFocus.Next: { + this.list.focusNext(undefined, true, undefined, (e) => !!e.item); + const index = this.list.getFocus()[0]; + if (index !== 0 && !this.elements[index - 1].item && this.list.firstVisibleIndex > index - 1) { + this.list.reveal(index - 1); + } break; - case QuickInputListFocus.Previous: - this.list.focusPrevious(); + } + case QuickInputListFocus.Previous: { + this.list.focusPrevious(undefined, true, undefined, (e) => !!e.item); + const index = this.list.getFocus()[0]; + if (index !== 0 && !this.elements[index - 1].item && this.list.firstVisibleIndex > index - 1) { + this.list.reveal(index - 1); + } break; + } case QuickInputListFocus.NextPage: - this.list.focusNextPage(); + this.list.focusNextPage(undefined, (e) => !!e.item); break; case QuickInputListFocus.PreviousPage: - this.list.focusPreviousPage(); + this.list.focusPreviousPage(undefined, (e) => !!e.item); break; } @@ -624,7 +672,9 @@ export class QuickInputList { element.detailHighlights = undefined; element.hidden = false; const previous = element.index && this.inputElements[element.index - 1]; - element.separator = previous && previous.type === 'separator' ? previous : undefined; + if (element.item) { + element.separator = previous && previous.type === 'separator' && !previous.buttons ? previous : undefined; + } }); } @@ -651,9 +701,8 @@ export class QuickInputList { element.labelHighlights = undefined; element.descriptionHighlights = undefined; element.detailHighlights = undefined; - element.hidden = !element.item.alwaysShow; + element.hidden = element.item ? !element.item.alwaysShow : true; } - element.separator = undefined; // we can show the separator unless the list gets sorted by match if (!this.sortByLabel) { @@ -678,9 +727,9 @@ export class QuickInputList { } this.elementsToIndexes = shownElements.reduce((map, element, index) => { - map.set(element.item, index); + map.set(element.item ?? element.separator!, index); return map; - }, new Map()); + }, new Map()); this.list.splice(0, this.list.length, shownElements); this.list.setFocus([]); this.list.layout(); @@ -730,6 +779,10 @@ export class QuickInputList { this._onButtonTriggered.fire(event); } + private fireSeparatorButtonTriggered(event: IQuickPickSeparatorButtonEvent) { + this._onSeparatorButtonTriggered.fire(event); + } + style(styles: IListStyles) { this.list.style(styles); } diff --git a/src/vs/base/parts/quickinput/common/quickInput.ts b/src/vs/base/parts/quickinput/common/quickInput.ts index f86a4c00817..ef88ef4eb4c 100644 --- a/src/vs/base/parts/quickinput/common/quickInput.ts +++ b/src/vs/base/parts/quickinput/common/quickInput.ts @@ -45,7 +45,10 @@ export interface IQuickPickItem { export interface IQuickPickSeparator { type: 'separator'; + id?: string; label?: string; + ariaLabel?: string; + buttons?: IQuickInputButton[]; } export interface IKeyMods { @@ -126,6 +129,7 @@ export interface IPickOptions { onKeyMods?: (keyMods: IKeyMods) => void; onDidFocus?: (entry: T) => void; onDidTriggerItemButton?: (context: IQuickPickItemButtonContext) => void; + onDidTriggerSeparatorButton?: (context: IQuickPickSeparatorButtonEvent) => void; } export interface IInputOptions { @@ -284,6 +288,8 @@ export interface IQuickPick extends IQuickInput { readonly onDidTriggerItemButton: Event>; + readonly onDidTriggerSeparatorButton: Event; + items: ReadonlyArray; canSelectMany: boolean; @@ -428,6 +434,11 @@ export interface IQuickPickItemButtonEvent { item: T; } +export interface IQuickPickSeparatorButtonEvent { + button: IQuickInputButton; + separator: IQuickPickSeparator; +} + export interface IQuickPickItemButtonContext extends IQuickPickItemButtonEvent { removeItem(): void; } diff --git a/src/vs/base/parts/sandbox/common/sandboxTypes.ts b/src/vs/base/parts/sandbox/common/sandboxTypes.ts index c6c087da214..8c296184f7d 100644 --- a/src/vs/base/parts/sandbox/common/sandboxTypes.ts +++ b/src/vs/base/parts/sandbox/common/sandboxTypes.ts @@ -26,7 +26,11 @@ export interface ISandboxConfiguration { windowId: number; /** - * Absolute installation path. + * Root path of the JavaScript sources. + * + * Note: This is NOT the installation root + * directory itself but contained in it at + * a level that is platform dependent. */ appRoot: string; diff --git a/src/vs/base/test/common/event.test.ts b/src/vs/base/test/common/event.test.ts index 79816c693e2..d87a96adda5 100644 --- a/src/vs/base/test/common/event.test.ts +++ b/src/vs/base/test/common/event.test.ts @@ -9,6 +9,7 @@ import { errorHandler, setUnexpectedErrorHandler } from 'vs/base/common/errors'; import { AsyncEmitter, DebounceEmitter, Emitter, Event, EventBufferer, EventMultiplexer, IWaitUntil, MicrotaskEmitter, PauseableEmitter, Relay } from 'vs/base/common/event'; import { DisposableStore, IDisposable, isDisposable, setDisposableTracker, toDisposable } from 'vs/base/common/lifecycle'; import { observableValue, transaction } from 'vs/base/common/observable'; +import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; import { DisposableTracker, ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; namespace Samples { @@ -317,26 +318,29 @@ suite('Event', function () { }); test('DebounceEmitter', async function () { - let callCount = 0; - let sum = 0; - const emitter = new DebounceEmitter({ - merge: arr => { - callCount += 1; - return arr.reduce((p, c) => p + c); - } + return runWithFakedTimers({}, async function () { + + let callCount = 0; + let sum = 0; + const emitter = new DebounceEmitter({ + merge: arr => { + callCount += 1; + return arr.reduce((p, c) => p + c); + } + }); + + emitter.event(e => { sum = e; }); + + const p = Event.toPromise(emitter.event); + + emitter.fire(1); + emitter.fire(2); + + await p; + + assert.strictEqual(callCount, 1); + assert.strictEqual(sum, 3); }); - - emitter.event(e => { sum = e; }); - - const p = Event.toPromise(emitter.event); - - emitter.fire(1); - emitter.fire(2); - - await p; - - assert.strictEqual(callCount, 1); - assert.strictEqual(sum, 3); }); test('Microtask Emitter', (done) => { @@ -410,59 +414,64 @@ suite('AsyncEmitter', function () { }); test('sequential delivery', async function () { + return runWithFakedTimers({}, async function () { - interface E extends IWaitUntil { - foo: boolean; - } + interface E extends IWaitUntil { + foo: boolean; + } - let globalState = 0; - const emitter = new AsyncEmitter(); + let globalState = 0; + const emitter = new AsyncEmitter(); - emitter.event(e => { - e.waitUntil(timeout(10).then(_ => { - assert.strictEqual(globalState, 0); - globalState += 1; - })); + emitter.event(e => { + e.waitUntil(timeout(10).then(_ => { + assert.strictEqual(globalState, 0); + globalState += 1; + })); + }); + + emitter.event(e => { + e.waitUntil(timeout(1).then(_ => { + assert.strictEqual(globalState, 1); + globalState += 1; + })); + }); + + await emitter.fireAsync({ foo: true }, CancellationToken.None); + assert.strictEqual(globalState, 2); }); - - emitter.event(e => { - e.waitUntil(timeout(1).then(_ => { - assert.strictEqual(globalState, 1); - globalState += 1; - })); - }); - - await emitter.fireAsync({ foo: true }, CancellationToken.None); - assert.strictEqual(globalState, 2); }); test('sequential, in-order delivery', async function () { - interface E extends IWaitUntil { - foo: number; - } - const events: number[] = []; - let done = false; - const emitter = new AsyncEmitter(); + return runWithFakedTimers({}, async function () { - // e1 - emitter.event(e => { - e.waitUntil(timeout(10).then(async _ => { - if (e.foo === 1) { - await emitter.fireAsync({ foo: 2 }, CancellationToken.None); - assert.deepStrictEqual(events, [1, 2]); - done = true; - } - })); + interface E extends IWaitUntil { + foo: number; + } + const events: number[] = []; + let done = false; + const emitter = new AsyncEmitter(); + + // e1 + emitter.event(e => { + e.waitUntil(timeout(10).then(async _ => { + if (e.foo === 1) { + await emitter.fireAsync({ foo: 2 }, CancellationToken.None); + assert.deepStrictEqual(events, [1, 2]); + done = true; + } + })); + }); + + // e2 + emitter.event(e => { + events.push(e.foo); + e.waitUntil(timeout(7)); + }); + + await emitter.fireAsync({ foo: 1 }, CancellationToken.None); + assert.ok(done); }); - - // e2 - emitter.event(e => { - events.push(e.foo); - e.waitUntil(timeout(7)); - }); - - await emitter.fireAsync({ foo: 1 }, CancellationToken.None); - assert.ok(done); }); test('catch errors', async function () { diff --git a/src/vs/base/test/common/map.test.ts b/src/vs/base/test/common/map.test.ts index 896d06ec812..0d3f3dfe131 100644 --- a/src/vs/base/test/common/map.test.ts +++ b/src/vs/base/test/common/map.test.ts @@ -862,6 +862,49 @@ suite('Map', () => { } }); + test('TernarySearchTree: Cannot read properties of undefined (reading \'length\'): #161618 (simple)', function () { + const raw = 'config.debug.toolBarLocation,floating,config.editor.renderControlCharacters,true,config.editor.renderWhitespace,selection,config.files.autoSave,off,config.git.enabled,true,config.notebook.globalToolbar,true,config.terminal.integrated.tabs.enabled,true,config.terminal.integrated.tabs.showActions,singleTerminalOrNarrow,config.terminal.integrated.tabs.showActiveTerminal,singleTerminalOrNarrow,config.workbench.activityBar.visible,true,config.workbench.experimental.settingsProfiles.enabled,true,config.workbench.layoutControl.type,both,config.workbench.sideBar.location,left,config.workbench.statusBar.visible,true'; + const array = raw.split(','); + const tuples: [string, string][] = []; + for (let i = 0; i < array.length; i += 2) { + tuples.push([array[i], array[i + 1]]); + } + + const map = TernarySearchTree.forConfigKeys(); + map.fill(tuples); + + assert.strictEqual([...map].join(), raw); + assert.ok(map.has('config.editor.renderWhitespace')); + + const len = [...map].length; + map.delete('config.editor.renderWhitespace'); + assert.ok(map._isBalanced()); + assert.strictEqual([...map].length, len - 1); + }); + + test('TernarySearchTree: Cannot read properties of undefined (reading \'length\'): #161618 (random)', function () { + const raw = 'config.debug.toolBarLocation,floating,config.editor.renderControlCharacters,true,config.editor.renderWhitespace,selection,config.files.autoSave,off,config.git.enabled,true,config.notebook.globalToolbar,true,config.terminal.integrated.tabs.enabled,true,config.terminal.integrated.tabs.showActions,singleTerminalOrNarrow,config.terminal.integrated.tabs.showActiveTerminal,singleTerminalOrNarrow,config.workbench.activityBar.visible,true,config.workbench.experimental.settingsProfiles.enabled,true,config.workbench.layoutControl.type,both,config.workbench.sideBar.location,left,config.workbench.statusBar.visible,true'; + const array = raw.split(','); + const tuples: [string, string][] = []; + for (let i = 0; i < array.length; i += 2) { + tuples.push([array[i], array[i + 1]]); + } + + for (let round = 100; round >= 0; round--) { + shuffle(tuples); + const map = TernarySearchTree.forConfigKeys(); + map.fill(tuples); + + assert.strictEqual([...map].join(), raw); + assert.ok(map.has('config.editor.renderWhitespace')); + + const len = [...map].length; + map.delete('config.editor.renderWhitespace'); + assert.ok(map._isBalanced()); + assert.strictEqual([...map].length, len - 1); + } + }); + test('TernarySearchTree (PathSegments) - lookup', function () { const map = new TernarySearchTree(new PathIterator()); diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/extensionsCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/extensionsCleaner.ts index a49cc156f35..7da094c5965 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/extensionsCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/extensionsCleaner.ts @@ -96,7 +96,7 @@ class ProfileExtensionsCleaner extends Disposable { private async uninstallExtensionsNotInProfiles(): Promise { const installed = await this.extensionManagementService.getAllUserInstalled(); - const toUninstall = installed.filter(installedExtension => !this.profileExtensionsLocations.has(this.getKey(installedExtension.identifier, installedExtension.manifest.version))); + const toUninstall = installed.filter(installedExtension => installedExtension.installedTimestamp /* Installed by VS Code */ && !this.profileExtensionsLocations.has(this.getKey(installedExtension.identifier, installedExtension.manifest.version))); if (toUninstall.length) { await Promise.all(toUninstall.map(extension => this.extensionManagementService.uninstall(extension, uninstalOptions))); } @@ -169,7 +169,7 @@ class ProfileExtensionsCleaner extends Disposable { private async uninstallExtensions(extensionsToRemove: { identifier: IExtensionIdentifier; version: string }[]): Promise { const installed = await this.extensionManagementService.getAllUserInstalled(); - const toUninstall = installed.filter(installedExtension => extensionsToRemove.some(e => this.getKey(installedExtension.identifier, installedExtension.manifest.version) === this.getKey(e.identifier, e.version))); + const toUninstall = installed.filter(installedExtension => installedExtension.installedTimestamp /* Installed by VS Code */ && extensionsToRemove.some(e => this.getKey(installedExtension.identifier, installedExtension.manifest.version) === this.getKey(e.identifier, e.version))); if (toUninstall.length) { await Promise.all(toUninstall.map(extension => this.extensionManagementService.uninstall(extension, uninstalOptions))); } diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 5d99003793a..091c36d4f28 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -49,7 +49,7 @@ import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services'; import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks'; import { NativeLanguagePackService } from 'vs/platform/languagePacks/node/languagePacks'; import { ConsoleLogger, ILoggerService, ILogService, MultiplexLogService } from 'vs/platform/log/common/log'; -import { FollowerLogService, LoggerChannelClient, LogLevelChannelClient } from 'vs/platform/log/common/logIpc'; +import { FollowerLogService, LoggerChannelClient, LogLevelChannel, LogLevelChannelClient } from 'vs/platform/log/common/logIpc'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import product from 'vs/platform/product/common/product'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -108,6 +108,9 @@ import { UserDataProfilesNativeService } from 'vs/platform/userDataProfile/elect import { SharedProcessRequestService } from 'vs/platform/request/electron-browser/sharedProcessRequestService'; import { OneDataSystemAppender } from 'vs/platform/telemetry/node/1dsAppender'; import { UserDataProfilesCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/userDataProfilesCleaner'; +import { RemoteTunnelService } from 'vs/platform/remoteTunnel/electron-browser/remoteTunnelService'; +import { IRemoteTunnelService } from 'vs/platform/remoteTunnel/common/remoteTunnel'; +import { ISharedProcessLifecycleService, SharedProcessLifecycleService } from 'vs/platform/lifecycle/electron-browser/sharedProcessLifecycleService'; class SharedProcessMain extends Disposable { @@ -115,6 +118,8 @@ class SharedProcessMain extends Disposable { private sharedProcessWorkerService: ISharedProcessWorkerService | undefined = undefined; + private lifecycleService: SharedProcessLifecycleService | undefined = undefined;; + constructor(private configuration: ISharedProcessConfiguration) { super(); @@ -124,7 +129,14 @@ class SharedProcessMain extends Disposable { private registerListeners(): void { // Shared process lifecycle - const onExit = () => this.dispose(); + const onExit = async () => { + if (this.lifecycleService) { + await this.lifecycleService.fireOnWillShutdown(); + this.lifecycleService.dispose(); + this.lifecycleService = undefined; + } + this.dispose(); + }; process.once('exit', onExit); ipcRenderer.once('vscode:electron-main->shared-process=exit', onExit); @@ -180,6 +192,8 @@ class SharedProcessMain extends Disposable { private async initServices(): Promise { const services = new ServiceCollection(); + // Lifecycle + // Product const productService = { _serviceBrand: undefined, ...product }; services.set(IProductService, productService); @@ -211,6 +225,10 @@ class SharedProcessMain extends Disposable { const logService = this._register(new FollowerLogService(logLevelClient, multiplexLogger)); services.set(ILogService, logService); + // Lifecycle + this.lifecycleService = new SharedProcessLifecycleService(logService); + services.set(ISharedProcessLifecycleService, this.lifecycleService); + // Worker this.sharedProcessWorkerService = new SharedProcessWorkerService(logService); services.set(ISharedProcessWorkerService, this.sharedProcessWorkerService); @@ -342,6 +360,8 @@ class SharedProcessMain extends Disposable { services.set(IUserDataSyncService, new SyncDescriptor(UserDataSyncService, undefined, false /* Initializes the Sync State */)); services.set(IUserDataSyncProfilesStorageService, new SyncDescriptor(UserDataSyncProfilesStorageService, undefined, true)); + // Terminal + const ptyHostService = new PtyHostService({ graceTime: LocalReconnectConstants.GraceTime, shortGraceTime: LocalReconnectConstants.ShortGraceTime, @@ -353,7 +373,6 @@ class SharedProcessMain extends Disposable { ); ptyHostService.initialize(); - // Terminal services.set(ILocalPtyService, this._register(ptyHostService)); // Signing @@ -363,11 +382,18 @@ class SharedProcessMain extends Disposable { services.set(ISharedTunnelsService, new SyncDescriptor(SharedTunnelsService)); services.set(ISharedProcessTunnelService, new SyncDescriptor(SharedProcessTunnelService)); + // Remote Tunnel + services.set(IRemoteTunnelService, new SyncDescriptor(RemoteTunnelService)); + return new InstantiationService(services); } private initChannels(accessor: ServicesAccessor): void { + // Log Level + const logLevelChannel = new LogLevelChannel(accessor.get(ILogService), accessor.get(ILoggerService)); + this.server.registerChannel('logLevel', logLevelChannel); + // Extensions Management const channel = new ExtensionManagementChannel(accessor.get(IExtensionManagementService), () => null); this.server.registerChannel('extensions', channel); @@ -426,6 +452,10 @@ class SharedProcessMain extends Disposable { const sharedProcessWorkerChannel = ProxyChannel.fromService(accessor.get(ISharedProcessWorkerService)); this.server.registerChannel(ipcSharedProcessWorkerChannelName, sharedProcessWorkerChannel); + // Remote Tunnel + const remoteTunnelChannel = ProxyChannel.fromService(accessor.get(IRemoteTunnelService)); + this.server.registerChannel('remoteTunnel', remoteTunnelChannel); + } private registerErrorHandler(logService: ILogService): void { diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 2e57bc75e9f..b8e21690c1e 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -816,7 +816,7 @@ export class CodeApplication extends Disposable { mainProcessElectronServer.registerChannel('externalTerminal', externalTerminalChannel); // Log Level (main & shared process) - const logLevelChannel = new LogLevelChannel(accessor.get(ILogService)); + const logLevelChannel = new LogLevelChannel(accessor.get(ILogService), accessor.get(ILoggerService)); mainProcessElectronServer.registerChannel('logLevel', logLevelChannel); sharedProcessClient.then(client => client.registerChannel('logLevel', logLevelChannel)); @@ -918,6 +918,14 @@ export class CodeApplication extends Disposable { // or if no window is open (macOS only) shouldOpenInNewWindow ||= isMacintosh && windowsMainService.getWindowCount() === 0; + // Pass along whether the application is being opened via a Continue On flow + const continueOn = params.get('continueOn'); + if (continueOn !== null) { + environmentService.continueOn = continueOn ?? undefined; + params.delete('continueOn'); + uri = uri.with({ query: params.toString() }); + } + // Check for URIs to open in window const windowOpenableFromProtocolLink = app.getWindowOpenableFromProtocolLink(uri); logService.trace('app#handleURL: windowOpenableFromProtocolLink = ', windowOpenableFromProtocolLink); diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index e358efbb545..ca1cdec6ac2 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -47,7 +47,6 @@ import { ILifecycleMainService, LifecycleMainService } from 'vs/platform/lifecyc import { BufferLogService } from 'vs/platform/log/common/bufferLog'; import { ConsoleMainLogger, getLogLevel, ILoggerService, ILogService, MultiplexLogService } from 'vs/platform/log/common/log'; import { LoggerService } from 'vs/platform/log/node/loggerService'; -import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog'; import product from 'vs/platform/product/common/product'; import { IProductService } from 'vs/platform/product/common/productService'; import { IProtocolMainService } from 'vs/platform/protocol/electron-main/protocol'; @@ -117,6 +116,7 @@ class CodeMain { const logService = accessor.get(ILogService); const lifecycleMainService = accessor.get(ILifecycleMainService); const fileService = accessor.get(IFileService); + const loggerService = accessor.get(ILoggerService); // Create the main IPC server by trying to be the server // If this throws an error it means we are not the first @@ -129,7 +129,7 @@ class CodeMain { }); // Delay creation of spdlog for perf reasons (https://github.com/microsoft/vscode/issues/72906) - bufferLogService.logger = new SpdLogLogger('main', join(environmentMainService.logsPath, 'main.log'), true, false, bufferLogService.getLevel()); + bufferLogService.logger = loggerService.createLogger(URI.file(join(environmentMainService.logsPath, 'main.log')), { name: 'main' }); // Lifecycle once(lifecycleMainService.onWillShutdown)(evt => { @@ -177,7 +177,7 @@ class CodeMain { services.set(IUriIdentityService, uriIdentityService); // Logger - services.set(ILoggerService, new LoggerService(logService, fileService)); + services.set(ILoggerService, new LoggerService(logService)); // State const stateMainService = new StateMainService(environmentMainService, logService, fileService); diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index e427bd7c6fb..297b2cf012e 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -8,7 +8,7 @@ import { chmodSync, existsSync, readFileSync, statSync, truncateSync, unlinkSync import { homedir, release, tmpdir } from 'os'; import type { ProfilingSession, Target } from 'v8-inspect-profiler'; import { Event } from 'vs/base/common/event'; -import { isAbsolute, resolve, join } from 'vs/base/common/path'; +import { isAbsolute, resolve, join, dirname } from 'vs/base/common/path'; import { IProcessEnvironment, isMacintosh, isWindows } from 'vs/base/common/platform'; import { randomPort } from 'vs/base/common/ports'; import { isString } from 'vs/base/common/types'; @@ -24,7 +24,6 @@ import product from 'vs/platform/product/common/product'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { randomPath } from 'vs/base/common/extpath'; import { Utils } from 'vs/platform/profiling/common/profiling'; -import { dirname } from 'vs/base/common/resources'; import { FileAccess } from 'vs/base/common/network'; function shouldSpawnCliProcess(argv: NativeParsedArgs): boolean { @@ -50,6 +49,31 @@ export async function main(argv: string[]): Promise { return; } + if (args.tunnel) { + if (!product.tunnelApplicationName) { + console.error(`'tunnel' command not supported in ${product.applicationName}`); + return; + } + return new Promise((resolve, reject) => { + let tunnelProcess; + if (process.env['VSCODE_DEV']) { + tunnelProcess = spawn('cargo', ['run', '--bin', 'code-tunnel', ...argv.slice(5)], { cwd: join(getAppRoot(), 'cli') }); + } else { + const tunnelCommand = join(dirname(process.execPath), 'bin', `${product.tunnelApplicationName}${isWindows ? '.exe' : ''}`); + const tunnelArgs = argv.slice(3); + tunnelProcess = spawn(tunnelCommand, tunnelArgs); + } + tunnelProcess.stdout.on('data', data => { + console.log(data.toString()); + }); + tunnelProcess.stderr.on('data', data => { + console.error(data.toString()); + }); + tunnelProcess.on('exit', resolve); + tunnelProcess.on('error', reject); + }); + } + // Help if (args.help) { const executable = `${product.applicationName}${isWindows ? '.exe' : ''}`; @@ -75,7 +99,7 @@ export async function main(argv: string[]): Promise { case 'fish': file = 'shellIntegration.fish'; break; default: throw new Error('Error using --locate-shell-integration-path: Invalid shell type'); } - console.log(join(dirname(FileAccess.asFileUri('', require)).fsPath, 'out', 'vs', 'workbench', 'contrib', 'terminal', 'browser', 'media', file)); + console.log(join(getAppRoot(), 'out', 'vs', 'workbench', 'contrib', 'terminal', 'browser', 'media', file)); } // Extensions Management @@ -467,6 +491,10 @@ export async function main(argv: string[]): Promise { } } +function getAppRoot() { + return dirname(FileAccess.asFileUri('', require).fsPath); +} + function eventuallyExit(code: number): void { setTimeout(() => process.exit(code), 0); } diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index d768689d23a..9f21950587b 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -801,7 +801,7 @@ export interface ICodeEditor extends editorCommon.IEditor { * @id Unique identifier of the contribution. * @return The action or null if action not found. */ - getAction(id: string): editorCommon.IEditorAction; + getAction(id: string): editorCommon.IEditorAction | null; /** * Execute a command on the editor. @@ -1121,6 +1121,12 @@ export interface IDiffEditor extends editorCommon.IEditor { */ readonly onDidUpdateDiff: Event; + /** + * An event emitted when the diff model is changed (i.e. the diff editor shows new content). + * @event + */ + readonly onDidChangeModel: Event; + /** * Saves current view state of the editor in a serializable object. */ diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index f61142282d3..7cdfebe3ad7 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -486,7 +486,7 @@ export namespace EditorExtensionsRegistry { return EditorContributionRegistry.INSTANCE.getEditorCommand(commandId); } - export function getEditorActions(): EditorAction[] { + export function getEditorActions(): Iterable { return EditorContributionRegistry.INSTANCE.getEditorActions(); } @@ -545,8 +545,8 @@ class EditorContributionRegistry { this.editorActions.push(action); } - public getEditorActions(): EditorAction[] { - return this.editorActions.slice(0); + public getEditorActions(): Iterable { + return this.editorActions; } public registerEditorCommand(editorCommand: EditorCommand) { diff --git a/src/vs/editor/browser/services/abstractCodeEditorService.ts b/src/vs/editor/browser/services/abstractCodeEditorService.ts index aaa31ee4ce4..61b1a394a06 100644 --- a/src/vs/editor/browser/services/abstractCodeEditorService.ts +++ b/src/vs/editor/browser/services/abstractCodeEditorService.ts @@ -156,6 +156,10 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC provider.refCount++; } + public listDecorationTypes(): string[] { + return Array.from(this._decorationOptionProviders.keys()); + } + public removeDecorationType(key: string): void { const provider = this._decorationOptionProviders.get(key); if (provider) { diff --git a/src/vs/editor/browser/services/bulkEditService.ts b/src/vs/editor/browser/services/bulkEditService.ts index 12d1417489a..4f06c9ca68b 100644 --- a/src/vs/editor/browser/services/bulkEditService.ts +++ b/src/vs/editor/browser/services/bulkEditService.ts @@ -108,6 +108,7 @@ export interface IBulkEditOptions { export interface IBulkEditResult { ariaSummary: string; + isApplied: boolean; } export type IBulkEditPreviewHandler = (edits: ResourceEdit[], options?: IBulkEditOptions) => Promise; diff --git a/src/vs/editor/browser/services/codeEditorService.ts b/src/vs/editor/browser/services/codeEditorService.ts index 40d7947efcd..9de2e964a03 100644 --- a/src/vs/editor/browser/services/codeEditorService.ts +++ b/src/vs/editor/browser/services/codeEditorService.ts @@ -41,6 +41,7 @@ export interface ICodeEditorService { getFocusedCodeEditor(): ICodeEditor | null; registerDecorationType(description: string, key: string, options: IDecorationRenderOptions, parentTypeKey?: string, editor?: ICodeEditor): void; + listDecorationTypes(): string[]; removeDecorationType(key: string): void; resolveDecorationOptions(typeKey: string, writable: boolean): IModelDecorationOptions; resolveDecorationCSSRules(decorationTypeKey: string): CSSRuleList | null; diff --git a/src/vs/editor/browser/viewParts/blockDecorations/blockDecorations.ts b/src/vs/editor/browser/viewParts/blockDecorations/blockDecorations.ts index 48f52d6d231..87057ba9ce6 100644 --- a/src/vs/editor/browser/viewParts/blockDecorations/blockDecorations.ts +++ b/src/vs/editor/browser/viewParts/blockDecorations/blockDecorations.ts @@ -82,10 +82,20 @@ export class BlockDecorations extends ViewPart { block = this.blocks[count] = createFastDomNode(document.createElement('div')); this.domNode.appendChild(block); } - const top = ctx.getVerticalOffsetForLineNumber(decoration.range.startLineNumber, true); - const bottom = decoration.range.isEmpty() - ? ctx.getVerticalOffsetForLineNumber(decoration.range.startLineNumber, false) - : ctx.getVerticalOffsetAfterLineNumber(decoration.range.endLineNumber, true); + + let top: number; + let bottom: number; + + if (decoration.options.blockIsAfterEnd) { + // range must be empty + top = ctx.getVerticalOffsetAfterLineNumber(decoration.range.endLineNumber, false); + bottom = ctx.getVerticalOffsetAfterLineNumber(decoration.range.endLineNumber, true); + } else { + top = ctx.getVerticalOffsetForLineNumber(decoration.range.startLineNumber, true); + bottom = decoration.range.isEmpty() + ? ctx.getVerticalOffsetForLineNumber(decoration.range.startLineNumber, false) + : ctx.getVerticalOffsetAfterLineNumber(decoration.range.endLineNumber, true); + } block.setClassName('blockDecorations-block ' + decoration.options.blockClassName); block.setLeft(ctx.scrollLeft); diff --git a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts index f45578d9f15..37c8f7ac5d0 100644 --- a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts +++ b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts @@ -432,6 +432,12 @@ class Widget { } } + // Left-align widgets that should appear :before content + if (this._affinity === PositionAffinity.LeftOfInjectedText && + this._viewRange.startColumn === 1) { + firstLineMinLeft = 0; + } + let lastLineMinLeft = Constants.MAX_SAFE_SMALL_INTEGER;//lastLine.Constants.MAX_SAFE_SMALL_INTEGER; for (const visibleRange of lastLine.ranges) { if (visibleRange.left < lastLineMinLeft) { diff --git a/src/vs/editor/browser/viewParts/lines/viewLines.css b/src/vs/editor/browser/viewParts/lines/viewLines.css index 315306d65f2..f83ea79dd02 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLines.css +++ b/src/vs/editor/browser/viewParts/lines/viewLines.css @@ -25,6 +25,15 @@ user-select: none; -webkit-user-select: none; } +/* Use user-select: text for lookup feature on macOS */ +/* https://github.com/microsoft/vscode/issues/85632 */ +.monaco-editor.mac .lines-content:hover, +.monaco-editor.mac .view-line:hover, +.monaco-editor.mac .view-lines:hover { + user-select: text; + -webkit-user-select: text; + -ms-user-select: text; +} .monaco-editor.enable-user-select { user-select: initial; diff --git a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts index 5c376b3e385..86db61f97e1 100644 --- a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts +++ b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts @@ -31,7 +31,7 @@ class Settings { public readonly cursorColor: string | null; public readonly themeType: 'light' | 'dark' | 'hcLight' | 'hcDark'; - public readonly backgroundColor: string | null; + public readonly backgroundColor: Color | null; public readonly top: number; public readonly right: number; @@ -64,18 +64,13 @@ class Settings { const minimapSide = minimapOpts.side; const themeColor = theme.getColor(editorOverviewRulerBackground); const defaultBackground = TokenizationRegistry.getDefaultBackground(); - let backgroundColor: Color | null = null; - if (themeColor !== undefined) { - backgroundColor = themeColor; - } else if (minimapEnabled) { - backgroundColor = defaultBackground; - } - - if (backgroundColor === null || minimapSide === 'left') { - this.backgroundColor = null; + if (themeColor) { + this.backgroundColor = themeColor; + } else if (minimapEnabled && minimapSide === 'right') { + this.backgroundColor = defaultBackground; } else { - this.backgroundColor = Color.Format.CSS.formatHex(backgroundColor); + this.backgroundColor = null; } const layoutInfo = options.get(EditorOption.layoutInfo); @@ -195,7 +190,7 @@ class Settings { && this.hideCursor === other.hideCursor && this.cursorColor === other.cursorColor && this.themeType === other.themeType - && this.backgroundColor === other.backgroundColor + && Color.equals(this.backgroundColor, other.backgroundColor) && this.top === other.top && this.right === other.right && this.domWidth === other.domWidth @@ -320,9 +315,10 @@ export class DecorationsOverviewRuler extends ViewPart { } private _render(): void { + const backgroundColor = this._settings.backgroundColor; if (this._settings.overviewRulerLanes === 0) { // overview ruler is off - this._domNode.setBackgroundColor(this._settings.backgroundColor ? this._settings.backgroundColor : ''); + this._domNode.setBackgroundColor(backgroundColor ? Color.Format.CSS.formatHexA(backgroundColor) : ''); this._domNode.setDisplay('none'); return; } @@ -339,11 +335,21 @@ export class DecorationsOverviewRuler extends ViewPart { const halfMinDecorationHeight = (minDecorationHeight / 2) | 0; const canvasCtx = this._domNode.domNode.getContext('2d')!; - if (this._settings.backgroundColor === null) { - canvasCtx.clearRect(0, 0, canvasWidth, canvasHeight); + if (backgroundColor) { + if (backgroundColor.isOpaque()) { + // We have a background color which is opaque, we can just paint the entire surface with it + canvasCtx.fillStyle = Color.Format.CSS.formatHexA(backgroundColor); + canvasCtx.fillRect(0, 0, canvasWidth, canvasHeight); + } else { + // We have a background color which is transparent, we need to first clear the surface and + // then fill it + canvasCtx.clearRect(0, 0, canvasWidth, canvasHeight); + canvasCtx.fillStyle = Color.Format.CSS.formatHexA(backgroundColor); + canvasCtx.fillRect(0, 0, canvasWidth, canvasHeight); + } } else { - canvasCtx.fillStyle = this._settings.backgroundColor; - canvasCtx.fillRect(0, 0, canvasWidth, canvasHeight); + // We don't have a background color + canvasCtx.clearRect(0, 0, canvasWidth, canvasHeight); } const x = this._settings.x; diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 8a49db0edb7..65ae5f61c9f 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -14,7 +14,7 @@ import { Color } from 'vs/base/common/color'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, EmitterOptions, Event, EventDeliveryQueue } from 'vs/base/common/event'; import { hash } from 'vs/base/common/hash'; -import { Disposable, IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, dispose, DisposableStore, DisposableMap } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { EditorConfiguration, IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; import * as editorBrowser from 'vs/editor/browser/editorBrowser'; @@ -241,8 +241,8 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE private readonly _id: number; private readonly _configuration: IEditorConfiguration; - protected _contributions: { [key: string]: editorCommon.IEditorContribution }; - protected _actions: { [key: string]: editorCommon.IEditorAction }; + protected readonly _contributions = new DisposableMap(); + protected readonly _actions = new Map(); // --- Members logically associated to a model protected _modelData: ModelData | null; @@ -318,9 +318,6 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE this._modelData = null; - this._contributions = {}; - this._actions = {}; - this._focusTracker = new CodeEditorWidgetFocusTracker(domElement); this._register(this._focusTracker.onChange(() => { this._editorWidgetFocus.setValue(this._focusTracker.hasFocus()); @@ -336,22 +333,22 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE contributions = EditorExtensionsRegistry.getEditorContributions(); } for (const desc of contributions) { - if (this._contributions[desc.id]) { + if (this._contributions.has(desc.id)) { onUnexpectedError(new Error(`Cannot have two contributions with the same id ${desc.id}`)); continue; } try { const contribution = this._instantiationService.createInstance(desc.ctor, this); - this._contributions[desc.id] = contribution; + this._contributions.set(desc.id, contribution); } catch (err) { onUnexpectedError(err); } } - EditorExtensionsRegistry.getEditorActions().forEach((action) => { - if (this._actions[action.id]) { + for (const action of EditorExtensionsRegistry.getEditorActions()) { + if (this._actions.has(action.id)) { onUnexpectedError(new Error(`Cannot have two actions with the same id ${action.id}`)); - return; + continue; } const internalAction = new InternalEditorAction( action.id, @@ -365,8 +362,8 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE }, this._contextKeyService ); - this._actions[internalAction.id] = internalAction; - }); + this._actions.set(internalAction.id, internalAction); + } const isDropIntoEnabled = () => { return !this._configuration.options.get(EditorOption.readOnly) @@ -429,14 +426,8 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE this._codeEditorService.removeCodeEditor(this); this._focusTracker.dispose(); - - const keys = Object.keys(this._contributions); - for (let i = 0, len = keys.length; i < len; i++) { - const contributionId = keys[i]; - this._contributions[contributionId].dispose(); - } - this._contributions = {}; - this._actions = {}; + this._contributions.dispose(); + this._actions.clear(); this._contentWidgets = {}; this._overlayWidgets = {}; @@ -1001,9 +992,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } const contributionsState: { [key: string]: any } = {}; - const keys = Object.keys(this._contributions); - for (const id of keys) { - const contribution = this._contributions[id]; + for (const [id, contribution] of this._contributions) { if (typeof contribution.saveViewState === 'function') { contributionsState[id] = contribution.saveViewState(); } @@ -1035,10 +1024,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } const contributionsState = codeEditorState.contributionsState || {}; - const keys = Object.keys(this._contributions); - for (let i = 0, len = keys.length; i < len; i++) { - const id = keys[i]; - const contribution = this._contributions[id]; + for (const [id, contribution] of this._contributions) { if (typeof contribution.restoreViewState === 'function') { contribution.restoreViewState(contributionsState[id]); } @@ -1059,19 +1045,11 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } public getContribution(id: string): T | null { - return (this._contributions[id] || null); + return (this._contributions.get(id) || null); } public getActions(): editorCommon.IEditorAction[] { - const result: editorCommon.IEditorAction[] = []; - - const keys = Object.keys(this._actions); - for (let i = 0, len = keys.length; i < len; i++) { - const id = keys[i]; - result.push(this._actions[id]); - } - - return result; + return Array.from(this._actions.values()); } public getSupportedActions(): editorCommon.IEditorAction[] { @@ -1082,8 +1060,8 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return result; } - public getAction(id: string): editorCommon.IEditorAction { - return this._actions[id] || null; + public getAction(id: string): editorCommon.IEditorAction | null { + return this._actions.get(id) || null; } public trigger(source: string | null | undefined, handlerId: string, payload: any): void { diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index 9ca599671d4..90af6898315 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -172,6 +172,8 @@ const diffInsertIcon = registerIcon('diff-insert', Codicon.add, nls.localize('di const diffRemoveIcon = registerIcon('diff-remove', Codicon.remove, nls.localize('diffRemoveIcon', 'Line decoration for removals in the diff editor.')); const ttPolicy = window.trustedTypes?.createPolicy('diffEditorWidget', { createHTML: value => value }); +const ariaNavigationTip = nls.localize('diff-aria-navigation-tip', ' use Shift + F7 to navigate changes'); + export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffEditor { private static readonly ONE_OVERVIEW_WIDTH = 15; @@ -181,6 +183,9 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE private readonly _onDidDispose: Emitter = this._register(new Emitter()); public readonly onDidDispose: Event = this._onDidDispose.event; + protected readonly _onDidChangeModel: Emitter = this._register(new Emitter()); + public readonly onDidChangeModel: Event = this._onDidChangeModel.event; + private readonly _onDidUpdateDiff: Emitter = this._register(new Emitter()); public readonly onDidUpdateDiff: Event = this._onDidUpdateDiff.event; @@ -830,6 +835,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } this._layoutOverviewViewport(); + + this._onDidChangeModel.fire(); } public getContainerDomNode(): HTMLElement { @@ -1241,6 +1248,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE if (options.originalAriaLabel) { result.ariaLabel = options.originalAriaLabel; } + result.ariaLabel += ariaNavigationTip; result.readOnly = !this._options.originalEditable; result.dropIntoEditor = { enabled: !result.readOnly }; result.extraEditorClassName = 'original-in-monaco-diff-editor'; @@ -1258,7 +1266,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE if (options.modifiedAriaLabel) { result.ariaLabel = options.modifiedAriaLabel; } - + result.ariaLabel += ariaNavigationTip; result.wordWrapOverride1 = this._options.diffWordWrap; result.revealHorizontalRightPadding = EditorOptions.revealHorizontalRightPadding.defaultValue + DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH; result.scrollbar!.verticalHasArrows = false; diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 6e08c683030..f3ac6a40cf9 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -1183,6 +1183,7 @@ class EditorAccessibilitySupport extends BaseEditorOption { + for (let i = 0; i < equalLinesCount; i++) { + const seq1Offset = seq1LastStart + i; + const seq2Offset = seq2LastStart + i; + if (originalLines[seq1Offset] !== modifiedLines[seq2Offset]) { + // This is because of whitespace changes, diff these lines + const characterDiffs = this.refineDiff(originalLines, modifiedLines, new SequenceDiff( + new OffsetRange(seq1Offset, seq1Offset + 1), + new OffsetRange(seq2Offset, seq2Offset + 1) + )); + for (const a of characterDiffs) { + alignments.push(a); + } + } + } + }; + + let seq1LastStart = 0; + let seq2LastStart = 0; + for (const diff of lineAlignments) { + assertFn(() => diff.seq1Range.start - seq1LastStart === diff.seq2Range.start - seq2LastStart); + + const equalLinesCount = diff.seq1Range.start - seq1LastStart; + + scanForWhitespaceChanges(equalLinesCount); + + seq1LastStart = diff.seq1Range.endExclusive; + seq2LastStart = diff.seq2Range.endExclusive; + const characterDiffs = this.refineDiff(originalLines, modifiedLines, diff); for (const a of characterDiffs) { alignments.push(a); } } + scanForWhitespaceChanges(originalLines.length - seq1LastStart); + const changes: LineRangeMapping[] = lineRangeMappingFromRangeMappings(alignments); return { diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index 5cefe83dfc8..6c7badf0b8a 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -92,6 +92,11 @@ export interface IModelDecorationOptions { */ className?: string | null; blockClassName?: string | null; + /** + * Indicates if this block should be rendered after the last line. + * In this case, the range must be empty and set to the last line. + */ + blockIsAfterEnd?: boolean | null; /** * Message to be rendered when hovering over the glyph margin decoration. */ diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 102dc04c973..27ee2e71319 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -2231,6 +2231,7 @@ export class ModelDecorationOptions implements model.IModelDecorationOptions { readonly description: string; readonly blockClassName: string | null; + readonly blockIsAfterEnd: boolean | null; readonly stickiness: model.TrackedRangeStickiness; readonly zIndex: number; readonly className: string | null; @@ -2258,6 +2259,7 @@ export class ModelDecorationOptions implements model.IModelDecorationOptions { private constructor(options: model.IModelDecorationOptions) { this.description = options.description; this.blockClassName = options.blockClassName ? cleanClassName(options.blockClassName) : null; + this.blockIsAfterEnd = options.blockIsAfterEnd ?? null; this.stickiness = options.stickiness || model.TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges; this.zIndex = options.zIndex || 0; this.className = options.className ? cleanClassName(options.className) : null; diff --git a/src/vs/editor/common/services/languageFeatureDebounce.ts b/src/vs/editor/common/services/languageFeatureDebounce.ts index e4e67bd700f..417a2d9dc08 100644 --- a/src/vs/editor/common/services/languageFeatureDebounce.ts +++ b/src/vs/editor/common/services/languageFeatureDebounce.ts @@ -8,7 +8,7 @@ import { LRUCache } from 'vs/base/common/map'; import { clamp, MovingAverage, SlidingWindowAverage } from 'vs/base/common/numbers'; import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry'; import { ITextModel } from 'vs/editor/common/model'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { matchesScheme } from 'vs/platform/opener/common/opener'; @@ -136,4 +136,4 @@ export class LanguageFeatureDebounceService implements ILanguageFeatureDebounceS } } -registerSingleton(ILanguageFeatureDebounceService, LanguageFeatureDebounceService, true); +registerSingleton(ILanguageFeatureDebounceService, LanguageFeatureDebounceService, InstantiationType.Delayed); diff --git a/src/vs/editor/common/services/languageFeaturesService.ts b/src/vs/editor/common/services/languageFeaturesService.ts index 4059a90b370..35d321b4ef2 100644 --- a/src/vs/editor/common/services/languageFeaturesService.ts +++ b/src/vs/editor/common/services/languageFeaturesService.ts @@ -7,7 +7,7 @@ import { URI } from 'vs/base/common/uri'; import { LanguageFeatureRegistry, NotebookInfo, NotebookInfoResolver } from 'vs/editor/common/languageFeatureRegistry'; import { CodeActionProvider, CodeLensProvider, CompletionItemProvider, DocumentPasteEditProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentOnDropEditProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSemanticTokensProvider, DocumentSymbolProvider, EvaluatableExpressionProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, InlineValuesProvider, LinkedEditingRangeProvider, LinkProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider } from 'vs/editor/common/languages'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class LanguageFeaturesService implements ILanguageFeaturesService { @@ -55,4 +55,4 @@ export class LanguageFeaturesService implements ILanguageFeaturesService { } -registerSingleton(ILanguageFeaturesService, LanguageFeaturesService, true); +registerSingleton(ILanguageFeaturesService, LanguageFeaturesService, InstantiationType.Delayed); diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionUi.ts b/src/vs/editor/contrib/codeAction/browser/codeActionUi.ts index 13e0d5fe187..3066ae54574 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionUi.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionUi.ts @@ -15,6 +15,7 @@ import { CodeActionTriggerType } from 'vs/editor/common/languages'; import { CodeActionItem, CodeActionSet } from 'vs/editor/contrib/codeAction/browser/codeAction'; import { MessageController } from 'vs/editor/contrib/message/browser/messageController'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { CodeActionsState } from './codeActionModel'; import { CodeActionShowOptions, CodeActionWidget } from './codeActionWidget'; @@ -34,8 +35,9 @@ export class CodeActionUi extends Disposable { private readonly delegate: { applyCodeAction: (action: CodeActionItem, regtriggerAfterApply: boolean, preview: boolean) => Promise; }, - @IInstantiationService private readonly _instantiationService: IInstantiationService, @IConfigurationService private readonly _configurationService: IConfigurationService, + @IContextKeyService private readonly _contextKeyService: IContextKeyService, + @IInstantiationService private readonly _instantiationService: IInstantiationService, ) { super(); @@ -166,7 +168,7 @@ export class CodeActionUi extends Disposable { onHide: () => { this._editor?.focus(); }, - }); + }, this._contextKeyService); } private toCoords(position: IPosition): IAnchor { diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionWidget.css b/src/vs/editor/contrib/codeAction/browser/codeActionWidget.css index 2561c7f4f04..c788bfe5d1e 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionWidget.css +++ b/src/vs/editor/contrib/codeAction/browser/codeActionWidget.css @@ -12,10 +12,8 @@ display: block; width: 100%; border: 1px solid var(--vscode-editorWidget-border) !important; - border-color: none; background-color: var(--vscode-editorWidget-background); - color: var(--vscode--editorWidget-foreground); - box-shadow: var(--vscode-widget-shadow) 0 2px 8px; + color: var(--vscode-editorWidget-foreground); } .codeActionWidget .monaco-list { @@ -42,7 +40,7 @@ width: 100%; } -.codeActionWidget .monaco-list .monaco-list-row.code-action.focused:not(.option-disabled) { +.codeActionWidget .monaco-list .monaco-list-row.code-action.focused:not(.option-disabled) { background-color: var(--vscode-quickInputList-focusBackground); color: var(--vscode-quickInputList-focusForeground); outline: 1px solid var(--vscode-menu-selectionBorder, transparent); @@ -51,7 +49,7 @@ .codeActionWidget .monaco-list-row.group-header { color: var(--vscode-pickerGroup-foreground); - font-weight: bold; + font-weight: 600; } .codeActionWidget .monaco-list .group-header, @@ -72,7 +70,7 @@ .codeActionWidget .monaco-list-row.code-action { display: flex; - gap: 10px; + gap: 6px; align-items: center; } @@ -98,28 +96,30 @@ .codeActionWidget .codeActionWidget-action-bar { margin-top: 4px; - margin-bottom: 4px; + background-color: var(--vscode-editorHoverWidget-statusBarBackground); + border-top: 1px solid var(--vscode-editorHoverWidget-border); } .codeActionWidget .codeActionWidget-action-bar::before { display: block; content: ""; width: 100%; - margin-bottom: 4px; - border-bottom: 1px solid var(--vscode-menu-separatorBackground); } .codeActionWidget .codeActionWidget-action-bar .actions-container { - padding: 0 10px; + padding: 0 8px; } .codeActionWidget-action-bar .action-label { color: var(--vscode-textLink-activeForeground); + font-size: 12px; + line-height: 22px; + padding: 0; pointer-events: all; } .codeActionWidget-action-bar .action-item { - margin-right: 10px; + margin-right: 16px; pointer-events: none; } diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionWidget.ts b/src/vs/editor/contrib/codeAction/browser/codeActionWidget.ts index 7c3180aa563..a35cb65a441 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionWidget.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionWidget.ts @@ -20,7 +20,7 @@ import { CodeActionKind, CodeActionTrigger, CodeActionTriggerSource } from 'vs/e import 'vs/editor/contrib/symbolIcons/browser/symbolIcons'; // The codicon symbol colors are defined here and must be loaded to get colors import { localize } from 'vs/nls'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -369,40 +369,44 @@ export class CodeActionWidget extends Disposable { readonly container: HTMLElement | undefined; readonly codeActions: CodeActionSet; readonly delegate: CodeActionWidgetDelegate; + readonly contextKeyService: IContextKeyService; }; - private readonly _ctxMenuWidgetVisible: IContextKey; - constructor( @ICommandService private readonly _commandService: ICommandService, - @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IContextViewService private readonly _contextViewService: IContextViewService, @IKeybindingService private readonly _keybindingService: IKeybindingService, @ITelemetryService private readonly _telemetryService: ITelemetryService, ) { super(); - - this._ctxMenuWidgetVisible = Context.Visible.bindTo(this._contextKeyService); } get isVisible(): boolean { return !!this.currentShowingContext; } - public async show(trigger: CodeActionTrigger, codeActions: CodeActionSet, anchor: IAnchor, container: HTMLElement | undefined, options: CodeActionShowOptions, delegate: CodeActionWidgetDelegate): Promise { + public async show(trigger: CodeActionTrigger, codeActions: CodeActionSet, anchor: IAnchor, container: HTMLElement | undefined, options: CodeActionShowOptions, delegate: CodeActionWidgetDelegate, contextKeyService: IContextKeyService): Promise { this.currentShowingContext = undefined; + const visibleContext = Context.Visible.bindTo(contextKeyService); const actionsToShow = options.includeDisabledActions && (showDisabled || codeActions.validActions.length === 0) ? codeActions.allActions : codeActions.validActions; if (!actionsToShow.length) { + visibleContext.reset(); return; } - this.currentShowingContext = { trigger, codeActions, anchor, container, delegate, options }; + this.currentShowingContext = { trigger, codeActions, anchor, container, delegate, options, contextKeyService }; this._contextViewService.showContextView({ getAnchor: () => anchor, - render: (container: HTMLElement) => this.renderWidget(container, trigger, codeActions, options, actionsToShow, delegate), - onHide: (didCancel: boolean) => this.onWidgetClosed(trigger, options, codeActions, didCancel, delegate), + render: (container: HTMLElement) => { + visibleContext.set(true); + return this.renderWidget(container, trigger, codeActions, options, actionsToShow, delegate); + }, + onHide: (didCancel: boolean) => { + visibleContext.reset(); + return this.onWidgetClosed(trigger, options, codeActions, didCancel, delegate); + }, }, container, false); } @@ -419,7 +423,6 @@ export class CodeActionWidget extends Disposable { } public hide() { - this._ctxMenuWidgetVisible.reset(); this.codeActionList.clear(); this._contextViewService.hideContextView(); } @@ -488,8 +491,6 @@ export class CodeActionWidget extends Disposable { const focusTracker = renderDisposables.add(dom.trackFocus(element)); renderDisposables.add(focusTracker.onDidBlur(() => this.hide())); - this._ctxMenuWidgetVisible.set(true); - return renderDisposables; } @@ -504,7 +505,7 @@ export class CodeActionWidget extends Disposable { showDisabled = newShowDisabled; if (previousCtx) { - this.show(previousCtx.trigger, previousCtx.codeActions, previousCtx.anchor, previousCtx.container, previousCtx.options, previousCtx.delegate); + this.show(previousCtx.trigger, previousCtx.codeActions, previousCtx.anchor, previousCtx.container, previousCtx.options, previousCtx.delegate, previousCtx.contextKeyService); } } @@ -530,6 +531,7 @@ export class CodeActionWidget extends Disposable { }); this.currentShowingContext = undefined; + delegate.onHide(cancelled); } diff --git a/src/vs/editor/contrib/codelens/browser/codeLensCache.ts b/src/vs/editor/contrib/codelens/browser/codeLensCache.ts index f6696e42746..4ab8835ae2f 100644 --- a/src/vs/editor/contrib/codelens/browser/codeLensCache.ts +++ b/src/vs/editor/contrib/codelens/browser/codeLensCache.ts @@ -10,7 +10,7 @@ import { Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; import { CodeLens, CodeLensList, CodeLensProvider } from 'vs/editor/common/languages'; import { CodeLensModel } from 'vs/editor/contrib/codelens/browser/codelens'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService, StorageScope, StorageTarget, WillSaveStateReason } from 'vs/platform/storage/common/storage'; @@ -129,4 +129,4 @@ export class CodeLensCache implements ICodeLensCache { } } -registerSingleton(ICodeLensCache, CodeLensCache, true); +registerSingleton(ICodeLensCache, CodeLensCache, InstantiationType.Delayed); diff --git a/src/vs/editor/contrib/documentSymbols/browser/outlineModel.ts b/src/vs/editor/contrib/documentSymbols/browser/outlineModel.ts index bbebc0a585e..e9746fec571 100644 --- a/src/vs/editor/contrib/documentSymbols/browser/outlineModel.ts +++ b/src/vs/editor/contrib/documentSymbols/browser/outlineModel.ts @@ -17,7 +17,7 @@ import { DocumentSymbol, DocumentSymbolProvider } from 'vs/editor/common/languag import { MarkerSeverity } from 'vs/platform/markers/common/markers'; import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IModelService } from 'vs/editor/common/services/model'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry'; @@ -485,4 +485,4 @@ export class OutlineModelService implements IOutlineModelService { } } -registerSingleton(IOutlineModelService, OutlineModelService, true); +registerSingleton(IOutlineModelService, OutlineModelService, InstantiationType.Delayed); diff --git a/src/vs/editor/contrib/find/browser/findOptionsWidget.ts b/src/vs/editor/contrib/find/browser/findOptionsWidget.ts index ca04b53497d..7169c362279 100644 --- a/src/vs/editor/contrib/find/browser/findOptionsWidget.ts +++ b/src/vs/editor/contrib/find/browser/findOptionsWidget.ts @@ -43,6 +43,7 @@ export class FindOptionsWidget extends Widget implements IOverlayWidget { this._domNode.className = 'findOptionsWidget'; this._domNode.style.display = 'none'; this._domNode.style.top = '10px'; + this._domNode.style.zIndex = '12'; this._domNode.setAttribute('role', 'presentation'); this._domNode.setAttribute('aria-hidden', 'true'); diff --git a/src/vs/editor/contrib/find/browser/findWidget.ts b/src/vs/editor/contrib/find/browser/findWidget.ts index 9bb69442edc..72b7ad349e8 100644 --- a/src/vs/editor/contrib/find/browser/findWidget.ts +++ b/src/vs/editor/contrib/find/browser/findWidget.ts @@ -40,6 +40,7 @@ import { contrastBorder, editorFindMatch, editorFindMatchBorder, editorFindMatch import { registerIcon, widgetClose } from 'vs/platform/theme/common/iconRegistry'; import { IColorTheme, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { isHighContrast } from 'vs/platform/theme/common/theme'; +import { assertIsDefined } from 'vs/base/common/types'; const findSelectionIcon = registerIcon('find-selection', Codicon.selection, nls.localize('findSelectionIcon', 'Icon for \'Find in Selection\' in the editor find widget.')); const findCollapsedIcon = registerIcon('find-collapsed', Codicon.chevronRight, nls.localize('findCollapsedIcon', 'Icon to indicate that the editor find widget is collapsed.')); @@ -1033,7 +1034,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL label: NLS_PREVIOUS_MATCH_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.PreviousMatchFindAction), icon: findPreviousMatchIcon, onTrigger: () => { - this._codeEditor.getAction(FIND_IDS.PreviousMatchFindAction).run().then(undefined, onUnexpectedError); + assertIsDefined(this._codeEditor.getAction(FIND_IDS.PreviousMatchFindAction)).run().then(undefined, onUnexpectedError); } })); @@ -1042,7 +1043,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL label: NLS_NEXT_MATCH_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.NextMatchFindAction), icon: findNextMatchIcon, onTrigger: () => { - this._codeEditor.getAction(FIND_IDS.NextMatchFindAction).run().then(undefined, onUnexpectedError); + assertIsDefined(this._codeEditor.getAction(FIND_IDS.NextMatchFindAction)).run().then(undefined, onUnexpectedError); } })); diff --git a/src/vs/editor/contrib/folding/browser/folding.ts b/src/vs/editor/contrib/folding/browser/folding.ts index 8ed4b79072b..4f1e00e9223 100644 --- a/src/vs/editor/contrib/folding/browser/folding.ts +++ b/src/vs/editor/contrib/folding/browser/folding.ts @@ -197,7 +197,7 @@ export class FoldingController extends Disposable implements IEditorContribution if (!model || !this._isEnabled || model.isTooLargeForTokenization() || !this.hiddenRangeModel) { return; } - if (!state || state.lineCount !== model.getLineCount()) { + if (!state) { return; } diff --git a/src/vs/editor/contrib/gotoError/browser/markerNavigationService.ts b/src/vs/editor/contrib/gotoError/browser/markerNavigationService.ts index 8e7e0bb70d6..1162ef145ea 100644 --- a/src/vs/editor/contrib/gotoError/browser/markerNavigationService.ts +++ b/src/vs/editor/contrib/gotoError/browser/markerNavigationService.ts @@ -12,7 +12,7 @@ import { URI } from 'vs/base/common/uri'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IMarker, IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -221,4 +221,4 @@ class MarkerNavigationService implements IMarkerNavigationService, IMarkerListPr } } -registerSingleton(IMarkerNavigationService, MarkerNavigationService, true); +registerSingleton(IMarkerNavigationService, MarkerNavigationService, InstantiationType.Delayed); diff --git a/src/vs/editor/contrib/gotoSymbol/browser/goToCommands.ts b/src/vs/editor/contrib/gotoSymbol/browser/goToCommands.ts index cc7d0b8c67a..6360be59725 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/goToCommands.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/goToCommands.ts @@ -29,7 +29,7 @@ import { ISymbolNavigationService } from 'vs/editor/contrib/gotoSymbol/browser/s import { MessageController } from 'vs/editor/contrib/message/browser/messageController'; import { PeekContext } from 'vs/editor/contrib/peekView/browser/peekView'; import * as nls from 'vs/nls'; -import { IAction2Options, ISubmenuItem, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; +import { IAction2F1RequiredOptions, IAction2Options, ISubmenuItem, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { TextEditorSelectionRevealType, TextEditorSelectionSource } from 'vs/platform/editor/common/editor'; @@ -42,7 +42,6 @@ import { IWordAtPosition } from 'vs/editor/common/core/wordHelper'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { Iterable } from 'vs/base/common/iterator'; - MenuRegistry.appendMenuItem(MenuId.EditorContext, { submenu: MenuId.EditorContextPeek, title: nls.localize('peek.submenu', "Peek"), @@ -79,9 +78,11 @@ export abstract class SymbolNavigationAction extends EditorAction2 { private static _allSymbolNavigationCommands = new Map(); private static _activeAlternativeCommands = new Set(); - readonly configuration: SymbolNavigationActionConfig; + static all(): IterableIterator { + return SymbolNavigationAction._allSymbolNavigationCommands.values(); + } - private static aaa(opts: IAction2Options): IAction2Options { + private static _patchConfig(opts: IAction2Options & IAction2F1RequiredOptions): IAction2Options { const result = { ...opts, f1: true }; // patch context menu when clause if (result.menu) { @@ -95,8 +96,10 @@ export abstract class SymbolNavigationAction extends EditorAction2 { return result; } - constructor(configuration: SymbolNavigationActionConfig, opts: IAction2Options) { - super(SymbolNavigationAction.aaa(opts)); + readonly configuration: SymbolNavigationActionConfig; + + constructor(configuration: SymbolNavigationActionConfig, opts: IAction2Options & IAction2F1RequiredOptions) { + super(SymbolNavigationAction._patchConfig(opts)); this.configuration = configuration; SymbolNavigationAction._allSymbolNavigationCommands.set(opts.id, this); } diff --git a/src/vs/editor/contrib/gotoSymbol/browser/link/clickLinkGesture.ts b/src/vs/editor/contrib/gotoSymbol/browser/link/clickLinkGesture.ts index c11b578b773..a5777a61539 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/link/clickLinkGesture.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/link/clickLinkGesture.ts @@ -25,9 +25,15 @@ export class ClickLinkMouseEvent { public readonly hasTriggerModifier: boolean; public readonly hasSideBySideModifier: boolean; public readonly isNoneOrSingleMouseDown: boolean; + public readonly isLeftClick: boolean; + public readonly isMiddleClick: boolean; + public readonly isRightClick: boolean; constructor(source: IEditorMouseEvent, opts: ClickLinkOptions) { this.target = source.target; + this.isLeftClick = source.event.leftButton; + this.isMiddleClick = source.event.middleButton; + this.isRightClick = source.event.rightButton; this.hasTriggerModifier = hasModifier(source.event, opts.triggerModifier); this.hasSideBySideModifier = hasModifier(source.event, opts.triggerSideBySideModifier); this.isNoneOrSingleMouseDown = (source.event.detail <= 1); diff --git a/src/vs/editor/contrib/gotoSymbol/browser/link/goToDefinitionAtPosition.ts b/src/vs/editor/contrib/gotoSymbol/browser/link/goToDefinitionAtPosition.ts index d775f018888..48ec76ca093 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/link/goToDefinitionAtPosition.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/link/goToDefinitionAtPosition.ts @@ -277,11 +277,12 @@ export class GotoDefinitionAtPositionEditorContribution implements IEditorContri } private isEnabled(mouseEvent: ClickLinkMouseEvent, withKey?: ClickLinkKeyboardEvent): boolean { - return this.editor.hasModel() && - mouseEvent.isNoneOrSingleMouseDown && - (mouseEvent.target.type === MouseTargetType.CONTENT_TEXT) && - (mouseEvent.hasTriggerModifier || (withKey ? withKey.keyCodeIsTriggerKey : false)) && - this.languageFeaturesService.definitionProvider.has(this.editor.getModel()); + return this.editor.hasModel() + && mouseEvent.isLeftClick + && mouseEvent.isNoneOrSingleMouseDown + && mouseEvent.target.type === MouseTargetType.CONTENT_TEXT + && (mouseEvent.hasTriggerModifier || (withKey ? withKey.keyCodeIsTriggerKey : false)) + && this.languageFeaturesService.definitionProvider.has(this.editor.getModel()); } private findDefinition(position: Position, token: CancellationToken): Promise { @@ -297,7 +298,7 @@ export class GotoDefinitionAtPositionEditorContribution implements IEditorContri this.editor.setPosition(position); return this.editor.invokeWithinContext((accessor) => { const canPeek = !openToSide && this.editor.getOption(EditorOption.definitionLinkOpensInPeek) && !this.isInPeekEditor(accessor); - const action = new DefinitionAction({ openToSide, openInPeek: canPeek, muteMessage: true }, { title: '', id: '', precondition: undefined }); + const action = new DefinitionAction({ openToSide, openInPeek: canPeek, muteMessage: true }, { title: { value: '', original: '' }, id: '', precondition: undefined }); return action.run(accessor, this.editor); }); } diff --git a/src/vs/editor/contrib/gotoSymbol/browser/symbolNavigation.ts b/src/vs/editor/contrib/gotoSymbol/browser/symbolNavigation.ts index e0ff47c0935..2c7046f3d31 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/symbolNavigation.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/symbolNavigation.ts @@ -15,7 +15,7 @@ import { OneReference, ReferencesModel } from 'vs/editor/contrib/gotoSymbol/brow import { localize } from 'vs/nls'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { createDecorator, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -149,7 +149,7 @@ class SymbolNavigationService implements ISymbolNavigationService { } } -registerSingleton(ISymbolNavigationService, SymbolNavigationService, true); +registerSingleton(ISymbolNavigationService, SymbolNavigationService, InstantiationType.Delayed); registerEditorCommand(new class extends EditorCommand { diff --git a/src/vs/editor/contrib/hover/browser/contentHover.ts b/src/vs/editor/contrib/hover/browser/contentHover.ts index e827d2cec6c..e0f2246f5a6 100644 --- a/src/vs/editor/contrib/hover/browser/contentHover.ts +++ b/src/vs/editor/contrib/hover/browser/contentHover.ts @@ -13,7 +13,7 @@ import { ContentWidgetPositionPreference, IActiveCodeEditor, ICodeEditor, IConte import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import { IModelDecoration } from 'vs/editor/common/model'; +import { IModelDecoration, PositionAffinity } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { TokenizationRegistry } from 'vs/editor/common/languages'; import { HoverOperation, HoverStartMode, IHoverComputer } from 'vs/editor/contrib/hover/browser/hoverOperation'; @@ -24,6 +24,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Context as SuggestContext } from 'vs/editor/contrib/suggest/browser/suggest'; import { AsyncIterableObject } from 'vs/base/common/async'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { Constants } from 'vs/base/common/uint'; const $ = dom.$; @@ -129,8 +130,8 @@ export class ContentHoverController extends Disposable { } // The hover is currently visible - - const isGettingCloser = (mouseEvent && this._widget.isMouseGettingCloser(mouseEvent.event.posx, mouseEvent.event.posy)); + const hoverIsSticky = this._editor.getOption(EditorOption.hover).sticky; + const isGettingCloser = (hoverIsSticky && mouseEvent && this._widget.isMouseGettingCloser(mouseEvent.event.posx, mouseEvent.event.posy)); if (isGettingCloser) { // The mouse is getting closer to the hover, so we will keep the hover untouched // But we will kick off a hover update at the new anchor, insisting on keeping the hover visible. @@ -240,7 +241,7 @@ export class ContentHoverController extends Disposable { } private _renderMessages(anchor: HoverAnchor, messages: IHoverPart[]): void { - const { showAtPosition, showAtRange, highlightRange } = ContentHoverController.computeHoverRanges(anchor.range, messages); + const { showAtPosition, showAtRange, highlightRange } = ContentHoverController.computeHoverRanges(this._editor, anchor.range, messages); const disposables = new DisposableStore(); const statusBar = disposables.add(new EditorHoverStatusBar(this._keybindingService)); @@ -261,6 +262,9 @@ export class ContentHoverController extends Disposable { disposables.add(participant.renderHoverParts(context, hoverParts)); } } + + const isBeforeContent = messages.some(m => m.isBeforeContent); + if (statusBar.hasContent) { fragment.appendChild(statusBar.hoverElement); } @@ -283,6 +287,7 @@ export class ContentHoverController extends Disposable { showAtRange, this._editor.getOption(EditorOption.hover).above, this._computer.shouldFocus, + isBeforeContent, anchor.initialMousePosX, anchor.initialMousePosY, disposables @@ -297,7 +302,19 @@ export class ContentHoverController extends Disposable { className: 'hoverHighlight' }); - public static computeHoverRanges(anchorRange: Range, messages: IHoverPart[]) { + public static computeHoverRanges(editor: ICodeEditor, anchorRange: Range, messages: IHoverPart[]) { + let startColumnBoundary = 1; + let endColumnBoundary = Constants.MAX_SAFE_SMALL_INTEGER; + if (editor.hasModel()) { + // Ensure the range is on the current view line + const viewModel = editor._getViewModel(); + const coordinatesConverter = viewModel.coordinatesConverter; + const anchorViewRange = coordinatesConverter.convertModelRangeToViewRange(anchorRange); + const anchorViewRangeStart = new Position(anchorViewRange.startLineNumber, viewModel.getLineMinColumn(anchorViewRange.startLineNumber)); + const anchorViewRangeEnd = new Position(anchorViewRange.endLineNumber, viewModel.getLineMaxColumn(anchorViewRange.endLineNumber)); + startColumnBoundary = coordinatesConverter.convertViewPositionToModelPosition(anchorViewRangeStart).column; + endColumnBoundary = coordinatesConverter.convertViewPositionToModelPosition(anchorViewRangeEnd).column; + } // The anchor range is always on a single line const anchorLineNumber = anchorRange.startLineNumber; let renderStartColumn = anchorRange.startColumn; @@ -309,8 +326,8 @@ export class ContentHoverController extends Disposable { highlightRange = Range.plusRange(highlightRange, msg.range); if (msg.range.startLineNumber === anchorLineNumber && msg.range.endLineNumber === anchorLineNumber) { // this message has a range that is completely sitting on the line of the anchor - renderStartColumn = Math.min(renderStartColumn, msg.range.startColumn); - renderEndColumn = Math.max(renderEndColumn, msg.range.endColumn); + renderStartColumn = Math.max(Math.min(renderStartColumn, msg.range.startColumn), startColumnBoundary); + renderEndColumn = Math.min(Math.max(renderEndColumn, msg.range.endColumn), endColumnBoundary); } if (msg.forceShowAtRange) { forceShowAtRange = msg.range; @@ -368,6 +385,7 @@ class ContentHoverVisibleData { public readonly showAtRange: Range, public readonly preferAbove: boolean, public readonly stoleFocus: boolean, + public readonly isBeforeContent: boolean, public initialMousePosX: number | undefined, public initialMousePosY: number | undefined, public readonly disposables: DisposableStore @@ -439,6 +457,10 @@ export class ContentHoverWidget extends Disposable implements IContentWidget { // Prefer rendering above if the suggest widget is visible preferAbove = true; } + + // :before content can align left of the text content + const affinity = this._visibleData.isBeforeContent ? PositionAffinity.LeftOfInjectedText : undefined; + return { position: this._visibleData.showAtPosition, range: this._visibleData.showAtRange, @@ -447,6 +469,7 @@ export class ContentHoverWidget extends Disposable implements IContentWidget { ? [ContentWidgetPositionPreference.ABOVE, ContentWidgetPositionPreference.BELOW] : [ContentWidgetPositionPreference.BELOW, ContentWidgetPositionPreference.ABOVE] ), + positionAffinity: affinity }; } diff --git a/src/vs/editor/contrib/hover/browser/hoverTypes.ts b/src/vs/editor/contrib/hover/browser/hoverTypes.ts index 2f74d533f26..f07090dea59 100644 --- a/src/vs/editor/contrib/hover/browser/hoverTypes.ts +++ b/src/vs/editor/contrib/hover/browser/hoverTypes.ts @@ -26,6 +26,11 @@ export interface IHoverPart { * even in the case of multiple hover parts. */ readonly forceShowAtRange?: boolean; + + /** + * If true, the hover item should appear before content + */ + readonly isBeforeContent?: boolean; /** * Is this hover part still valid for this new anchor? */ diff --git a/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts b/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts index c2e2f56a91e..19d151cf757 100644 --- a/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts +++ b/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts @@ -30,6 +30,7 @@ export class MarkdownHover implements IHoverPart { public readonly owner: IEditorHoverParticipant, public readonly range: Range, public readonly contents: IMarkdownString[], + public readonly isBeforeContent: boolean, public readonly ordinal: number ) { } @@ -55,7 +56,7 @@ export class MarkdownHoverParticipant implements IEditorHoverParticipant= maxTokenizationLineLength) { result.push(new MarkdownHover(this, anchor.range, [{ value: nls.localize('too many characters', "Tokenization is skipped for long lines for performance reasons. This can be configured via `editor.maxTokenizationLineLength`.") - }], index++)); + }], false, index++)); } + let isBeforeContent = false; + for (const d of lineDecorations) { const startColumn = (d.range.startLineNumber === lineNumber) ? d.range.startColumn : 1; const endColumn = (d.range.endLineNumber === lineNumber) ? d.range.endColumn : maxColumn; @@ -90,8 +93,12 @@ export class MarkdownHoverParticipant implements IEditorHoverParticipant !isEmptyMarkdownString(item.hover.contents)) .map(item => { const rng = item.hover.range ? Range.lift(item.hover.range) : anchor.range; - return new MarkdownHover(this, rng, item.hover.contents, item.ordinal); + return new MarkdownHover(this, rng, item.hover.contents, false, item.ordinal); }); } diff --git a/src/vs/editor/contrib/hover/test/browser/contentHover.test.ts b/src/vs/editor/contrib/hover/test/browser/contentHover.test.ts index 7e35cea943b..1dacdd5a203 100644 --- a/src/vs/editor/contrib/hover/test/browser/contentHover.test.ts +++ b/src/vs/editor/contrib/hover/test/browser/contentHover.test.ts @@ -8,20 +8,45 @@ import { ContentHoverController } from 'vs/editor/contrib/hover/browser/contentH import { Range } from 'vs/editor/common/core/range'; import { Position } from 'vs/editor/common/core/position'; import { IHoverPart } from 'vs/editor/contrib/hover/browser/hoverTypes'; +import { TestCodeEditorInstantiationOptions, withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; suite('Content Hover', () => { test('issue #151235: Gitlens hover shows up in the wrong place', () => { - const actual = ContentHoverController.computeHoverRanges( - new Range(5, 5, 5, 5), - [{ range: new Range(4, 1, 5, 6) }] - ); - assert.deepStrictEqual( - actual, - { - showAtPosition: new Position(5, 5), - showAtRange: new Range(5, 5, 5, 5), - highlightRange: new Range(4, 1, 5, 6) - } - ); + const text = 'just some text'; + withTestCodeEditor(text, {}, (editor) => { + const actual = ContentHoverController.computeHoverRanges( + editor, + new Range(5, 5, 5, 5), + [{ range: new Range(4, 1, 5, 6) }] + ); + assert.deepStrictEqual( + actual, + { + showAtPosition: new Position(5, 5), + showAtRange: new Range(5, 5, 5, 5), + highlightRange: new Range(4, 1, 5, 6) + } + ); + }); + }); + + test('issue #95328: Hover placement with word-wrap', () => { + const text = 'just some text'; + const opts: TestCodeEditorInstantiationOptions = { wordWrap: 'wordWrapColumn', wordWrapColumn: 6 }; + withTestCodeEditor(text, opts, (editor) => { + const actual = ContentHoverController.computeHoverRanges( + editor, + new Range(1, 8, 1, 8), + [{ range: new Range(1, 1, 1, 15) }] + ); + assert.deepStrictEqual( + actual, + { + showAtPosition: new Position(1, 6), + showAtRange: new Range(1, 6, 1, 11), + highlightRange: new Range(1, 1, 1, 15) + } + ); + }); }); }); diff --git a/src/vs/editor/contrib/inlayHints/browser/inlayHintsController.ts b/src/vs/editor/contrib/inlayHints/browser/inlayHintsController.ts index 0e59778b739..4e7cce9783d 100644 --- a/src/vs/editor/contrib/inlayHints/browser/inlayHintsController.ts +++ b/src/vs/editor/contrib/inlayHints/browser/inlayHintsController.ts @@ -29,7 +29,7 @@ import { ClickLinkGesture, ClickLinkMouseEvent } from 'vs/editor/contrib/gotoSym import { InlayHintAnchor, InlayHintItem, InlayHintsFragments } from 'vs/editor/contrib/inlayHints/browser/inlayHints'; import { goToDefinitionWithLocation, showGoToContextMenu } from 'vs/editor/contrib/inlayHints/browser/inlayHintsLocations'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import * as colors from 'vs/platform/theme/common/colorRegistry'; @@ -60,7 +60,7 @@ class InlayHintsCache { interface IInlayHintsCache extends InlayHintsCache { } const IInlayHintsCache = createDecorator('IInlayHintsCache'); -registerSingleton(IInlayHintsCache, InlayHintsCache, true); +registerSingleton(IInlayHintsCache, InlayHintsCache, InstantiationType.Delayed); // --- rendered label diff --git a/src/vs/editor/contrib/inlayHints/browser/inlayHintsHover.ts b/src/vs/editor/contrib/inlayHints/browser/inlayHintsHover.ts index e04c6514d7a..f9781fcf029 100644 --- a/src/vs/editor/contrib/inlayHints/browser/inlayHintsHover.ts +++ b/src/vs/editor/contrib/inlayHints/browser/inlayHintsHover.ts @@ -92,11 +92,11 @@ export class InlayHintsHover extends MarkdownHoverParticipant implements IEditor itemTooltip = part.item.hint.tooltip; } if (itemTooltip) { - executor.emitOne(new MarkdownHover(this, anchor.range, [itemTooltip], 0)); + executor.emitOne(new MarkdownHover(this, anchor.range, [itemTooltip], false, 0)); } // (1.2) Inlay dbl-click gesture if (isNonEmptyArray(part.item.hint.textEdits)) { - executor.emitOne(new MarkdownHover(this, anchor.range, [new MarkdownString().appendText(localize('hint.dbl', "Double click to insert"))], 10001)); + executor.emitOne(new MarkdownHover(this, anchor.range, [new MarkdownString().appendText(localize('hint.dbl', "Double click to insert"))], false, 10001)); } // (2) Inlay Label Part Tooltip @@ -107,7 +107,7 @@ export class InlayHintsHover extends MarkdownHoverParticipant implements IEditor partTooltip = part.part.tooltip; } if (partTooltip) { - executor.emitOne(new MarkdownHover(this, anchor.range, [partTooltip], 1)); + executor.emitOne(new MarkdownHover(this, anchor.range, [partTooltip], false, 1)); } // (2.2) Inlay Label Part Help Hover @@ -130,7 +130,7 @@ export class InlayHintsHover extends MarkdownHoverParticipant implements IEditor linkHint = new MarkdownString(`[${localize('hint.cmd', "Execute Command")}](${asCommandLink(part.part.command)} "${part.part.command.title}") (${kb})`, { isTrusted: true }); } if (linkHint) { - executor.emitOne(new MarkdownHover(this, anchor.range, [linkHint], 10000)); + executor.emitOne(new MarkdownHover(this, anchor.range, [linkHint], false, 10000)); } } @@ -156,7 +156,7 @@ export class InlayHintsHover extends MarkdownHoverParticipant implements IEditor } return getHover(this._languageFeaturesService.hoverProvider, model, new Position(range.startLineNumber, range.startColumn), token) .filter(item => !isEmptyMarkdownString(item.hover.contents)) - .map(item => new MarkdownHover(this, part.item.anchor.range, item.hover.contents, 2 + item.ordinal)); + .map(item => new MarkdownHover(this, part.item.anchor.range, item.hover.contents, false, 2 + item.ordinal)); } finally { ref.dispose(); } diff --git a/src/vs/editor/contrib/inlayHints/browser/inlayHintsLocations.ts b/src/vs/editor/contrib/inlayHints/browser/inlayHintsLocations.ts index 89f650807bb..b2df34e97ec 100644 --- a/src/vs/editor/contrib/inlayHints/browser/inlayHintsLocations.ts +++ b/src/vs/editor/contrib/inlayHints/browser/inlayHintsLocations.ts @@ -7,7 +7,6 @@ import * as dom from 'vs/base/browser/dom'; import { Action, IAction, Separator } from 'vs/base/common/actions'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IActiveCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { Range } from 'vs/editor/common/core/range'; import { Location } from 'vs/editor/common/languages'; @@ -16,7 +15,7 @@ import { DefinitionAction, SymbolNavigationAction, SymbolNavigationAnchor } from import { ClickLinkMouseEvent } from 'vs/editor/contrib/gotoSymbol/browser/link/clickLinkGesture'; import { RenderedInlayHintLabelPart } from 'vs/editor/contrib/inlayHints/browser/inlayHintsController'; import { PeekContext } from 'vs/editor/contrib/peekView/browser/peekView'; -import { isIMenuItem, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; +import { isIMenuItem, MenuId, MenuItemAction, MenuRegistry } from 'vs/platform/actions/common/actions'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -41,13 +40,13 @@ export async function showGoToContextMenu(accessor: ServicesAccessor, editor: IC const menuActions: IAction[] = []; // from all registered (not active) context menu actions select those - // that are a symbol navigation action + // that are a symbol navigation actions const filter = new Set(MenuRegistry.getMenuItems(MenuId.EditorContext) .map(item => isIMenuItem(item) ? item.command.id : '')); - for (const delegate of EditorExtensionsRegistry.getEditorActions()) { - if (delegate instanceof SymbolNavigationAction && filter.has(delegate.id)) { - menuActions.push(new Action(delegate.id, delegate.label, undefined, true, async () => { + for (const delegate of SymbolNavigationAction.all()) { + if (filter.has(delegate.desc.id)) { + menuActions.push(new Action(delegate.desc.id, MenuItemAction.label(delegate.desc, { renderShortTitle: true }), undefined, true, async () => { const ref = await resolverService.createModelReference(location.uri); try { await instaService.invokeFunction(delegate.run.bind(delegate), editor, new SymbolNavigationAnchor(ref.object.textEditorModel, Range.getStartPosition(location.range))); @@ -105,7 +104,7 @@ export async function goToDefinitionWithLocation(accessor: ServicesAccessor, eve const isInPeek = PeekContext.inPeekEditor.getValue(contextKeyService); const canPeek = !openToSide && editor.getOption(EditorOption.definitionLinkOpensInPeek) && !isInPeek; - const action = new DefinitionAction({ openToSide, openInPeek: canPeek, muteMessage: true }, { title: '', id: '', precondition: undefined }); + const action = new DefinitionAction({ openToSide, openInPeek: canPeek, muteMessage: true }, { title: { value: '', original: '' }, id: '', precondition: undefined }); return action.run(accessor, editor, { model: ref.object.textEditorModel, position: Range.getStartPosition(location.range) }, Range.lift(location.range)); }); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts index 562960aa1ce..61595c2b880 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts @@ -512,6 +512,7 @@ export class InlineCompletionsSession extends BaseGhostTextWidgetModel { // otherwise command args might get disposed. const cache = this.cache.clearAndLeak(); + this.editor.pushUndoStop(); if (completion.snippetInfo) { this.editor.executeEdits( 'inlineSuggestion.accept', diff --git a/src/vs/editor/contrib/rename/browser/renameInputField.css b/src/vs/editor/contrib/rename/browser/renameInputField.css index 99757bf38a4..9000a713ec2 100644 --- a/src/vs/editor/contrib/rename/browser/renameInputField.css +++ b/src/vs/editor/contrib/rename/browser/renameInputField.css @@ -15,6 +15,7 @@ .monaco-editor .rename-box .rename-input { padding: 3px; width: calc(100% - 6px); + border-radius: 2px; } .monaco-editor .rename-box .rename-label { diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css index 78ce346f864..f3dc213f679 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css @@ -29,7 +29,7 @@ .monaco-editor .sticky-widget { width : 100%; - box-shadow : var(--vscode-scrollbar-shadow) 0 2px 6px -2px; + box-shadow : var(--vscode-scrollbar-shadow) 0 3px 2px -2px; z-index : 2; background-color : var(--vscode-editorStickyScroll-background); } diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollActions.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollActions.ts index a1c6cac5baf..e13331895c9 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollActions.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollActions.ts @@ -5,6 +5,7 @@ import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { localize } from 'vs/nls'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { Action2, MenuId } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -16,15 +17,19 @@ export class ToggleStickyScroll extends Action2 { id: 'editor.action.toggleStickyScroll', title: { value: localize('toggleStickyScroll', "Toggle Sticky Scroll"), - mnemonicTitle: localize('miStickyScroll', "&&Sticky Scroll"), + mnemonicTitle: localize('mitoggleStickyScroll', "&&Toggle Sticky Scroll"), original: 'Toggle Sticky Scroll', }, - // Hardcoding due to import violation - category: { value: localize('view', "View"), original: 'View' }, - toggled: ContextKeyExpr.equals('config.editor.stickyScroll.enabled', true), + category: Categories.View, + toggled: { + condition: ContextKeyExpr.equals('config.editor.stickyScroll.enabled', true), + title: localize('stickyScroll', "Sticky Scroll"), + mnemonicTitle: localize('miStickyScroll', "&&Sticky Scroll"), + }, menu: [ { id: MenuId.CommandPalette }, - { id: MenuId.MenubarViewMenu, group: '5_editor', order: 6 }, + { id: MenuId.MenubarEditorFeaturesMenu, order: 6 }, + { id: MenuId.StickyScrollContext } ] }); } diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts index 47fbfec59b9..a5dc15c6e41 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts @@ -12,6 +12,9 @@ import { StickyScrollWidget, StickyScrollWidgetState } from './stickyScrollWidge import { StickyLineCandidateProvider, StickyRange } from './stickyScrollProvider'; import { IModelTokensChangedEvent } from 'vs/editor/common/textModelEvents'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import * as dom from 'vs/base/browser/dom'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { MenuId } from 'vs/platform/actions/common/actions'; export class StickyScrollController extends Disposable implements IEditorContribution { @@ -26,6 +29,7 @@ export class StickyScrollController extends Disposable implements IEditorContrib _editor: ICodeEditor, @ILanguageFeaturesService _languageFeaturesService: ILanguageFeaturesService, @IInstantiationService _instaService: IInstantiationService, + @IContextMenuService private readonly _contextMenuService: IContextMenuService, ) { super(); this._editor = _editor; @@ -41,6 +45,9 @@ export class StickyScrollController extends Disposable implements IEditorContrib } })); this.readConfiguration(); + this._register(dom.addDisposableListener(this._stickyScrollWidget.getDomNode(), dom.EventType.CONTEXT_MENU, async (event: MouseEvent) => { + this.onContextMenu(event); + })); } public get stickyScrollCandidateProvider() { @@ -51,6 +58,13 @@ export class StickyScrollController extends Disposable implements IEditorContrib return this._widgetState; } + private onContextMenu(event: MouseEvent) { + this._contextMenuService.showContextMenu({ + menuId: MenuId.StickyScrollContext, + getAnchor: () => event, + }); + } + private readConfiguration() { const options = this._editor.getOption(EditorOption.stickyScroll); if (options.enabled === false) { diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts index 7846d007c17..d93642d2d4d 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts @@ -225,7 +225,7 @@ class StickyOutlineElement { outlineElements = provider.children; } else { let tempID = ''; - let maxTotalSumOfRanges = 0; + let maxTotalSumOfRanges = -1; let optimalOutlineGroup = undefined; for (const [_key, outlineGroup] of outlineModel.children.entries()) { const totalSumRanges = StickyOutlineElement.findSumOfRangesOfGroup(outlineGroup); diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index f35d88029b6..ed4f02e586c 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -152,7 +152,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { this._editor.revealPosition({ lineNumber: this._hoverOnLine, column: 1 }); } this._instaService.invokeFunction(goToDefinitionWithLocation, e, this._editor as IActiveCodeEditor, { uri: this._editor.getModel()!.uri, range: this._stickyRangeProjectedOnEditor } as Location); - } else { + } else if (!e.isRightClick) { // Normal click this._editor.revealPosition({ lineNumber: this._hoverOnLine, column: 1 }); } @@ -275,7 +275,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { const mouseOverEvent = new StandardMouseEvent(e); const text = mouseOverEvent.target.innerText; this._hoverOnLine = line; - // TODO: workaround to find the column index, perhaps need more solid solution + // TODO: workaround to find the column index, perhaps need a more solid solution this._hoverOnColumn = this._editor.getModel().getLineContent(line).indexOf(text) + 1 || -1; } })); @@ -296,8 +296,9 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { const minimapSide = this._editor.getOption(EditorOption.minimap).side; if (minimapSide === 'left') { this._rootDomNode.style.marginLeft = this._editor.getLayoutInfo().minimap.minimapCanvasOuterWidth + 'px'; - } else if (minimapSide === 'right') { - this._rootDomNode.style.marginLeft = '0px'; + } + else if (minimapSide === 'right') { + this._rootDomNode.style.marginLeft = '1px'; } this._rootDomNode.style.zIndex = '11'; } diff --git a/src/vs/editor/contrib/stickyScroll/test/browser/stickyScroll.test.ts b/src/vs/editor/contrib/stickyScroll/test/browser/stickyScroll.test.ts index 4f7e077bfbf..9e4e01fb994 100644 --- a/src/vs/editor/contrib/stickyScroll/test/browser/stickyScroll.test.ts +++ b/src/vs/editor/contrib/stickyScroll/test/browser/stickyScroll.test.ts @@ -13,12 +13,15 @@ import { DocumentSymbol, SymbolKind } from 'vs/editor/common/languages'; import { StickyLineCandidate, StickyLineCandidateProvider } from 'vs/editor/contrib/stickyScroll/browser/stickyScrollProvider'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { ILogService, NullLogService } from 'vs/platform/log/common/log'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { mock } from 'vs/base/test/common/mock'; suite('Sticky Scroll Tests', () => { const serviceCollection = new ServiceCollection( [ILanguageFeaturesService, new LanguageFeaturesService()], - [ILogService, new NullLogService()] + [ILogService, new NullLogService()], + [IContextMenuService, new class extends mock() { }] ); const text = [ diff --git a/src/vs/editor/contrib/suggest/browser/suggestCommitCharacters.ts b/src/vs/editor/contrib/suggest/browser/suggestCommitCharacters.ts index 1b89a6435a8..61080e43376 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestCommitCharacters.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestCommitCharacters.ts @@ -8,6 +8,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { CharacterSet } from 'vs/editor/common/core/characterClassifier'; +import { State, SuggestModel } from 'vs/editor/contrib/suggest/browser/suggestModel'; import { ISelectedSuggestion, SuggestWidget } from './suggestWidget'; export class CommitCharacterController { @@ -19,14 +20,23 @@ export class CommitCharacterController { readonly item: ISelectedSuggestion; }; - constructor(editor: ICodeEditor, widget: SuggestWidget, accept: (selected: ISelectedSuggestion) => any) { + constructor(editor: ICodeEditor, widget: SuggestWidget, model: SuggestModel, accept: (selected: ISelectedSuggestion) => any) { + + this._disposables.add(model.onDidSuggest(e => { + if (e.completionModel.items.length === 0) { + this.reset(); + } + })); + this._disposables.add(model.onDidCancel(e => { + this.reset(); + })); this._disposables.add(widget.onDidShow(() => this._onItem(widget.getFocusedItem()))); this._disposables.add(widget.onDidFocus(this._onItem, this)); this._disposables.add(widget.onDidHide(this.reset, this)); this._disposables.add(editor.onWillType(text => { - if (this._active && !widget.isFrozen()) { + if (this._active && !widget.isFrozen() && model.state !== State.Idle) { const ch = text.charCodeAt(text.length - 1); if (this._active.acceptCharacters.has(ch) && editor.getOption(EditorOption.acceptSuggestionOnCommitCharacter)) { accept(this._active.item); diff --git a/src/vs/editor/contrib/suggest/browser/suggestController.ts b/src/vs/editor/contrib/suggest/browser/suggestController.ts index 14045fe6bec..21a4e1ff60d 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestController.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestController.ts @@ -47,8 +47,9 @@ import { basename, extname } from 'vs/base/common/resources'; import { hash } from 'vs/base/common/hash'; // sticky suggest widget which doesn't disappear on focus out and such -const _sticky = false; -// _sticky = Boolean("true"); // done "weirdly" so that a lint warning prevents you from pushing this +const _sticky = false + // || Boolean("true") // done "weirdly" so that a lint warning prevents you from pushing this + ; class LineSuffix { @@ -142,13 +143,9 @@ export class SuggestController implements IEditorContribution { this._toDispose.add(widget.onDidSelect(item => this._insertSuggestion(item, 0), this)); // Wire up logic to accept a suggestion on certain characters - const commitCharacterController = new CommitCharacterController(this.editor, widget, item => this._insertSuggestion(item, InsertFlags.NoAfterUndoStop)); + const commitCharacterController = new CommitCharacterController(this.editor, widget, this.model, item => this._insertSuggestion(item, InsertFlags.NoAfterUndoStop)); this._toDispose.add(commitCharacterController); - this._toDispose.add(this.model.onDidSuggest(e => { - if (e.completionModel.items.length === 0) { - commitCharacterController.reset(); - } - })); + // Wire up makes text edit context key const ctxMakesTextEdit = SuggestContext.MakesTextEdit.bindTo(this._contextKeyService); diff --git a/src/vs/editor/contrib/suggest/browser/suggestMemory.ts b/src/vs/editor/contrib/suggest/browser/suggestMemory.ts index 8b2868cbde9..dce5326c6da 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestMemory.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestMemory.ts @@ -12,7 +12,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { CompletionItemKind, CompletionItemKinds } from 'vs/editor/common/languages'; import { CompletionItem } from 'vs/editor/contrib/suggest/browser/suggest'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService, StorageScope, StorageTarget, WillSaveStateReason } from 'vs/platform/storage/common/storage'; @@ -306,4 +306,4 @@ export interface ISuggestMemoryService { select(model: ITextModel, pos: IPosition, items: CompletionItem[]): number; } -registerSingleton(ISuggestMemoryService, SuggestMemoryService, true); +registerSingleton(ISuggestMemoryService, SuggestMemoryService, InstantiationType.Delayed); diff --git a/src/vs/editor/contrib/suggest/browser/suggestModel.ts b/src/vs/editor/contrib/suggest/browser/suggestModel.ts index b31ba921faf..e1c6778e15f 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestModel.ts @@ -29,6 +29,7 @@ import { CompletionDurations, CompletionItem, CompletionOptions, getSnippetSugge import { IWordAtPosition } from 'vs/editor/common/core/wordHelper'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { FuzzyScoreOptions } from 'vs/base/common/filters'; +import { assertType } from 'vs/base/common/types'; export interface ICancelEvent { readonly retrigger: boolean; @@ -424,23 +425,12 @@ export class SuggestModel implements IDisposable { } private _refilterCompletionItems(): void { - // Re-filter suggestions. This MUST run async because filtering/scoring - // uses the model content AND the cursor position. The latter is NOT - // updated when the document has changed (the event which drives this method) - // and therefore a little pause (next mirco task) is needed. See: - // https://stackoverflow.com/questions/25915634/difference-between-microtask-and-macrotask-within-an-event-loop-context#25933985 - Promise.resolve().then(() => { - if (this._state === State.Idle) { - return; - } - if (!this._editor.hasModel()) { - return; - } - const model = this._editor.getModel(); - const position = this._editor.getPosition(); - const ctx = new LineContext(model, position, this._state === State.Auto, false, false); - this._onNewContext(ctx); - }); + assertType(this._editor.hasModel()); + + const model = this._editor.getModel(); + const position = this._editor.getPosition(); + const ctx = new LineContext(model, position, this._state === State.Auto, false, false); + this._onNewContext(ctx); } trigger(context: SuggestTriggerContext, retrigger: boolean = false, onlyFrom?: Set, existing?: { items: CompletionItem[]; clipboardText: string | undefined }, noFilter?: boolean): void { diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts index 08cf6fe22a0..f2df600f279 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts @@ -10,7 +10,7 @@ import { IListEvent, IListGestureEvent, IListMouseEvent } from 'vs/base/browser/ import { List } from 'vs/base/browser/ui/list/listWidget'; import { CancelablePromise, createCancelablePromise, disposableTimeout, TimeoutTimer } from 'vs/base/common/async'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { Emitter, Event } from 'vs/base/common/event'; +import { Emitter, Event, PauseableEmitter } from 'vs/base/common/event'; import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { clamp } from 'vs/base/common/numbers'; import * as strings from 'vs/base/common/strings'; @@ -130,8 +130,8 @@ export class SuggestWidget implements IDisposable { private readonly _disposables = new DisposableStore(); - private readonly _onDidSelect = new Emitter(); - private readonly _onDidFocus = new Emitter(); + private readonly _onDidSelect = new PauseableEmitter(); + private readonly _onDidFocus = new PauseableEmitter(); private readonly _onDidHide = new Emitter(); private readonly _onDidShow = new Emitter(); @@ -540,11 +540,23 @@ export class SuggestWidget implements IDisposable { } this._focusedItem = undefined; - this._list.splice(0, this._list.length, this._completionModel.items); - this._setState(isFrozen ? State.Frozen : State.Open); - if (selectionIndex >= 0) { - this._list.reveal(selectionIndex, 0); - this._list.setFocus([selectionIndex]); + + // calling list.splice triggers focus event which this widget forwards. That can lead to + // suggestions being cancelled and the widget being cleared (and hidden). All this happens + // before revealing and focusing is done which means revealing and focusing will fail when + // they get run. + this._onDidFocus.pause(); + this._onDidSelect.pause(); + try { + this._list.splice(0, this._list.length, this._completionModel.items); + this._setState(isFrozen ? State.Frozen : State.Open); + if (selectionIndex >= 0) { + this._list.reveal(selectionIndex, 0); + this._list.setFocus([selectionIndex]); + } + } finally { + this._onDidFocus.resume(); + this._onDidSelect.resume(); } this._layout(this.element.size); diff --git a/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts b/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts index cfde37e9e3d..fce75a30b85 100644 --- a/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts @@ -462,4 +462,106 @@ suite('SuggestController', function () { assert.strictEqual(editor.getValue(), 'import "my_class.txt";\nMyClassName'); }); + + test('Pressing enter on autocomplete should always apply the selected dropdown completion, not a different, hidden one #161883', async function () { + disposables.add(languageFeaturesService.completionProvider.register({ scheme: 'test-ctrl' }, { + provideCompletionItems(doc, pos) { + + const word = doc.getWordUntilPosition(pos); + const range = new Range(pos.lineNumber, word.startColumn, pos.lineNumber, word.endColumn); + + return { + suggestions: [{ + kind: CompletionItemKind.Text, + label: 'filterBankSize', + insertText: 'filterBankSize', + sortText: 'a', + range + }, { + kind: CompletionItemKind.Text, + label: 'filter', + insertText: 'filter', + sortText: 'b', + range + }] + }; + } + })); + + editor.setValue('filte'); + editor.setSelection(new Selection(1, 6, 1, 6)); + + const p1 = Event.toPromise(controller.model.onDidSuggest); + controller.triggerSuggest(); + + const { completionModel } = await p1; + assert.strictEqual(completionModel.items.length, 2); + + const [first, second] = completionModel.items; + assert.strictEqual(first.textLabel, 'filterBankSize'); + assert.strictEqual(second.textLabel, 'filter'); + + assert.deepStrictEqual(editor.getSelection(), new Selection(1, 6, 1, 6)); + editor.trigger('keyboard', 'type', { text: 'r' }); // now filter "overtakes" filterBankSize because it is fully matched + assert.deepStrictEqual(editor.getSelection(), new Selection(1, 7, 1, 7)); + + controller.acceptSelectedSuggestion(false, false); + assert.strictEqual(editor.getValue(), 'filter'); + }); + + test('Fast autocomple typing selects the previous autocomplete suggestion, #71795', async function () { + disposables.add(languageFeaturesService.completionProvider.register({ scheme: 'test-ctrl' }, { + provideCompletionItems(doc, pos) { + + const word = doc.getWordUntilPosition(pos); + const range = new Range(pos.lineNumber, word.startColumn, pos.lineNumber, word.endColumn); + + return { + suggestions: [{ + kind: CompletionItemKind.Text, + label: 'false', + insertText: 'false', + range + }, { + kind: CompletionItemKind.Text, + label: 'float', + insertText: 'float', + range + }, { + kind: CompletionItemKind.Text, + label: 'for', + insertText: 'for', + range + }, { + kind: CompletionItemKind.Text, + label: 'foreach', + insertText: 'foreach', + range + }] + }; + } + })); + + editor.setValue('f'); + editor.setSelection(new Selection(1, 2, 1, 2)); + + const p1 = Event.toPromise(controller.model.onDidSuggest); + controller.triggerSuggest(); + + const { completionModel } = await p1; + assert.strictEqual(completionModel.items.length, 4); + + const [first, second, third, fourth] = completionModel.items; + assert.strictEqual(first.textLabel, 'false'); + assert.strictEqual(second.textLabel, 'float'); + assert.strictEqual(third.textLabel, 'for'); + assert.strictEqual(fourth.textLabel, 'foreach'); + + assert.deepStrictEqual(editor.getSelection(), new Selection(1, 2, 1, 2)); + editor.trigger('keyboard', 'type', { text: 'o' }); // filters`false` and `float` + assert.deepStrictEqual(editor.getSelection(), new Selection(1, 3, 1, 3)); + + controller.acceptSelectedSuggestion(false, false); + assert.strictEqual(editor.getValue(), 'for'); + }); }); diff --git a/src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts b/src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts index 0a1b3dda2de..d60987964ca 100644 --- a/src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts @@ -407,9 +407,9 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { model.setValue(''); - return withOracle((model, editor) => { + return withOracle(async (model, editor) => { - return assertEvent(model.onDidSuggest, () => { + await assertEvent(model.onDidSuggest, () => { editor.setPosition({ lineNumber: 1, column: 1 }); editor.trigger('keyboard', Handler.Type, { text: 'foo' }); @@ -419,16 +419,29 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { const [first] = event.completionModel.items; assert.strictEqual(first.completion.label, 'foo.bar'); - return assertEvent(model.onDidSuggest, () => { - editor.trigger('keyboard', Handler.Type, { text: '.' }); + }); - }, event => { - assert.strictEqual(event.auto, true); - assert.strictEqual(event.completionModel.items.length, 2); - const [first, second] = event.completionModel.items; - assert.strictEqual(first.completion.label, 'foo.bar'); - assert.strictEqual(second.completion.label, 'boom'); - }); + await assertEvent(model.onDidSuggest, () => { + editor.trigger('keyboard', Handler.Type, { text: '.' }); + + }, event => { + // SYNC + assert.strictEqual(event.auto, true); + assert.strictEqual(event.completionModel.items.length, 1); + const [first] = event.completionModel.items; + assert.strictEqual(first.completion.label, 'foo.bar'); + }); + + await assertEvent(model.onDidSuggest, () => { + // nothing -> triggered by the trigger character typing (see above) + + }, event => { + // ASYNC + assert.strictEqual(event.auto, true); + assert.strictEqual(event.completionModel.items.length, 2); + const [first, second] = event.completionModel.items; + assert.strictEqual(first.completion.label, 'foo.bar'); + assert.strictEqual(second.completion.label, 'boom'); }); }); }); diff --git a/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts b/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts index b9f16e4c68a..bd6d9cd9b84 100644 --- a/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts +++ b/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts @@ -483,7 +483,7 @@ export class UnicodeHighlighterHoverParticipant implements IEditorHoverParticipa .appendMarkdown(reason) .appendText(' ') .appendLink(uri, adjustSettings); - result.push(new MarkdownHover(this, d.range, [markdown], index++)); + result.push(new MarkdownHover(this, d.range, [markdown], false, index++)); } return result; } diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts index 614714310e0..8e9a237c5b3 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts @@ -372,9 +372,9 @@ export class StandaloneCodeEditor extends CodeEditorWidget implements IStandalon ); // Store it under the original id, such that trigger with the original id will work - this._actions[id] = internalAction; + this._actions.set(id, internalAction); toDispose.add(toDisposable(() => { - delete this._actions[id]; + this._actions.delete(id); })); return toDispose; diff --git a/src/vs/editor/standalone/browser/standaloneLayoutService.ts b/src/vs/editor/standalone/browser/standaloneLayoutService.ts index aba9ec78509..42b7b0786a3 100644 --- a/src/vs/editor/standalone/browser/standaloneLayoutService.ts +++ b/src/vs/editor/standalone/browser/standaloneLayoutService.ts @@ -7,7 +7,7 @@ import * as dom from 'vs/base/browser/dom'; import { Event } from 'vs/base/common/event'; import { ILayoutService, ILayoutOffsetInfo } from 'vs/platform/layout/browser/layoutService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; class StandaloneLayoutService implements ILayoutService { declare readonly _serviceBrand: undefined; @@ -63,4 +63,4 @@ export class EditorScopedLayoutService extends StandaloneLayoutService { } } -registerSingleton(ILayoutService, StandaloneLayoutService, true); +registerSingleton(ILayoutService, StandaloneLayoutService, InstantiationType.Delayed); diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index c25434b6c38..5da75b817a5 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -817,7 +817,8 @@ class StandaloneBulkEditService implements IBulkEditService { } return { - ariaSummary: strings.format(StandaloneServicesNLS.bulkEditServiceSummary, totalEdits, totalFiles) + ariaSummary: strings.format(StandaloneServicesNLS.bulkEditServiceSummary, totalEdits, totalFiles), + isApplied: totalEdits > 0 }; } } @@ -948,9 +949,11 @@ class StandaloneContextMenuService extends ContextMenuService { @INotificationService notificationService: INotificationService, @IContextViewService contextViewService: IContextViewService, @IKeybindingService keybindingService: IKeybindingService, - @IThemeService themeService: IThemeService + @IThemeService themeService: IThemeService, + @IMenuService menuService: IMenuService, + @IContextKeyService contextKeyService: IContextKeyService, ) { - super(telemetryService, notificationService, contextViewService, keybindingService, themeService); + super(telemetryService, notificationService, contextViewService, keybindingService, themeService, menuService, contextKeyService); this.configure({ blockMouse: false }); // we do not want that in the standalone editor } } diff --git a/src/vs/editor/test/browser/testCodeEditor.ts b/src/vs/editor/test/browser/testCodeEditor.ts index bdcc66b5f1e..9278f28c685 100644 --- a/src/vs/editor/test/browser/testCodeEditor.ts +++ b/src/vs/editor/test/browser/testCodeEditor.ts @@ -86,7 +86,7 @@ export class TestCodeEditor extends CodeEditorWidget implements ICodeEditor { } public registerAndInstantiateContribution(id: string, ctor: new (editor: ICodeEditor, ...services: BrandedService[]) => T): T { const r: T = this._instantiationService.createInstance(ctor, this); - this._contributions[id] = r; + this._contributions.set(id, r); return r; } public registerDisposable(disposable: IDisposable): void { @@ -133,7 +133,7 @@ function isTextModel(arg: ITextModel | string | string[] | ITextBufferFactory): function _withTestCodeEditor(arg: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: TestInstantiationService) => void): void; function _withTestCodeEditor(arg: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: TestInstantiationService) => Promise): Promise; -async function _withTestCodeEditor(arg: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: TestInstantiationService) => Promise | void): Promise | void> { +function _withTestCodeEditor(arg: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: TestInstantiationService) => Promise | void): Promise | void { const disposables = new DisposableStore(); const instantiationService = createCodeEditorServices(disposables, options.serviceCollection); delete options.serviceCollection; @@ -149,12 +149,12 @@ async function _withTestCodeEditor(arg: ITextModel | string | string[] | ITextBu const editor = disposables.add(instantiateTestCodeEditor(instantiationService, model, options)); const viewModel = editor.getViewModel()!; viewModel.setHasFocus(true); - try { - const result = callback(editor, editor.getViewModel()!, instantiationService); - await result; - } finally { - disposables.dispose(); + const result = callback(editor, editor.getViewModel()!, instantiationService); + if (result) { + return result.then(() => disposables.dispose()); } + + disposables.dispose(); } export function createCodeEditorServices(disposables: DisposableStore, services: ServiceCollection = new ServiceCollection()): TestInstantiationService { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 5c9c1a1a25c..555ee5b0659 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -1488,6 +1488,11 @@ declare namespace monaco.editor { */ className?: string | null; blockClassName?: string | null; + /** + * Indicates if this block should be rendered after the last line. + * In this case, the range must be empty and set to the last line. + */ + blockIsAfterEnd?: boolean | null; /** * Message to be rendered when hovering over the glyph margin decoration. */ @@ -5342,7 +5347,7 @@ declare namespace monaco.editor { * @id Unique identifier of the contribution. * @return The action or null if action not found. */ - getAction(id: string): IEditorAction; + getAction(id: string): IEditorAction | null; /** * Execute a command on the editor. * The edits will land on the undo-redo stack, but no "undo stop" will be pushed. @@ -5504,6 +5509,11 @@ declare namespace monaco.editor { * @event */ readonly onDidUpdateDiff: IEvent; + /** + * An event emitted when the diff model is changed (i.e. the diff editor shows new content). + * @event + */ + readonly onDidChangeModel: IEvent; /** * Saves current view state of the editor in a serializable object. */ diff --git a/src/vs/platform/action/common/action.ts b/src/vs/platform/action/common/action.ts index 4b3f1b5f0ff..38d336ea603 100644 --- a/src/vs/platform/action/common/action.ts +++ b/src/vs/platform/action/common/action.ts @@ -6,6 +6,7 @@ import { URI, UriDto } from 'vs/base/common/uri'; import { ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { Categories } from './actionCommonCategories'; export interface ILocalizedString { @@ -30,16 +31,50 @@ export interface ICommandActionTitle extends ILocalizedString { export type Icon = { dark?: URI; light?: URI } | ThemeIcon; +export interface ICommandActionToggleInfo { + + /** + * The condition that marks the action as toggled. + */ + condition: ContextKeyExpression; + + icon?: Icon; + + tooltip?: string; + + /** + * The title that goes well with a a check mark, e.g "(check) Line Numbers" vs "Toggle Line Numbers" + */ + title?: string; + + /** + * Like title but with a mnemonic designation. + */ + mnemonicTitle?: string; +} + +export function isICommandActionToggleInfo(thing: ContextKeyExpression | ICommandActionToggleInfo | undefined): thing is ICommandActionToggleInfo { + return thing ? (thing).condition !== undefined : false; +} + export interface ICommandAction { id: string; title: string | ICommandActionTitle; shortTitle?: string | ICommandActionTitle; - category?: string | ILocalizedString; + category?: keyof typeof Categories | ILocalizedString | string; tooltip?: string | ILocalizedString; icon?: Icon; source?: string; precondition?: ContextKeyExpression; - toggled?: ContextKeyExpression | { condition: ContextKeyExpression; icon?: Icon; tooltip?: string; title?: string | ILocalizedString }; + + /** + * The action is a toggle action. Define the context key expression that reflects its toggle-state + * or define toggle-info including an icon and a title that goes well with a checkmark. + */ + toggled?: ContextKeyExpression | ICommandActionToggleInfo; + + /** @deprecated see https://github.com/microsoft/vscode/issues/162004 */ + _isFakeAction?: true; } export type ISerializableCommandAction = UriDto; diff --git a/src/vs/platform/action/common/actionCommonCategories.ts b/src/vs/platform/action/common/actionCommonCategories.ts new file mode 100644 index 00000000000..5a62d7df897 --- /dev/null +++ b/src/vs/platform/action/common/actionCommonCategories.ts @@ -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. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; + +export const Categories = Object.freeze({ + View: { value: localize('view', "View"), original: 'View' }, + Help: { value: localize('help', "Help"), original: 'Help' }, + Test: { value: localize('test', "Test"), original: 'Test' }, + Preferences: { value: localize('preferences', "Preferences"), original: 'Preferences' }, + Developer: { value: localize({ key: 'developer', comment: ['A developer on Code itself or someone diagnosing issues in Code'] }, "Developer"), original: 'Developer' } +}); diff --git a/src/vs/platform/actions/browser/menuEntryActionViewItem.css b/src/vs/platform/actions/browser/menuEntryActionViewItem.css index 31eb20ba8a8..5f0134dfeae 100644 --- a/src/vs/platform/actions/browser/menuEntryActionViewItem.css +++ b/src/vs/platform/actions/browser/menuEntryActionViewItem.css @@ -11,27 +11,29 @@ background-size: 16px; } - @keyframes shift { 0% { - transform: scale(1); + transform: translateX(0px); } - 15%{ - transform: scale(1.1); + 33%{ + transform: translateX(0.5px); } - 100% { - transform: scale(1); + 66% { + transform: translateX(-0.5px); } } .monaco-toolbar.config .monaco-action-bar .action-item { animation-duration: 1.2s; - animation-iteration-count: 1; + animation-iteration-count: infinite; animation-name: shift; } +.monaco-toolbar.config .monaco-action-bar .action-item:nth-child(odd) { + animation-delay: 0.6s; +} .monaco-dropdown-with-default { display: flex !important; diff --git a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts index 76c445d5603..bac16a59fc4 100644 --- a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts +++ b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts @@ -5,7 +5,7 @@ import { $, addDisposableListener, append, asCSSUrl, EventType, ModifierKeyEmitter, prepend } from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { ActionViewItem, BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { ActionViewItem, BaseActionViewItem, SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { DropdownMenuActionViewItem, IDropdownMenuActionViewItemOptions } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; import { ActionRunner, IAction, IRunEvent, Separator, SubmenuAction } from 'vs/base/common/actions'; import { Event } from 'vs/base/common/event'; @@ -16,9 +16,9 @@ import { isLinux, isWindows, OS } from 'vs/base/common/platform'; import 'vs/css!./menuEntryActionViewItem'; import { localize } from 'vs/nls'; import { IMenu, IMenuActionOptions, IMenuService, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions'; -import { ICommandAction, Icon } from 'vs/platform/action/common/action'; +import { ICommandAction, isICommandActionToggleInfo } from 'vs/platform/action/common/action'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -27,6 +27,8 @@ import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService' import { isDark } from 'vs/platform/theme/common/theme'; import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; import { assertType } from 'vs/base/common/types'; +import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; +import { selectBorder } from 'vs/platform/theme/common/colorRegistry'; export function createAndFillInContextMenuActions(menu: IMenu, options: IMenuActionOptions | undefined, target: IAction[] | { primary: IAction[]; secondary: IAction[] }, primaryGroup?: string): void { const groups = menu.getActions(options); @@ -240,7 +242,7 @@ export class MenuEntryActionViewItem extends ActionViewItem { return; } - const icon = this._commandAction.checked && (item.toggled as { icon?: Icon })?.icon ? (item.toggled as { icon: Icon }).icon : item.icon; + const icon = this._commandAction.checked && isICommandActionToggleInfo(item.toggled) && item.toggled.icon ? item.toggled.icon : item.icon; if (!icon) { return; @@ -468,6 +470,37 @@ export class DropdownWithDefaultActionViewItem extends BaseActionViewItem { } } +class SubmenuEntrySelectActionViewItem extends SelectActionViewItem { + + constructor( + action: SubmenuItemAction, + @IThemeService private readonly themeService: IThemeService, + @IContextViewService contextViewService: IContextViewService + ) { + super(null, action, action.actions.map(a => ({ + text: a.id === Separator.ID ? '\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' : a.label, + isDisabled: !a.enabled, + })), 0, contextViewService, { ariaLabel: action.tooltip, optionsAsChildren: true }); + this._register(attachSelectBoxStyler(this.selectBox, themeService)); + this.select(Math.max(0, action.actions.findIndex(a => a.checked))); + } + + override render(container: HTMLElement): void { + super.render(container); + this._register(attachStylerCallback(this.themeService, { selectBorder }, colors => { + container.style.borderColor = colors.selectBorder ? `${colors.selectBorder}` : ''; + })); + } + + protected override runAction(option: string, index: number): void { + const action = (this.action as SubmenuItemAction).actions[index]; + if (action) { + this.actionRunner.run(action); + } + } + +} + /** * Creates action view items for menu actions or submenu actions. */ @@ -475,10 +508,14 @@ export function createActionViewItem(instaService: IInstantiationService, action if (action instanceof MenuItemAction) { return instaService.createInstance(MenuEntryActionViewItem, action, options); } else if (action instanceof SubmenuItemAction) { - if (action.item.rememberDefaultAction) { - return instaService.createInstance(DropdownWithDefaultActionViewItem, action, options); + if (action.item.isSelection) { + return instaService.createInstance(SubmenuEntrySelectActionViewItem, action); } else { - return instaService.createInstance(SubmenuEntryActionViewItem, action, options); + if (action.item.rememberDefaultAction) { + return instaService.createInstance(DropdownWithDefaultActionViewItem, action, options); + } else { + return instaService.createInstance(SubmenuEntryActionViewItem, action, options); + } } } else { return undefined; diff --git a/src/vs/platform/actions/browser/toolbar.ts b/src/vs/platform/actions/browser/toolbar.ts index be59a56d5a3..397a14c5f4a 100644 --- a/src/vs/platform/actions/browser/toolbar.ts +++ b/src/vs/platform/actions/browser/toolbar.ts @@ -10,7 +10,7 @@ import { coalesceInPlace } from 'vs/base/common/arrays'; import { BugIndicatingError } from 'vs/base/common/errors'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; -import { createAndFillInActionBarActions, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenuActionOptions, IMenuService, MenuId, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -18,8 +18,12 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; export const enum HiddenItemStrategy { + /** This toolbar doesn't support hiding*/ + NoHide = -1, + /** Hidden items aren't shown anywhere */ Ignore = 0, - RenderInSecondaryGroup = 1 + /** Hidden items move into the secondary group */ + RenderInSecondaryGroup = 1, } export type IWorkbenchToolBarOptions = IToolBarOptions & { @@ -96,7 +100,7 @@ export class WorkbenchToolBar extends ToolBar { } } - override setActions(_primary: readonly IAction[], _secondary: readonly IAction[] = []): void { + override setActions(_primary: readonly IAction[], _secondary: readonly IAction[] = [], menuIds?: readonly MenuId[]): void { this._sessionDisposables.clear(); const primary = _primary.slice(); @@ -104,32 +108,33 @@ export class WorkbenchToolBar extends ToolBar { const toggleActions: IAction[] = []; let someAreHidden = false; + // unless disabled, move all hidden items to secondary group or ignore them + if (this._options?.hiddenItemStrategy !== HiddenItemStrategy.NoHide) { + let shouldPrependSeparator = secondary.length > 0; + for (let i = 0; i < primary.length; i++) { + const action = primary[i]; + if (!(action instanceof MenuItemAction) && !(action instanceof SubmenuItemAction)) { + // console.warn(`Action ${action.id}/${action.label} is not a MenuItemAction`); + continue; + } + if (!action.hideActions) { + continue; + } - // move all hidden items to secondary group or ignore them - let shouldPrependSeparator = secondary.length > 0; - for (let i = 0; i < primary.length; i++) { - const action = primary[i]; - if (!(action instanceof MenuItemAction) && !(action instanceof SubmenuItemAction)) { - // console.warn(`Action ${action.id}/${action.label} is not a MenuItemAction`); - continue; - } - if (!action.hideActions) { - continue; - } + // collect all toggle actions + toggleActions.push(action.hideActions.toggle); - // collect all toggle actions - toggleActions.push(action.hideActions.toggle); - - // hidden items move into overflow or ignore - if (action.hideActions.isHidden) { - someAreHidden = true; - primary[i] = undefined!; - if (this._options?.hiddenItemStrategy !== HiddenItemStrategy.Ignore) { - if (shouldPrependSeparator) { - shouldPrependSeparator = false; - secondary.unshift(new Separator()); + // hidden items move into overflow or ignore + if (action.hideActions.isHidden) { + someAreHidden = true; + primary[i] = undefined!; + if (this._options?.hiddenItemStrategy !== HiddenItemStrategy.Ignore) { + if (shouldPrependSeparator) { + shouldPrependSeparator = false; + secondary.unshift(new Separator()); + } + secondary.unshift(action); } - secondary.unshift(action); } } } @@ -151,8 +156,14 @@ export class WorkbenchToolBar extends ToolBar { // add "hide foo" actions let hideAction: IAction; - if ((action instanceof MenuItemAction || action instanceof SubmenuItemAction) && action.hideActions) { + if (action instanceof MenuItemAction || action instanceof SubmenuItemAction) { + if (!action.hideActions) { + // no context menu for MenuItemAction instances that support no hiding + // those are fake actions and need to be cleaned up + return; + } hideAction = action.hideActions.hide; + } else { hideAction = toAction({ id: 'label', @@ -164,32 +175,27 @@ export class WorkbenchToolBar extends ToolBar { actions = [hideAction, new Separator(), ...toggleActions]; // add "Reset Menu" action - if (someAreHidden && this._options?.resetMenu) { + if (this._options?.resetMenu && !menuIds) { + menuIds = [this._options.resetMenu]; + } + if (someAreHidden && menuIds) { actions.push(new Separator()); actions.push(toAction({ id: 'resetThisMenu', label: localize('resetThisMenu', "Reset Menu"), - run: () => this._menuService.resetHiddenStates(this._options!.resetMenu) + run: () => this._menuService.resetHiddenStates(menuIds) })); } - // add context menu actions (iff appicable) - if (this._options?.contextMenu) { - const menu = this._menuService.createMenu(this._options.contextMenu, this._contextKeyService); - const contextMenuActions: IAction[] = []; - createAndFillInContextMenuActions(menu, { ...this._options?.menuOptions, renderShortTitle: true, }, contextMenuActions); - menu.dispose(); - - if (contextMenuActions.length > 0) { - actions = [...actions, new Separator(), ...contextMenuActions]; - } - } - - this.getElement().classList.toggle('config', true); + // this.getElement().classList.toggle('config', true); this._contextMenuService.showContextMenu({ getAnchor: () => e, getActions: () => actions, + // add context menu actions (iff appicable) + menuId: this._options?.contextMenu, + menuActionOptions: { renderShortTitle: true, ...this._options?.menuOptions }, + contextKeyService: this._contextKeyService, onHide: () => this.getElement().classList.toggle('config', false), }); })); @@ -258,7 +264,7 @@ export class MenuWorkbenchToolBar extends WorkbenchToolBar { super(container, { resetMenu: menuId, ...options }, menuService, contextKeyService, contextMenuService, keybindingService, telemetryService); // update logic - const menu = this._store.add(menuService.createMenu(menuId, contextKeyService)); + const menu = this._store.add(menuService.createMenu(menuId, contextKeyService, { emitEventsForSubmenuChanges: true })); const updateToolbar = () => { const primary: IAction[] = []; const secondary: IAction[] = []; diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index e32071c164c..c09a7e422a3 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -9,6 +9,7 @@ import { Event, MicrotaskEmitter } from 'vs/base/common/event'; import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { LinkedList } from 'vs/base/common/linkedList'; import { ICommandAction, ICommandActionTitle, Icon, ILocalizedString } from 'vs/platform/action/common/action'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { CommandsRegistry, ICommandHandlerDescription, ICommandService } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr, ContextKeyExpression, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { SyncDescriptor, SyncDescriptor0 } from 'vs/platform/instantiation/common/descriptors'; @@ -31,6 +32,7 @@ export interface ISubmenuItem { when?: ContextKeyExpression; group?: 'navigation' | string; order?: number; + isSelection?: boolean; rememberDefaultAction?: boolean; // for dropdown menu: if true the last executed action is remembered as the default action } @@ -80,6 +82,7 @@ export class MenuId { static readonly MenubarGoMenu = new MenuId('MenubarGoMenu'); static readonly MenubarHelpMenu = new MenuId('MenubarHelpMenu'); static readonly MenubarLayoutMenu = new MenuId('MenubarLayoutMenu'); + static readonly MenubarEditorFeaturesMenu = new MenuId('MenubarEditorFeaturesMenu'); static readonly MenubarNewBreakpointMenu = new MenuId('MenubarNewBreakpointMenu'); static readonly MenubarPanelAlignmentMenu = new MenuId('MenubarPanelAlignmentMenu'); static readonly MenubarPanelPositionMenu = new MenuId('MenubarPanelPositionMenu'); @@ -103,6 +106,7 @@ export class MenuId { static readonly SearchContext = new MenuId('SearchContext'); static readonly StatusBarWindowIndicatorMenu = new MenuId('StatusBarWindowIndicatorMenu'); static readonly StatusBarRemoteIndicatorMenu = new MenuId('StatusBarRemoteIndicatorMenu'); + static readonly StickyScrollContext = new MenuId('StickyScrollContext'); static readonly TestItem = new MenuId('TestItem'); static readonly TestItemGutter = new MenuId('TestItemGutter'); static readonly TestPeekElement = new MenuId('TestPeekElement'); @@ -238,7 +242,7 @@ export interface IMenuService { /** * Reset the menu's hidden states. */ - resetHiddenStates(menuId: MenuId | undefined): void; + resetHiddenStates(menuIds: readonly MenuId[] | undefined): void; } export type ICommandsMap = Map; @@ -397,6 +401,12 @@ export interface IMenuItemHide { // subscribes to events of Action or modified properties export class MenuItemAction implements IAction { + static label(action: ICommandAction, options?: IMenuActionOptions): string { + return options?.renderShortTitle && action.shortTitle + ? (typeof action.shortTitle === 'string' ? action.shortTitle : action.shortTitle.value) + : (typeof action.title === 'string' ? action.title : action.title.value); + } + readonly item: ICommandAction; readonly alt: MenuItemAction | undefined; @@ -418,9 +428,7 @@ export class MenuItemAction implements IAction { @ICommandService private _commandService: ICommandService ) { this.id = item.id; - this.label = options?.renderShortTitle && item.shortTitle - ? (typeof item.shortTitle === 'string' ? item.shortTitle : item.shortTitle.value) - : (typeof item.title === 'string' ? item.title : item.title.value); + this.label = MenuItemAction.label(item, options); this.tooltip = (typeof item.tooltip === 'string' ? item.tooltip : item.tooltip?.value) ?? ''; this.enabled = !item.precondition || contextKeyService.contextMatchesRules(item.precondition); this.checked = undefined; @@ -532,13 +540,7 @@ export class SyncActionDescriptor { type OneOrN = T | T[]; -export interface IAction2Options extends ICommandAction { - - /** - * Shorthand to add this command to the command palette - */ - f1?: boolean; - +interface IAction2CommonOptions extends ICommandAction { /** * One or many menu items. */ @@ -554,6 +556,53 @@ export interface IAction2Options extends ICommandAction { * showing keybindings that have no other UX. */ description?: ICommandHandlerDescription; + + /** + * @deprecated workaround added for https://github.com/microsoft/vscode/issues/162004 + * This action doesn't do anything is just a workaround for rendering "something" + * inside a specific toolbar + */ + _isFakeAction?: true; +} + +interface IBaseAction2Options extends IAction2CommonOptions { + + /** + * This type is used when an action is not going to show up in the command palette. + * In that case, it's able to use a string for the `title` and `category` properties. + */ + f1?: false; +} + +interface ICommandPaletteOptions extends IAction2CommonOptions { + + /** + * The title of the command that will be displayed in the command palette after the category. + * This overrides {@link ICommandAction.title} to ensure a string isn't used so that the title + * includes the localized value and the original value for users using language packs. + */ + title: ICommandActionTitle; + + /** + * The category of the command that will be displayed in the command palette before the title suffixed. + * with a colon This overrides {@link ICommandAction.title} to ensure a string isn't used so that + * the title includes the localized value and the original value for users using language packs. + */ + category?: keyof typeof Categories | ILocalizedString; + + /** + * Shorthand to add this command to the command palette. Note: this is not the only way to declare that + * a command should be in the command palette... however, enforcing ILocalizedString in the other scenarios + * is much more challenging and this gets us most of the way there. + */ + f1: true; +} + +export type IAction2Options = ICommandPaletteOptions | IBaseAction2Options; + +export interface IAction2F1RequiredOptions { + title: ICommandActionTitle; + category?: keyof typeof Categories | ILocalizedString; } export abstract class Action2 { diff --git a/src/vs/platform/actions/common/menuResetAction.ts b/src/vs/platform/actions/common/menuResetAction.ts index 84ee76e2b91..6cebce22257 100644 --- a/src/vs/platform/actions/common/menuResetAction.ts +++ b/src/vs/platform/actions/common/menuResetAction.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { Action2, IMenuService } from 'vs/platform/actions/common/actions'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; @@ -14,10 +15,10 @@ export class MenuHiddenStatesReset extends Action2 { super({ id: 'menu.resetHiddenStates', title: { - value: localize('title', 'Reset Hidden Menus'), - original: 'Reset Hidden Menus' + value: localize('title', 'Reset All Menus'), + original: 'Reset All Menus' }, - category: localize('cat', 'View'), + category: Categories.View, f1: true }); } diff --git a/src/vs/platform/actions/common/menuService.ts b/src/vs/platform/actions/common/menuService.ts index 86ff1bd3b7e..d1c95179c67 100644 --- a/src/vs/platform/actions/common/menuService.ts +++ b/src/vs/platform/actions/common/menuService.ts @@ -10,7 +10,7 @@ import { IMenu, IMenuActionOptions, IMenuChangeEvent, IMenuCreateOptions, IMenuI import { ICommandAction, ILocalizedString } from 'vs/platform/action/common/action'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { ContextKeyExpression, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IAction, Separator, toAction } from 'vs/base/common/actions'; +import { Separator, toAction } from 'vs/base/common/actions'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { removeFastWithoutKeepingOrder } from 'vs/base/common/arrays'; import { localize } from 'vs/nls'; @@ -32,8 +32,8 @@ export class MenuService implements IMenuService { return new MenuImpl(id, this._hiddenStates, { emitEventsForSubmenuChanges: false, eventDebounceDelay: 50, ...options }, this._commandService, contextKeyService); } - resetHiddenStates(id?: MenuId): void { - this._hiddenStates.reset(id); + resetHiddenStates(ids?: MenuId[]): void { + this._hiddenStates.reset(ids); } } @@ -108,17 +108,19 @@ class PersistedMenuHideState { this._persist(); } - reset(menu?: MenuId): void { - if (menu === undefined) { + reset(menus?: MenuId[]): void { + if (menus === undefined) { // reset all this._data = Object.create(null); this._persist(); } else { // reset only for a specific menu - if (this._data[menu.id]) { - delete this._data[menu.id]; - this._persist(); + for (const { id } of menus) { + if (this._data[id]) { + delete this._data[id]; + } } + this._persist(); } } @@ -215,13 +217,10 @@ class MenuInfo { createActionGroups(options: IMenuActionOptions | undefined): [string, Array][] { const result: [string, Array][] = []; - const allToggleActions: IAction[][] = []; for (const group of this._menuGroups) { const [id, items] = group; - const toggleActions: IAction[] = []; - const activeActions: Array = []; for (const item of items) { if (this._contextKeyService.contextMatchesRules(item.when)) { @@ -229,7 +228,8 @@ class MenuInfo { const menuHide = createMenuHide(this._id, isMenuItem ? item.command : item, this._hiddenStates); if (isMenuItem) { // MenuItemAction - activeActions.push(new MenuItemAction(item.command, item.alt, options, menuHide, this._contextKeyService, this._commandService)); + const actualMenuHide = item.command._isFakeAction ? undefined : menuHide; + activeActions.push(new MenuItemAction(item.command, item.alt, options, actualMenuHide, this._contextKeyService, this._commandService)); } else { // SubmenuItemAction @@ -244,9 +244,6 @@ class MenuInfo { if (activeActions.length > 0) { result.push([id, activeActions]); } - if (toggleActions.length > 0) { - allToggleActions.push(toggleActions); - } } return result; } diff --git a/src/vs/platform/configuration/common/configurationModels.ts b/src/vs/platform/configuration/common/configurationModels.ts index 804398fe3d3..469bcf54e1c 100644 --- a/src/vs/platform/configuration/common/configurationModels.ts +++ b/src/vs/platform/configuration/common/configurationModels.ts @@ -19,6 +19,12 @@ import { FileOperation, IFileService } from 'vs/platform/files/common/files'; import { Registry } from 'vs/platform/registry/common/platform'; import { Workspace } from 'vs/platform/workspace/common/workspace'; +export interface IInspectValue { + value?: V; + override?: V; + merged?: V; +} + export class ConfigurationModel implements IConfigurationModel { private frozen: boolean = false; @@ -33,7 +39,7 @@ export class ConfigurationModel implements IConfigurationModel { } private _rawConfiguration: ConfigurationModel | undefined; - private get rawConfiguration(): ConfigurationModel { + get rawConfiguration(): ConfigurationModel { if (!this._rawConfiguration) { if (this.raw?.length) { const rawConfigurationModels = this.raw.map(raw => { @@ -77,7 +83,7 @@ export class ConfigurationModel implements IConfigurationModel { return section ? getConfigurationValue(this.contents, section) : this.contents; } - inspect(section: string | undefined, overrideIdentifier?: string | null): { value?: V; override?: V; merged?: V } { + inspect(section: string | undefined, overrideIdentifier?: string | null): IInspectValue { const value = this.rawConfiguration.getValue(section); const override = overrideIdentifier ? this.rawConfiguration.getOverrideValue(section, overrideIdentifier) : undefined; const merged = overrideIdentifier ? this.rawConfiguration.override(overrideIdentifier).getValue(section) : value; @@ -483,6 +489,162 @@ export class UserSettings extends Disposable { } } +class ConfigurationInspectValue implements IConfigurationValue { + + constructor( + private readonly key: string, + private readonly overrides: IConfigurationOverrides, + readonly value: V | undefined, + readonly overrideIdentifiers: string[] | undefined, + private readonly defaultConfiguration: ConfigurationModel, + private readonly policyConfiguration: ConfigurationModel | undefined, + private readonly applicationConfiguration: ConfigurationModel | undefined, + private readonly userConfiguration: ConfigurationModel, + private readonly localUserConfiguration: ConfigurationModel, + private readonly remoteUserConfiguration: ConfigurationModel, + private readonly workspaceConfiguration: ConfigurationModel | undefined, + private readonly folderConfigurationModel: ConfigurationModel | undefined, + private readonly memoryInspectValue: IInspectValue, + ) { + } + + private _defaultInspectValue: IInspectValue | undefined; + private get defaultInspectValue(): IInspectValue { + if (!this._defaultInspectValue) { + this._defaultInspectValue = this.defaultConfiguration.inspect(this.key, this.overrides.overrideIdentifier); + } + return this._defaultInspectValue; + } + + get defaultValue(): V | undefined { + return this.defaultInspectValue.merged; + } + + get default(): { value?: V; override?: V } | undefined { + return this.defaultInspectValue.value !== undefined || this.defaultInspectValue.override !== undefined ? { value: this.defaultInspectValue.value, override: this.defaultInspectValue.override } : undefined; + } + + private _policyInspectValue: IInspectValue | undefined | null; + private get policyInspectValue(): IInspectValue | null { + if (this._policyInspectValue === undefined) { + this._policyInspectValue = this.policyConfiguration ? this.policyConfiguration.inspect(this.key) : null; + } + return this._policyInspectValue; + } + + get policyValue(): V | undefined { + return this.policyInspectValue?.merged; + } + + get policy(): { value?: V; override?: V } | undefined { + return this.policyInspectValue?.value !== undefined ? { value: this.policyInspectValue.value } : undefined; + } + + private _applicationInspectValue: IInspectValue | undefined | null; + private get applicationInspectValue(): IInspectValue | null { + if (this._applicationInspectValue === undefined) { + this._applicationInspectValue = this.applicationConfiguration ? this.applicationConfiguration.inspect(this.key) : null; + } + return this._applicationInspectValue; + } + + get applicationValue(): V | undefined { + return this.applicationInspectValue?.merged; + } + + get application(): { value?: V; override?: V } | undefined { + return this.applicationInspectValue?.value !== undefined || this.applicationInspectValue?.override !== undefined ? { value: this.applicationInspectValue.value, override: this.applicationInspectValue.override } : undefined; + } + + private _userInspectValue: IInspectValue | undefined; + private get userInspectValue(): IInspectValue { + if (!this._userInspectValue) { + this._userInspectValue = this.userConfiguration.inspect(this.key, this.overrides.overrideIdentifier); + } + return this._userInspectValue; + } + + get userValue(): V | undefined { + return this.userInspectValue.merged; + } + + get user(): { value?: V; override?: V } | undefined { + return this.userInspectValue.value !== undefined || this.userInspectValue.override !== undefined ? { value: this.userInspectValue.value, override: this.userInspectValue.override } : undefined; + } + + private _userLocalInspectValue: IInspectValue | undefined; + private get userLocalInspectValue(): IInspectValue { + if (!this._userLocalInspectValue) { + this._userLocalInspectValue = this.localUserConfiguration.inspect(this.key, this.overrides.overrideIdentifier); + } + return this._userLocalInspectValue; + } + + get userLocalValue(): V | undefined { + return this.userLocalInspectValue.merged; + } + + get userLocal(): { value?: V; override?: V } | undefined { + return this.userLocalInspectValue.value !== undefined || this.userLocalInspectValue.override !== undefined ? { value: this.userLocalInspectValue.value, override: this.userLocalInspectValue.override } : undefined; + } + + private _userRemoteInspectValue: IInspectValue | undefined; + private get userRemoteInspectValue(): IInspectValue { + if (!this._userRemoteInspectValue) { + this._userRemoteInspectValue = this.remoteUserConfiguration.inspect(this.key, this.overrides.overrideIdentifier); + } + return this._userRemoteInspectValue; + } + + get userRemoteValue(): V | undefined { + return this.userRemoteInspectValue.merged; + } + + get userRemote(): { value?: V; override?: V } | undefined { + return this.userRemoteInspectValue.value !== undefined || this.userRemoteInspectValue.override !== undefined ? { value: this.userRemoteInspectValue.value, override: this.userRemoteInspectValue.override } : undefined; + } + + private _workspaceInspectValue: IInspectValue | undefined | null; + private get workspaceInspectValue(): IInspectValue | null { + if (this._workspaceInspectValue === undefined) { + this._workspaceInspectValue = this.workspaceConfiguration ? this.workspaceConfiguration.inspect(this.key, this.overrides.overrideIdentifier) : null; + } + return this._workspaceInspectValue; + } + + get workspaceValue(): V | undefined { + return this.workspaceInspectValue?.merged; + } + + get workspace(): { value?: V; override?: V } | undefined { + return this.workspaceInspectValue?.value !== undefined || this.workspaceInspectValue?.override !== undefined ? { value: this.workspaceInspectValue.value, override: this.workspaceInspectValue.override } : undefined; + } + + private _workspaceFolderInspectValue: IInspectValue | undefined | null; + private get workspaceFolderInspectValue(): IInspectValue | null { + if (this._workspaceFolderInspectValue === undefined) { + this._workspaceFolderInspectValue = this.folderConfigurationModel ? this.folderConfigurationModel.inspect(this.key, this.overrides.overrideIdentifier) : null; + } + return this._workspaceFolderInspectValue; + } + + get workspaceFolderValue(): V | undefined { + return this.workspaceFolderInspectValue?.merged; + } + + get workspaceFolder(): { value?: V; override?: V } | undefined { + return this.workspaceFolderInspectValue?.value !== undefined || this.workspaceFolderInspectValue?.override !== undefined ? { value: this.workspaceFolderInspectValue.value, override: this.workspaceFolderInspectValue.override } : undefined; + } + + get memoryValue(): V | undefined { + return this.memoryInspectValue.merged; + } + + get memory(): { value?: V; override?: V } | undefined { + return this.memoryInspectValue.value !== undefined || this.memoryInspectValue.override !== undefined ? { value: this.memoryInspectValue.value, override: this.memoryInspectValue.override } : undefined; + } + +} export class Configuration { @@ -532,45 +694,26 @@ export class Configuration { inspect(key: string, overrides: IConfigurationOverrides, workspace: Workspace | undefined): IConfigurationValue { const consolidateConfigurationModel = this.getConsolidatedConfigurationModel(key, overrides, workspace); + const overrideIdentifiers: string[] = arrays.distinct(consolidateConfigurationModel.overrides.map(override => override.identifiers).flat()).filter(overrideIdentifier => consolidateConfigurationModel.getOverrideValue(key, overrideIdentifier) !== undefined); const folderConfigurationModel = this.getFolderConfigurationModelForResource(overrides.resource, workspace); const memoryConfigurationModel = overrides.resource ? this._memoryConfigurationByResource.get(overrides.resource) || this._memoryConfiguration : this._memoryConfiguration; - const defaultInspectValue = this._defaultConfiguration.inspect(key, overrides.overrideIdentifier); - const policyInspectValue = this._policyConfiguration.isEmpty() ? undefined : this._policyConfiguration.freeze().inspect(key); - const applicationInspectValue = this.applicationConfiguration.isEmpty() ? undefined : this.applicationConfiguration.freeze().inspect(key); - const userInspectValue = this.userConfiguration.freeze().inspect(key, overrides.overrideIdentifier); - const userLocalInspectValue = this.localUserConfiguration.freeze().inspect(key, overrides.overrideIdentifier); - const userRemoteInspectValue = this.remoteUserConfiguration.freeze().inspect(key, overrides.overrideIdentifier); - const workspaceInspectValue = workspace ? this._workspaceConfiguration.freeze().inspect(key, overrides.overrideIdentifier) : undefined; //Check on workspace exists or not because _workspaceConfiguration is never null - const workspaceFolderInspectValue = folderConfigurationModel ? folderConfigurationModel.freeze().inspect(key, overrides.overrideIdentifier) : undefined; - const memoryInspectValue = memoryConfigurationModel.inspect(key, overrides.overrideIdentifier); - const value = consolidateConfigurationModel.getValue(key); - const overrideIdentifiers: string[] = arrays.distinct(consolidateConfigurationModel.overrides.map(override => override.identifiers).flat()).filter(overrideIdentifier => consolidateConfigurationModel.getOverrideValue(key, overrideIdentifier) !== undefined); + return new ConfigurationInspectValue( + key, + overrides, + consolidateConfigurationModel.getValue(key), + overrideIdentifiers.length ? overrideIdentifiers : undefined, + this._defaultConfiguration, + this._policyConfiguration.isEmpty() ? undefined : this._policyConfiguration.freeze(), + this.applicationConfiguration.isEmpty() ? undefined : this.applicationConfiguration.freeze(), + this.userConfiguration.freeze(), + this.localUserConfiguration.freeze(), + this.remoteUserConfiguration.freeze(), + workspace ? this._workspaceConfiguration.freeze() : undefined, + folderConfigurationModel ? folderConfigurationModel.freeze() : undefined, + memoryConfigurationModel.inspect(key, overrides.overrideIdentifier) + ); - return { - defaultValue: defaultInspectValue ? defaultInspectValue.merged : undefined, - policyValue: policyInspectValue ? policyInspectValue.merged : undefined, - applicationValue: applicationInspectValue ? applicationInspectValue.merged : undefined, - userValue: userInspectValue ? userInspectValue.merged : undefined, - userLocalValue: userLocalInspectValue ? userLocalInspectValue.merged : undefined, - userRemoteValue: userRemoteInspectValue ? userRemoteInspectValue.merged : undefined, - workspaceValue: workspaceInspectValue ? workspaceInspectValue.merged : undefined, - workspaceFolderValue: workspaceFolderInspectValue ? workspaceFolderInspectValue.merged : undefined, - memoryValue: memoryInspectValue ? memoryInspectValue.merged : undefined, - value, - - default: defaultInspectValue.value !== undefined || defaultInspectValue.override !== undefined ? { value: defaultInspectValue.value, override: defaultInspectValue.override } : undefined, - policy: policyInspectValue?.value !== undefined ? { value: policyInspectValue.value } : undefined, - application: applicationInspectValue?.value !== undefined || applicationInspectValue?.override !== undefined ? { value: applicationInspectValue.value, override: applicationInspectValue.override } : undefined, - user: userInspectValue.value !== undefined || userInspectValue.override !== undefined ? { value: userInspectValue.value, override: userInspectValue.override } : undefined, - userLocal: userLocalInspectValue.value !== undefined || userLocalInspectValue.override !== undefined ? { value: userLocalInspectValue.value, override: userLocalInspectValue.override } : undefined, - userRemote: userRemoteInspectValue.value !== undefined || userRemoteInspectValue.override !== undefined ? { value: userRemoteInspectValue.value, override: userRemoteInspectValue.override } : undefined, - workspace: workspaceInspectValue?.value !== undefined || workspaceInspectValue?.override !== undefined ? { value: workspaceInspectValue.value, override: workspaceInspectValue.override } : undefined, - workspaceFolder: workspaceFolderInspectValue?.value !== undefined || workspaceFolderInspectValue?.override !== undefined ? { value: workspaceFolderInspectValue.value, override: workspaceFolderInspectValue.override } : undefined, - memory: memoryInspectValue.value !== undefined || memoryInspectValue.override !== undefined ? { value: memoryInspectValue.value, override: memoryInspectValue.override } : undefined, - - overrideIdentifiers: overrideIdentifiers.length ? overrideIdentifiers : undefined - }; } keys(workspace: Workspace | undefined): { @@ -978,7 +1121,7 @@ export class ConfigurationChangeEvent implements IConfigurationChangeEvent { } function compare(from: ConfigurationModel | undefined, to: ConfigurationModel | undefined): IConfigurationCompareResult { - const { added, removed, updated } = compareConfigurationContents(to, from); + const { added, removed, updated } = compareConfigurationContents(to?.rawConfiguration, from?.rawConfiguration); const overrides: [string, string[]][] = []; const fromOverrideIdentifiers = from?.getAllOverrideIdentifiers() || []; diff --git a/src/vs/platform/configuration/test/common/configurationService.test.ts b/src/vs/platform/configuration/test/common/configurationService.test.ts index 451a52e4917..0cb77b1693f 100644 --- a/src/vs/platform/configuration/test/common/configurationService.test.ts +++ b/src/vs/platform/configuration/test/common/configurationService.test.ts @@ -9,6 +9,7 @@ import { Event } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; +import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { Extensions as ConfigurationExtensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { ConfigurationService } from 'vs/platform/configuration/common/configurationService'; @@ -34,7 +35,7 @@ suite('ConfigurationService', () => { teardown(() => disposables.clear()); - test('simple', async () => { + test('simple', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }')); const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService(), new NullLogService())); await testObject.initialize(); @@ -44,9 +45,9 @@ suite('ConfigurationService', () => { assert.ok(config); assert.strictEqual(config.foo, 'bar'); - }); + })); - test('config gets flattened', async () => { + test('config gets flattened', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService(), new NullLogService())); @@ -63,9 +64,9 @@ suite('ConfigurationService', () => { assert.ok(config.testworkbench); assert.ok(config.testworkbench.editor); assert.strictEqual(config.testworkbench.editor.tabs, true); - }); + })); - test('error case does not explode', async () => { + test('error case does not explode', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(settingsResource, VSBuffer.fromString(',,,,')); const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService(), new NullLogService())); @@ -75,18 +76,18 @@ suite('ConfigurationService', () => { }>(); assert.ok(config); - }); + })); - test('missing file does not explode', async () => { + test('missing file does not explode', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject = disposables.add(new ConfigurationService(URI.file('__testFile'), fileService, new NullPolicyService(), new NullLogService())); await testObject.initialize(); const config = testObject.getValue<{ foo: string }>(); assert.ok(config); - }); + })); - test('trigger configuration change event when file does not exist', async () => { + test('trigger configuration change event when file does not exist', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService(), new NullLogService())); await testObject.initialize(); return new Promise((c, e) => { @@ -97,9 +98,9 @@ suite('ConfigurationService', () => { fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }')).catch(e); }); - }); + })); - test('trigger configuration change event when file exists', async () => { + test('trigger configuration change event when file exists', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService(), new NullLogService())); await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }')); await testObject.initialize(); @@ -111,9 +112,9 @@ suite('ConfigurationService', () => { })); fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "barz" }')); }); - }); + })); - test('reloadConfiguration', async () => { + test('reloadConfiguration', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }')); const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService(), new NullLogService())); @@ -132,9 +133,9 @@ suite('ConfigurationService', () => { }>(); assert.ok(config); assert.strictEqual(config.foo, 'changed'); - }); + })); - test('model defaults', async () => { + test('model defaults', () => runWithFakedTimers({ useFakeTimers: true }, async () => { interface ITestSetting { configuration: { service: { @@ -176,9 +177,9 @@ suite('ConfigurationService', () => { setting = testObject.getValue(); assert.ok(setting); assert.strictEqual(setting.configuration.service.testSetting, 'isChanged'); - }); + })); - test('lookup', async () => { + test('lookup', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); configurationRegistry.registerConfiguration({ 'id': '_test', @@ -212,9 +213,9 @@ suite('ConfigurationService', () => { assert.strictEqual(res.userValue, 'bar'); assert.strictEqual(res.value, 'bar'); - }); + })); - test('lookup with null', async () => { + test('lookup with null', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); configurationRegistry.registerConfiguration({ 'id': '_testNull', @@ -242,5 +243,5 @@ suite('ConfigurationService', () => { assert.strictEqual(res.defaultValue, null); assert.strictEqual(res.value, null); assert.strictEqual(res.userValue, null); - }); + })); }); diff --git a/src/vs/platform/contextkey/browser/contextKeyService.ts b/src/vs/platform/contextkey/browser/contextKeyService.ts index 07a136ef7b9..0ebe4e7c7ef 100644 --- a/src/vs/platform/contextkey/browser/contextKeyService.ts +++ b/src/vs/platform/contextkey/browser/contextKeyService.ts @@ -10,12 +10,14 @@ import { TernarySearchTree } from 'vs/base/common/map'; import { MarshalledObject } from 'vs/base/common/marshalling'; import { MarshalledId } from 'vs/base/common/marshallingIds'; import { cloneAndChange, distinct } from 'vs/base/common/objects'; +import { StopWatch } from 'vs/base/common/stopwatch'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpression, ContextKeyInfo, ContextKeyValue, IContext, IContextKey, IContextKeyChangeEvent, IContextKeyService, IContextKeyServiceTarget, IReadableSet, RawContextKey, SET_CONTEXT_COMMAND_ID } from 'vs/platform/contextkey/common/contextkey'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; const KEYBINDING_CONTEXT_ATTR = 'data-keybinding-context'; @@ -603,7 +605,27 @@ function findContextAttr(domNode: IContextKeyServiceTarget | null): number { } export function setContext(accessor: ServicesAccessor, contextKey: any, contextValue: any) { - accessor.get(IContextKeyService).createKey(String(contextKey), stringifyURIs(contextValue)); + const contextKeyService = accessor.get(IContextKeyService); + const telemetryService = accessor.get(ITelemetryService); + + const sw = new StopWatch(true); + contextKeyService.createKey(String(contextKey), stringifyURIs(contextValue)); + const duration = sw.elapsed(); + + type TelemetryData = { + duration: number; + contextKey: string; + }; + type TelemetryClassification = { + owner: 'jrieken'; + comment: 'Performance numbers of the setContext-API command'; + duration: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'The time it took to set the context key' }; + contextKey: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The context key that got set' }; + }; + telemetryService.publicLog2('command.setContext', { + contextKey: String(contextKey), + duration + }); } function stringifyURIs(contextValue: any): any { diff --git a/src/vs/platform/contextkey/common/contextkey.ts b/src/vs/platform/contextkey/common/contextkey.ts index 97473d6c93f..b2bcf092821 100644 --- a/src/vs/platform/contextkey/common/contextkey.ts +++ b/src/vs/platform/contextkey/common/contextkey.ts @@ -162,12 +162,12 @@ export abstract class ContextKeyExpr { if (serializedOne.indexOf(' not in ') >= 0) { const pieces = serializedOne.split(' not in '); - return ContextKeyNotInExpr.create(pieces[0].trim(), pieces[1].trim()); + return ContextKeyNotInExpr.create(pieces[0].trim(), this._deserializeValue(pieces[1], strict)); } if (serializedOne.indexOf(' in ') >= 0) { const pieces = serializedOne.split(' in '); - return ContextKeyInExpr.create(pieces[0].trim(), pieces[1].trim()); + return ContextKeyInExpr.create(pieces[0].trim(), this._deserializeValue(pieces[1], strict)); } if (/^[^<=>]+>=[^<=>]+$/.test(serializedOne)) { diff --git a/src/vs/platform/contextkey/test/browser/contextkey.test.ts b/src/vs/platform/contextkey/test/browser/contextkey.test.ts index 50309c6fe6b..8cbecda9b4b 100644 --- a/src/vs/platform/contextkey/test/browser/contextkey.test.ts +++ b/src/vs/platform/contextkey/test/browser/contextkey.test.ts @@ -6,12 +6,14 @@ import * as assert from 'assert'; import { DeferredPromise } from 'vs/base/common/async'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; +import { mock } from 'vs/base/test/common/mock'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { ContextKeyService, setContext } from 'vs/platform/contextkey/browser/contextKeyService'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; suite('ContextKeyService', () => { test('updateParent', () => { @@ -64,7 +66,12 @@ suite('ContextKeyService', () => { const contextKeyService: IContextKeyService = disposables.add(new ContextKeyService(configurationService)); const instantiationService = new TestInstantiationService(new ServiceCollection( [IConfigurationService, configurationService], - [IContextKeyService, contextKeyService] + [IContextKeyService, contextKeyService], + [ITelemetryService, new class extends mock() { + override async publicLog2() { + // + } + }] )); const uri = URI.parse('test://abc'); diff --git a/src/vs/platform/contextview/browser/contextMenuService.ts b/src/vs/platform/contextview/browser/contextMenuService.ts index c80851be9ac..fe80ed05a20 100644 --- a/src/vs/platform/contextview/browser/contextMenuService.ts +++ b/src/vs/platform/contextview/browser/contextMenuService.ts @@ -5,14 +5,18 @@ import { IContextMenuDelegate } from 'vs/base/browser/contextmenu'; import { ModifierKeyEmitter } from 'vs/base/browser/dom'; +import { IAction, Separator } from 'vs/base/common/actions'; import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; +import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ContextMenuHandler, IContextMenuHandlerOptions } from './contextMenuHandler'; -import { IContextMenuService, IContextViewService } from './contextView'; +import { IContextMenuMenuDelegate, IContextMenuService, IContextViewService } from './contextView'; export class ContextMenuService extends Disposable implements IContextMenuService { @@ -27,10 +31,10 @@ export class ContextMenuService extends Disposable implements IContextMenuServic return this._contextMenuHandler; } - private readonly _onDidShowContextMenu = new Emitter(); + private readonly _onDidShowContextMenu = this._store.add(new Emitter()); readonly onDidShowContextMenu = this._onDidShowContextMenu.event; - private readonly _onDidHideContextMenu = new Emitter(); + private readonly _onDidHideContextMenu = this._store.add(new Emitter()); readonly onDidHideContextMenu = this._onDidHideContextMenu.event; constructor( @@ -38,7 +42,9 @@ export class ContextMenuService extends Disposable implements IContextMenuServic @INotificationService private readonly notificationService: INotificationService, @IContextViewService private readonly contextViewService: IContextViewService, @IKeybindingService private readonly keybindingService: IKeybindingService, - @IThemeService private readonly themeService: IThemeService + @IThemeService private readonly themeService: IThemeService, + @IMenuService private readonly menuService: IMenuService, + @IContextKeyService private readonly contextKeyService: IContextKeyService, ) { super(); } @@ -49,7 +55,10 @@ export class ContextMenuService extends Disposable implements IContextMenuServic // ContextMenu - showContextMenu(delegate: IContextMenuDelegate): void { + showContextMenu(delegate: IContextMenuDelegate | IContextMenuMenuDelegate): void { + + delegate = ContextMenuMenuDelegate.transform(delegate, this.menuService, this.contextKeyService); + this.contextMenuHandler.showContextMenu({ ...delegate, onHide: (didCancel) => { @@ -62,3 +71,33 @@ export class ContextMenuService extends Disposable implements IContextMenuServic this._onDidShowContextMenu.fire(); } } + +export namespace ContextMenuMenuDelegate { + + function is(thing: IContextMenuDelegate | IContextMenuMenuDelegate): thing is IContextMenuMenuDelegate { + return thing && (thing).menuId instanceof MenuId; + } + + export function transform(delegate: IContextMenuDelegate | IContextMenuMenuDelegate, menuService: IMenuService, globalContextKeyService: IContextKeyService): IContextMenuDelegate { + if (!is(delegate)) { + return delegate; + } + const { menuId, menuActionOptions, contextKeyService } = delegate; + return { + ...delegate, + getActions: () => { + const target: IAction[] = []; + if (menuId) { + const menu = menuService.createMenu(menuId, contextKeyService ?? globalContextKeyService); + createAndFillInContextMenuActions(menu, menuActionOptions, target); + menu.dispose(); + } + if (!delegate.getActions) { + return target; + } else { + return Separator.join(delegate.getActions(), target); + } + } + }; + } +} diff --git a/src/vs/platform/contextview/browser/contextView.ts b/src/vs/platform/contextview/browser/contextView.ts index b99bfdc0aa5..505d76eabfb 100644 --- a/src/vs/platform/contextview/browser/contextView.ts +++ b/src/vs/platform/contextview/browser/contextView.ts @@ -5,8 +5,11 @@ import { IContextMenuDelegate } from 'vs/base/browser/contextmenu'; import { AnchorAlignment, AnchorAxisAlignment, IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; +import { IAction } from 'vs/base/common/actions'; import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { IMenuActionOptions, MenuId } from 'vs/platform/actions/common/actions'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export const IContextViewService = createDecorator('contextViewService'); @@ -44,5 +47,25 @@ export interface IContextMenuService { readonly onDidShowContextMenu: Event; readonly onDidHideContextMenu: Event; - showContextMenu(delegate: IContextMenuDelegate): void; + showContextMenu(delegate: IContextMenuDelegate | IContextMenuMenuDelegate): void; } + +export type IContextMenuMenuDelegate = { + /** + * The MenuId that should be used to populate the context menu. + */ + menuId?: MenuId; + /** + * Optional options how menu actions are invoked + */ + menuActionOptions?: IMenuActionOptions; + /** + * Optional context key service which drives the given menu + */ + contextKeyService?: IContextKeyService; + + /** + * Optional getter for extra actions. They will be prepended to the menu actions. + */ + getActions?(): IAction[]; +} & Omit; diff --git a/src/vs/platform/environment/common/argv.ts b/src/vs/platform/environment/common/argv.ts index 73754ba6042..0ef429408f2 100644 --- a/src/vs/platform/environment/common/argv.ts +++ b/src/vs/platform/environment/common/argv.ts @@ -7,6 +7,18 @@ * A list of command line arguments we support natively. */ export interface NativeParsedArgs { + // subcommands + tunnel?: { + 'cli-data-dir'?: string; + 'disable-telemetry'?: boolean; + 'telemetry-level'?: string; + user: { + login: { + 'access-token'?: string; + 'provider'?: string; + }; + }; + }; _: string[]; 'folder-uri'?: string[]; // undefined or array of 1 or more 'file-uri'?: string[]; // undefined or array of 1 or more @@ -36,7 +48,7 @@ export interface NativeParsedArgs { 'trace-category-filter'?: string; 'trace-options'?: string; 'open-devtools'?: boolean; - log?: string; + log?: string[]; logExtensionHostCommunication?: boolean; 'extensions-dir'?: string; 'extensions-download-dir'?: string; @@ -90,6 +102,7 @@ export interface NativeParsedArgs { 'logsPath'?: string; '__enable-file-policy'?: boolean; editSessionId?: string; + continueOn?: string; 'locate-shell-integration-path'?: string; 'profile'?: string; 'profile-temp'?: boolean; diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index faf55adc736..3953e06a708 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -65,6 +65,7 @@ export interface IEnvironmentService { sync: 'on' | 'off' | undefined; // --- continue edit session + continueOn?: string; editSessionId?: string; editSessionsLogResource: URI; @@ -80,13 +81,13 @@ export interface IEnvironmentService { // --- logging logsPath: string; logLevel?: string; + extensionLogLevel?: [string, string][]; verbose: boolean; isBuilt: boolean; // --- telemetry disableTelemetry: boolean; telemetryLogResource: URI; - extensionTelemetryLogResource: URI; serviceMachineIdResource: URI; // --- Policy @@ -124,6 +125,13 @@ export interface INativeEnvironmentService extends IEnvironmentService { args: NativeParsedArgs; // --- data paths + /** + * Root path of the JavaScript sources. + * + * Note: This is NOT the installation root + * directory itself but contained in it at + * a level that is platform dependent. + */ appRoot: string; userHome: URI; appSettingsHome: URI; diff --git a/src/vs/platform/environment/common/environmentService.ts b/src/vs/platform/environment/common/environmentService.ts index d90cbc60fef..934a8657683 100644 --- a/src/vs/platform/environment/common/environmentService.ts +++ b/src/vs/platform/environment/common/environmentService.ts @@ -14,6 +14,8 @@ import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { ExtensionKind, IDebugParams, IExtensionHostDebugParams, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { IProductService } from 'vs/platform/product/common/productService'; +export const EXTENSION_IDENTIFIER_WITH_LOG_REGEX = /^([^.]+\..+):(.+)$/; + export interface INativeEnvironmentPaths { /** @@ -216,7 +218,20 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron get isBuilt(): boolean { return !env['VSCODE_DEV']; } get verbose(): boolean { return !!this.args.verbose; } - get logLevel(): string | undefined { return this.args.log; } + + @memoize + get logLevel(): string | undefined { return this.args.log?.find(entry => !EXTENSION_IDENTIFIER_WITH_LOG_REGEX.test(entry)); } + @memoize + get extensionLogLevel(): [string, string][] | undefined { + const result: [string, string][] = []; + for (const entry of this.args.log || []) { + const matches = EXTENSION_IDENTIFIER_WITH_LOG_REGEX.exec(entry); + if (matches && matches[1] && matches[2]) { + result.push([matches[1], matches[2]]); + } + } + return result.length ? result : undefined; + } @memoize get serviceMachineIdResource(): URI { return joinPath(URI.file(this.userDataPath), 'machineid'); } @@ -226,7 +241,6 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron @memoize get telemetryLogResource(): URI { return URI.file(join(this.logsPath, 'telemetry.log')); } - get extensionTelemetryLogResource(): URI { return URI.file(join(this.logsPath, 'extensionTelemetry.log')); } get disableTelemetry(): boolean { return !!this.args['disable-telemetry']; } @memoize @@ -247,6 +261,14 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron editSessionId: string | undefined = this.args['editSessionId']; + get continueOn(): string | undefined { + return this.args['continueOn']; + } + + set continueOn(value: string | undefined) { + this.args['continueOn'] = value; + } + get args(): NativeParsedArgs { return this._args; } constructor( diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index d5167a39273..9ea98d94eb4 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -26,20 +26,47 @@ export interface Option { deprecationMessage?: string; allowEmptyValue?: boolean; cat?: keyof typeof helpCategories; + global?: boolean; +} + +export interface Subcommand { + type: 'subcommand'; + description?: string; + deprecationMessage?: string; + options: OptionDescriptions>; } export type OptionDescriptions = { - [P in keyof T]: Option>; + [P in keyof T]: + T[P] extends boolean ? Option<'boolean'> : + T[P] extends string ? Option<'string'> : + T[P] extends string[] ? Option<'string[]'> : + Subcommand }; -type OptionTypeName = - T extends boolean ? 'boolean' : - T extends string ? 'string' : - T extends string[] ? 'string[]' : - T extends undefined ? 'undefined' : - 'unknown'; - export const OPTIONS: OptionDescriptions> = { + 'tunnel': { + type: 'subcommand', + description: 'Make the current machine accessible from vscode.dev or other machines through a secure tunnel', + options: { + 'cli-data-dir': { type: 'string', args: 'dir', description: localize('cliDataDir', "Directory where CLI metadata should be stored.") }, + 'disable-telemetry': { type: 'boolean' }, + 'telemetry-level': { type: 'string' }, + user: { + type: 'subcommand', + options: { + login: { + type: 'subcommand', + options: { + provider: { type: 'string' }, + 'access-token': { type: 'string' } + } + } + } + } + } + }, + 'diff': { type: 'boolean', cat: 'o', alias: 'd', args: ['file', 'file'], description: localize('diff', "Compare two files with each other.") }, 'merge': { type: 'boolean', cat: 'o', alias: 'm', args: ['path1', 'path2', 'base', 'result'], description: localize('merge', "Perform a three-way merge by providing paths for two modified versions of a file, the common origin of both modified versions and the output file to save merge results.") }, 'add': { type: 'boolean', cat: 'o', alias: 'a', args: 'folder', description: localize('add', "Add folder(s) to the last active window.") }, @@ -65,8 +92,8 @@ export const OPTIONS: OptionDescriptions> = { 'enable-proposed-api': { type: 'string[]', allowEmptyValue: true, cat: 'e', args: 'ext-id', description: localize('experimentalApis', "Enables proposed API features for extensions. Can receive one or more extension IDs to enable individually.") }, 'version': { type: 'boolean', cat: 't', alias: 'v', description: localize('version', "Print version.") }, - 'verbose': { type: 'boolean', cat: 't', description: localize('verbose', "Print verbose output (implies --wait).") }, - 'log': { type: 'string', cat: 't', args: 'level', description: localize('log', "Log level to use. Default is 'info'. Allowed values are 'critical', 'error', 'warn', 'info', 'debug', 'trace', 'off'.") }, + 'verbose': { type: 'boolean', cat: 't', global: true, description: localize('verbose', "Print verbose output (implies --wait).") }, + 'log': { type: 'string[]', cat: 't', args: 'level', global: true, description: localize('log', "Log level to use. Default is 'info'. Allowed values are 'critical', 'error', 'warn', 'info', 'debug', 'trace', 'off'. You can also configure the log level of an extension by passing extension id and log level in the following format: '${publisher}.${name}:${logLevel}'. For example: 'vscode.csharp:trace'. Can receive one or more such entries.") }, 'status': { type: 'boolean', alias: 's', cat: 't', description: localize('status', "Print process usage and diagnostics information.") }, 'prof-startup': { type: 'boolean', cat: 't', description: localize('prof-startup', "Run CPU profiler during startup.") }, 'prof-append-timers': { type: 'string' }, @@ -80,7 +107,7 @@ export const OPTIONS: OptionDescriptions> = { 'inspect-extensions': { type: 'string', allowEmptyValue: true, deprecates: ['debugPluginHost'], args: 'port', cat: 't', description: localize('inspect-extensions', "Allow debugging and profiling of extensions. Check the developer tools for the connection URI.") }, 'inspect-brk-extensions': { type: 'string', allowEmptyValue: true, deprecates: ['debugBrkPluginHost'], args: 'port', cat: 't', description: localize('inspect-brk-extensions', "Allow debugging and profiling of extensions with the extension host being paused after start. Check the developer tools for the connection URI.") }, 'disable-gpu': { type: 'boolean', cat: 't', description: localize('disableGPU', "Disable GPU hardware acceleration.") }, - 'ms-enable-electron-run-as-node': { type: 'boolean' }, + 'ms-enable-electron-run-as-node': { type: 'boolean', global: true }, 'max-memory': { type: 'string', cat: 't', description: localize('maxMemory', "Max memory size for a window (in Mbytes)."), args: 'memory' }, 'telemetry': { type: 'boolean', cat: 't', description: localize('telemetry', "Shows all telemetry events which VS code collects.") }, @@ -129,6 +156,7 @@ export const OPTIONS: OptionDescriptions> = { 'logsPath': { type: 'string' }, '__enable-file-policy': { type: 'boolean' }, 'editSessionId': { type: 'string' }, + 'continueOn': { type: 'string' }, 'locate-shell-integration-path': { type: 'string', args: ['bash', 'pwsh', 'zsh', 'fish'] }, 'enable-coi': { type: 'boolean' }, @@ -166,9 +194,11 @@ export interface ErrorReporter { onMultipleValues(id: string, usedValue: string): void; onEmptyValue(id: string): void; onDeprecatedOption(deprecatedId: string, message: string): void; + + getSubcommandReporter?(commmand: string): ErrorReporter; } -const ignoringReporter: ErrorReporter = { +const ignoringReporter = { onUnknownOption: () => { }, onMultipleValues: () => { }, onEmptyValue: () => { }, @@ -176,29 +206,56 @@ const ignoringReporter: ErrorReporter = { }; export function parseArgs(args: string[], options: OptionDescriptions, errorReporter: ErrorReporter = ignoringReporter): T { + const firstArg = args.find(a => a.length > 0 && a[0] !== '-'); + const alias: { [key: string]: string } = {}; - const string: string[] = ['_']; - const boolean: string[] = []; + const stringOptions: string[] = ['_']; + const booleanOptions: string[] = []; + const globalOptions: OptionDescriptions = {}; + let command: Subcommand | undefined = undefined; for (const optionId in options) { const o = options[optionId]; - if (o.alias) { - alias[optionId] = o.alias; - } - - if (o.type === 'string' || o.type === 'string[]') { - string.push(optionId); - if (o.deprecates) { - string.push(...o.deprecates); + if (o.type === 'subcommand') { + if (optionId === firstArg) { + command = o; } - } else if (o.type === 'boolean') { - boolean.push(optionId); - if (o.deprecates) { - boolean.push(...o.deprecates); + } else { + if (o.alias) { + alias[optionId] = o.alias; + } + + if (o.type === 'string' || o.type === 'string[]') { + stringOptions.push(optionId); + if (o.deprecates) { + stringOptions.push(...o.deprecates); + } + } else if (o.type === 'boolean') { + booleanOptions.push(optionId); + if (o.deprecates) { + booleanOptions.push(...o.deprecates); + } + } + if (o.global) { + globalOptions[optionId] = o; } } } + if (command && firstArg) { + const options = globalOptions; + for (const optionId in command.options) { + options[optionId] = command.options[optionId]; + } + const newArgs = args.filter(a => a !== firstArg); + const reporter = errorReporter.getSubcommandReporter ? errorReporter.getSubcommandReporter(firstArg) : undefined; + const subcommandOptions = parseArgs(newArgs, options, reporter); + return { + [firstArg]: subcommandOptions + }; + } + + // remove aliases to avoid confusion - const parsedArgs = minimist(args, { string, boolean, alias }); + const parsedArgs = minimist(args, { string: stringOptions, boolean: booleanOptions, alias }); const cleanedArgs: any = {}; const remainingArgs: any = parsedArgs; @@ -210,6 +267,9 @@ export function parseArgs(args: string[], options: OptionDescriptions, err for (const optionId in options) { const o = options[optionId]; + if (o.type === 'subcommand') { + continue; + } if (o.alias) { delete remainingArgs[o.alias]; } @@ -283,14 +343,17 @@ function formatUsage(optionId: string, option: Option) { // exported only for testing export function formatOptions(options: OptionDescriptions, columns: number): string[] { - let maxLength = 0; const usageTexts: [string, string][] = []; for (const optionId in options) { const o = options[optionId]; const usageText = formatUsage(optionId, o); - maxLength = Math.max(maxLength, usageText.length); usageTexts.push([usageText, o.description!]); } + return formatUsageTexts(usageTexts, columns); +} + +function formatUsageTexts(usageTexts: [string, string][], columns: number) { + const maxLength = usageTexts.reduce((previous, e) => Math.max(previous, e[0].length), 12); const argLength = maxLength + 2/*left padding*/ + 1/*right padding*/; if (columns - argLength < 25) { // Use a condensed version on narrow terminals @@ -342,9 +405,14 @@ export function buildHelpMessage(productName: string, executableName: string, ve help.push(''); } const optionsByCategory: { [P in keyof typeof helpCategories]?: OptionDescriptions } = {}; + const subcommands: { command: string; description: string }[] = []; for (const optionId in options) { const o = options[optionId]; - if (o.description && o.cat) { + if (o.type === 'subcommand') { + if (o.description) { + subcommands.push({ command: optionId, description: o.description }); + } + } else if (o.description && o.cat) { let optionsByCat = optionsByCategory[o.cat]; if (!optionsByCat) { optionsByCategory[o.cat] = optionsByCat = {}; @@ -363,6 +431,13 @@ export function buildHelpMessage(productName: string, executableName: string, ve help.push(''); } } + + if (subcommands.length) { + help.push(localize('subcommands', "Subcommands")); + help.push(...formatUsageTexts(subcommands.map(s => [s.command, s.description]), columns)); + help.push(''); + } + return help.join('\n'); } diff --git a/src/vs/platform/environment/node/argvHelper.ts b/src/vs/platform/environment/node/argvHelper.ts index fcf94639485..610ba3d774a 100644 --- a/src/vs/platform/environment/node/argvHelper.ts +++ b/src/vs/platform/environment/node/argvHelper.ts @@ -12,19 +12,34 @@ import { ErrorReporter, OPTIONS, parseArgs } from 'vs/platform/environment/node/ const MIN_MAX_MEMORY_SIZE_MB = 2048; function parseAndValidate(cmdLineArgs: string[], reportWarnings: boolean): NativeParsedArgs { + const onMultipleValues = (id: string, val: string) => { + console.warn(localize('multipleValues', "Option '{0}' is defined more than once. Using value '{1}'.", id, val)); + }; + const onEmptyValue = (id: string) => { + console.warn(localize('emptyValue', "Option '{0}' requires a non empty value. Ignoring the option.", id)); + }; + const onDeprecatedOption = (deprecatedOption: string, message: string) => { + console.warn(localize('deprecatedArgument', "Option '{0}' is deprecated: {1}", deprecatedOption, message)); + }; + const getSubcommandReporter = (command: string) => ({ + onUnknownOption: (id: string) => { + if (command !== 'tunnel') { + console.warn(localize('unknownSubCommandOption', "Warning: '{0}' is not in the list of known options for subcommand '{1}'", id, command)); + } + }, + onMultipleValues, + onEmptyValue, + onDeprecatedOption, + getSubcommandReporter: command !== 'tunnel' ? getSubcommandReporter : undefined + }); const errorReporter: ErrorReporter = { onUnknownOption: (id) => { console.warn(localize('unknownOption', "Warning: '{0}' is not in the list of known options, but still passed to Electron/Chromium.", id)); }, - onMultipleValues: (id, val) => { - console.warn(localize('multipleValues', "Option '{0}' is defined more than once. Using value '{1}'.", id, val)); - }, - onEmptyValue: (id) => { - console.warn(localize('emptyValue', "Option '{0}' requires a non empty value. Ignoring the option.", id)); - }, - onDeprecatedOption: (deprecatedOption: string, message: string) => { - console.warn(localize('deprecatedArgument', "Option '{0}' is deprecated: {1}", deprecatedOption, message)); - } + onMultipleValues, + onEmptyValue, + onDeprecatedOption, + getSubcommandReporter }; const args = parseArgs(cmdLineArgs, OPTIONS, reportWarnings ? errorReporter : undefined); @@ -68,7 +83,12 @@ export function parseMainProcessArgv(processArgv: string[]): NativeParsedArgs { * Use this to parse raw code CLI process.argv such as: `Electron cli.js . --verbose --wait` */ export function parseCLIProcessArgv(processArgv: string[]): NativeParsedArgs { - const [, , ...args] = processArgv; // remove the first non-option argument: it's always the app location + let [, , ...args] = processArgv; // remove the first non-option argument: it's always the app location + + // If dev, remove the first non-option argument: it's the app location + if (process.env['VSCODE_DEV']) { + args = stripAppPath(args) || []; + } return parseAndValidate(args, true); } diff --git a/src/vs/platform/environment/test/node/argv.test.ts b/src/vs/platform/environment/test/node/argv.test.ts index 7d1439125d2..fc182805893 100644 --- a/src/vs/platform/environment/test/node/argv.test.ts +++ b/src/vs/platform/environment/test/node/argv.test.ts @@ -4,23 +4,28 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { formatOptions, Option } from 'vs/platform/environment/node/argv'; +import { formatOptions, Option, OptionDescriptions, Subcommand, parseArgs, ErrorReporter } from 'vs/platform/environment/node/argv'; import { addArg } from 'vs/platform/environment/node/argvHelper'; -suite('formatOptions', () => { +function o(description: string, type: 'boolean' | 'string' | 'string[]' = 'string'): Option { + return { + description, type + }; +} +function c(description: string, options: OptionDescriptions): Subcommand { + return { + description, type: 'subcommand', options + }; +} - function o(description: string): Option { - return { - description, type: 'string' - }; - } +suite('formatOptions', () => { test('Text should display small columns correctly', () => { assert.deepStrictEqual( formatOptions({ 'add': o('bar') }, 80), - [' --add bar'] + [' --add bar'] ); assert.deepStrictEqual( formatOptions({ @@ -29,9 +34,9 @@ suite('formatOptions', () => { 'trace': o('b') }, 80), [ - ' --add bar', - ' --wait ba', - ' --trace b' + ' --add bar', + ' --wait ba', + ' --trace b' ]); }); @@ -41,8 +46,8 @@ suite('formatOptions', () => { 'add': o(('bar ').repeat(9)) }, 40), [ - ' --add bar bar bar bar bar bar bar bar', - ' bar' + ' --add bar bar bar bar bar bar', + ' bar bar bar' ]); }); @@ -65,4 +70,67 @@ suite('formatOptions', () => { assert.deepStrictEqual(addArg(['--wait', '--', '--foo'], 'bar'), ['--wait', 'bar', '--', '--foo']); assert.deepStrictEqual(addArg(['--', '--foo'], 'bar'), ['bar', '--', '--foo']); }); + + test('subcommands', () => { + assert.deepStrictEqual( + formatOptions({ + 'testcmd': c('A test command', { add: o('A test command option') }) + }, 30), + [ + ' --testcmd', + ' A test command' + ]); + }); +}); + +suite('parseArgs', () => { + function newErrorReporter(result: string[] = [], command = ''): ErrorReporter & { result: string[] } { + const commandPrefix = command ? command + '-' : ''; + return { + onDeprecatedOption: (deprecatedId) => result.push(`${commandPrefix}onDeprecatedOption ${deprecatedId}`), + onUnknownOption: (id) => result.push(`${commandPrefix}onUnknownOption ${id}`), + onEmptyValue: (id) => result.push(`${commandPrefix}onEmptyValue ${id}`), + onMultipleValues: (id, usedValue) => result.push(`${commandPrefix}onMultipleValues ${id} ${usedValue}`), + getSubcommandReporter: (c) => newErrorReporter(result, commandPrefix + c), + result + }; + } + + function assertParse(options: OptionDescriptions, input: string[], expected: T, expectedErrors: string[]) { + const errorReporter = newErrorReporter(); + assert.deepStrictEqual(parseArgs(input, options, errorReporter), expected); + assert.deepStrictEqual(errorReporter.result, expectedErrors); + } + + test('subcommands', () => { + const options1 = { + 'testcmd': c('A test command', { + testArg: o('A test command option') + }) + }; + assertParse( + options1, + ['testcmd', '--testArg=foo'], + { testcmd: { testArg: 'foo', '_': [] } }, + [] + ); + assertParse( + options1, + ['testcmd', '--testArg=foo', '--testX'], + { testcmd: { testArg: 'foo', '_': [] } }, + ['testcmd-onUnknownOption testX'] + ); + const options2 = { + 'testcmd': c('A test command', { + testArg: o('A test command option') + }), + testX: { global: true } + }; + assertParse( + options2, + ['testcmd', '--testArg=foo', '--testX'], + { testcmd: { testArg: 'foo', testX: true, '_': [] } }, + [] + ); + }); }); diff --git a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts index 6b805a92768..3e91ecf3b2c 100644 --- a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts +++ b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts @@ -50,16 +50,16 @@ export abstract class AbstractExtensionManagementService extends Disposable impl private readonly uninstallingExtensions = new Map(); private readonly _onInstallExtension = this._register(new Emitter()); - readonly onInstallExtension = this._onInstallExtension.event; + get onInstallExtension() { return this._onInstallExtension.event; } protected readonly _onDidInstallExtensions = this._register(new Emitter()); - readonly onDidInstallExtensions = this._onDidInstallExtensions.event; + get onDidInstallExtensions() { return this._onDidInstallExtensions.event; } protected readonly _onUninstallExtension = this._register(new Emitter()); - readonly onUninstallExtension = this._onUninstallExtension.event; + get onUninstallExtension() { return this._onUninstallExtension.event; } protected _onDidUninstallExtension = this._register(new Emitter()); - readonly onDidUninstallExtension = this._onDidUninstallExtension.event; + get onDidUninstallExtension() { return this._onDidUninstallExtension.event; } private readonly participants: IExtensionManagementParticipant[] = []; diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index e523486bdbd..e9a3651cb50 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -713,10 +713,6 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi } async query(options: IQueryOptions, token: CancellationToken): Promise> { - if (!this.isEnabled()) { - throw new Error('No extension gallery service configured.'); - } - let text = options.text || ''; const pageSize = options.pageSize ?? 50; diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index 3b4958cba2b..7ab51f1d2cd 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -496,7 +496,6 @@ export interface IExtensionTipsService { export const ExtensionsLabel = localize('extensions', "Extensions"); export const ExtensionsLocalizedLabel = { value: ExtensionsLabel, original: 'Extensions' }; -export const ExtensionsChannelId = 'extensions'; export const PreferencesLabel = localize('preferences', "Preferences"); export const PreferencesLocalizedLabel = { value: PreferencesLabel, original: 'Preferences' }; diff --git a/src/vs/platform/extensionManagement/electron-sandbox/extensionsScannerService.ts b/src/vs/platform/extensionManagement/electron-sandbox/extensionsScannerService.ts index c13b4c23371..f99ba24598c 100644 --- a/src/vs/platform/extensionManagement/electron-sandbox/extensionsScannerService.ts +++ b/src/vs/platform/extensionManagement/electron-sandbox/extensionsScannerService.ts @@ -8,7 +8,7 @@ import { INativeEnvironmentService } from 'vs/platform/environment/common/enviro import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; import { IExtensionsScannerService, NativeExtensionsScannerService, } from 'vs/platform/extensionManagement/common/extensionsScannerService'; import { IFileService } from 'vs/platform/files/common/files'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -35,4 +35,4 @@ export class ExtensionsScannerService extends NativeExtensionsScannerService imp } -registerSingleton(IExtensionsScannerService, ExtensionsScannerService, true); +registerSingleton(IExtensionsScannerService, ExtensionsScannerService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/extensionResourceLoader/browser/extensionResourceLoaderService.ts b/src/vs/platform/extensionResourceLoader/browser/extensionResourceLoaderService.ts similarity index 90% rename from src/vs/workbench/services/extensionResourceLoader/browser/extensionResourceLoaderService.ts rename to src/vs/platform/extensionResourceLoader/browser/extensionResourceLoaderService.ts index de8912cf33d..e2bc58ae980 100644 --- a/src/vs/workbench/services/extensionResourceLoader/browser/extensionResourceLoaderService.ts +++ b/src/vs/platform/extensionResourceLoader/browser/extensionResourceLoaderService.ts @@ -4,15 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; -import { AbstractExtensionResourceLoaderService, IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; import { FileAccess, Schemas } from 'vs/base/common/network'; import { IProductService } from 'vs/platform/product/common/productService'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ILogService } from 'vs/platform/log/common/log'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { AbstractExtensionResourceLoaderService, IExtensionResourceLoaderService } from 'vs/platform/extensionResourceLoader/common/extensionResourceLoader'; class ExtensionResourceLoaderService extends AbstractExtensionResourceLoaderService { @@ -52,4 +52,4 @@ class ExtensionResourceLoaderService extends AbstractExtensionResourceLoaderServ } } -registerSingleton(IExtensionResourceLoaderService, ExtensionResourceLoaderService, true); +registerSingleton(IExtensionResourceLoaderService, ExtensionResourceLoaderService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader.ts b/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts similarity index 100% rename from src/vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader.ts rename to src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts diff --git a/src/vs/workbench/services/extensionResourceLoader/electron-sandbox/extensionResourceLoaderService.ts b/src/vs/platform/extensionResourceLoader/electron-sandbox/extensionResourceLoaderService.ts similarity index 89% rename from src/vs/workbench/services/extensionResourceLoader/electron-sandbox/extensionResourceLoaderService.ts rename to src/vs/platform/extensionResourceLoader/electron-sandbox/extensionResourceLoaderService.ts index 3b30c2dc1f3..c6e63dc3aec 100644 --- a/src/vs/workbench/services/extensionResourceLoader/electron-sandbox/extensionResourceLoaderService.ts +++ b/src/vs/platform/extensionResourceLoader/electron-sandbox/extensionResourceLoaderService.ts @@ -4,15 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; -import { AbstractExtensionResourceLoaderService, IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; import { IProductService } from 'vs/platform/product/common/productService'; import { asTextOrError, IRequestService } from 'vs/platform/request/common/request'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { AbstractExtensionResourceLoaderService, IExtensionResourceLoaderService } from 'vs/platform/extensionResourceLoader/common/extensionResourceLoader'; export class ExtensionResourceLoaderService extends AbstractExtensionResourceLoaderService { @@ -39,4 +39,4 @@ export class ExtensionResourceLoaderService extends AbstractExtensionResourceLoa } -registerSingleton(IExtensionResourceLoaderService, ExtensionResourceLoaderService, true); +registerSingleton(IExtensionResourceLoaderService, ExtensionResourceLoaderService, InstantiationType.Delayed); diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index 267435d090a..1fb311969e8 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -268,6 +268,7 @@ export interface IRelaxedExtensionManifest { description?: string; main?: string; browser?: string; + preview?: boolean; // For now this only supports pointing to l10n bundle files // but it will be used for package.l10n.json files in the future l10n?: string; diff --git a/src/vs/platform/instantiation/common/extensions.ts b/src/vs/platform/instantiation/common/extensions.ts index 474a9a59ea1..6b3cb6ac484 100644 --- a/src/vs/platform/instantiation/common/extensions.ts +++ b/src/vs/platform/instantiation/common/extensions.ts @@ -22,7 +22,7 @@ export const enum InstantiationType { Delayed = 1 } -export function registerSingleton(id: ServiceIdentifier, ctor: new (...services: Services) => T, supportsDelayedInstantiation: boolean | InstantiationType): void; +export function registerSingleton(id: ServiceIdentifier, ctor: new (...services: Services) => T, supportsDelayedInstantiation: false | InstantiationType): void; export function registerSingleton(id: ServiceIdentifier, descriptor: SyncDescriptor): void; export function registerSingleton(id: ServiceIdentifier, ctorOrDescriptor: { new(...services: Services): T } | SyncDescriptor, supportsDelayedInstantiation?: boolean | InstantiationType): void { if (!(ctorOrDescriptor instanceof SyncDescriptor)) { diff --git a/src/vs/platform/instantiation/common/instantiationService.ts b/src/vs/platform/instantiation/common/instantiationService.ts index 21908bbcff0..bf9deb002c5 100644 --- a/src/vs/platform/instantiation/common/instantiationService.ts +++ b/src/vs/platform/instantiation/common/instantiationService.ts @@ -4,11 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { IdleValue } from 'vs/base/common/async'; +import { Event } from 'vs/base/common/event'; import { illegalState } from 'vs/base/common/errors'; +import { toDisposable } from 'vs/base/common/lifecycle'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { Graph } from 'vs/platform/instantiation/common/graph'; import { IInstantiationService, ServiceIdentifier, ServicesAccessor, _util } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { LinkedList } from 'vs/base/common/linkedList'; // TRACING const _enableAllTracing = false @@ -111,7 +114,7 @@ export class InstantiationService implements IInstantiationService { } // now create the instance - return new ctor(...[...args, ...serviceArgs]); + return Reflect.construct(ctor, args.concat(serviceArgs)); } private _setServiceInstance(id: ServiceIdentifier, instance: T): void { @@ -246,15 +249,52 @@ export class InstantiationService implements IInstantiationService { // Return a proxy object that's backed by an idle value. That // strategy is to instantiate services in our idle time or when actually // needed but not when injected into a consumer + + // return "empty events" when the service isn't instantiated yet + const earlyListeners = new Map>>>(); + const idle = new IdleValue(() => { const result = child._createInstance(ctor, args, _trace); + + // early listeners that we kept are now being subscribed to + // the real service + for (const [key, values] of earlyListeners) { + const candidate = >(result)[key]; + if (typeof candidate === 'function') { + for (const listener of values) { + candidate.apply(result, listener); + } + } + } + earlyListeners.clear(); + return result; }); return new Proxy(Object.create(null), { get(target: any, key: PropertyKey): any { + + if (!idle.isInitialized) { + // looks like an event + if (typeof key === 'string' && (key.startsWith('onDid') || key.startsWith('onWill'))) { + let list = earlyListeners.get(key); + if (!list) { + list = new LinkedList(); + earlyListeners.set(key, list); + } + const event: Event = (callback, thisArg, disposables) => { + const rm = list!.push([callback, thisArg, disposables]); + return toDisposable(rm); + }; + return event; + } + } + + // value already exists if (key in target) { return target[key]; } + + // create value const obj = idle.value; let prop = obj[key]; if (typeof prop !== 'function') { diff --git a/src/vs/platform/instantiation/test/common/instantiationService.test.ts b/src/vs/platform/instantiation/test/common/instantiationService.test.ts index a737b46533c..fabe3100318 100644 --- a/src/vs/platform/instantiation/test/common/instantiationService.test.ts +++ b/src/vs/platform/instantiation/test/common/instantiationService.test.ts @@ -4,6 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { Emitter, Event } from 'vs/base/common/event'; +import { dispose } from 'vs/base/common/lifecycle'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { createDecorator, IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; @@ -459,4 +461,67 @@ suite('Instantiation Service', () => { assert.strictEqual(cycle, 'A -> B -> A'); } }); + + test('Delayed and events', function () { + const A = createDecorator('A'); + interface A { + _serviceBrand: undefined; + onDidDoIt: Event; + doIt(): void; + } + + let created = false; + class AImpl implements A { + _serviceBrand: undefined; + _doIt = 0; + + _onDidDoIt = new Emitter(); + onDidDoIt: Event = this._onDidDoIt.event; + + constructor() { + created = true; + } + + doIt(): void { + this._doIt += 1; + this._onDidDoIt.fire(this); + } + } + + const insta = new InstantiationService(new ServiceCollection( + [A, new SyncDescriptor(AImpl, undefined, true)], + ), true, undefined, true); + + class Consumer { + constructor(@A readonly a: A) { + // eager subscribe -> NO service instance + } + } + + const c: Consumer = insta.createInstance(Consumer); + let eventCount = 0; + + // subscribing to event doesn't trigger instantiation + const listener = (e: any) => { + assert.ok(e instanceof AImpl); + eventCount++; + }; + const d1 = c.a.onDidDoIt(listener); + const d2 = c.a.onDidDoIt(listener); + assert.strictEqual(created, false); + assert.strictEqual(eventCount, 0); + d2.dispose(); + + // instantiation happens on first call + c.a.doIt(); + assert.strictEqual(created, true); + assert.strictEqual(eventCount, 1); + + + const d3 = c.a.onDidDoIt(listener); + c.a.doIt(); + assert.strictEqual(eventCount, 3); + + dispose([d1, d3]); + }); }); diff --git a/src/vs/platform/keybinding/common/keybindingResolver.ts b/src/vs/platform/keybinding/common/keybindingResolver.ts index a76a8a8da14..636504d11fc 100644 --- a/src/vs/platform/keybinding/common/keybindingResolver.ts +++ b/src/vs/platform/keybinding/common/keybindingResolver.ts @@ -70,7 +70,10 @@ export class KeybindingResolver { if (keypressChordPart && defaultKb.keypressParts[1] !== keypressChordPart) { return false; } - if (when) { + + // `true` means always, as does `undefined` + // so we will treat `true` === `undefined` + if (when && when.type !== ContextKeyExprType.True) { if (!defaultKb.when) { return false; } diff --git a/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts b/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts index 77935d73c2c..137d23986ad 100644 --- a/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts +++ b/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts @@ -235,6 +235,28 @@ suite('KeybindingResolver', () => { ]); }); + test('issue #157751: Auto-quoting of context keys prevents removal of keybindings via UI', () => { + const defaults = [ + kbItem(KeyCode.KeyA, 'command1', null, ContextKeyExpr.deserialize(`editorTextFocus && activeEditor != workbench.editor.notebook && editorLangId in julia.supportedLanguageIds`), true), + ]; + const overrides = [ + kbItem(KeyCode.KeyA, '-command1', null, ContextKeyExpr.deserialize(`editorTextFocus && activeEditor != 'workbench.editor.notebook' && editorLangId in 'julia.supportedLanguageIds'`), false), + ]; + const actual = KeybindingResolver.handleRemovals([...defaults, ...overrides]); + assert.deepStrictEqual(actual, []); + }); + + test('issue #160604: Remove keybindings with when clause does not work', () => { + const defaults = [ + kbItem(KeyCode.KeyA, 'command1', null, undefined, true), + ]; + const overrides = [ + kbItem(KeyCode.KeyA, '-command1', null, ContextKeyExpr.true(), false), + ]; + const actual = KeybindingResolver.handleRemovals([...defaults, ...overrides]); + assert.deepStrictEqual(actual, []); + }); + test('contextIsEntirelyIncluded', () => { const toContextKeyExpression = (expr: ContextKeyExpression | string | null) => { if (typeof expr === 'string' || !expr) { diff --git a/src/vs/platform/languagePacks/browser/languagePacks.ts b/src/vs/platform/languagePacks/browser/languagePacks.ts index 83b4c051e0f..f63b7b552fa 100644 --- a/src/vs/platform/languagePacks/browser/languagePacks.ts +++ b/src/vs/platform/languagePacks/browser/languagePacks.ts @@ -3,9 +3,74 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { Language } from 'vs/base/common/platform'; +import { URI } from 'vs/base/common/uri'; +import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionResourceLoaderService } from 'vs/platform/extensionResourceLoader/common/extensionResourceLoader'; import { ILanguagePackItem, LanguagePackBaseService } from 'vs/platform/languagePacks/common/languagePacks'; +import { ILogService } from 'vs/platform/log/common/log'; export class WebLanguagePacksService extends LanguagePackBaseService { + constructor( + @IExtensionResourceLoaderService private readonly extensionResourceLoaderService: IExtensionResourceLoaderService, + @IExtensionGalleryService extensionGalleryService: IExtensionGalleryService, + @ILogService private readonly logService: ILogService + ) { + super(extensionGalleryService); + } + + async getBuiltInExtensionTranslationsUri(id: string): Promise { + + const queryTimeout = new CancellationTokenSource(); + setTimeout(() => queryTimeout.cancel(), 1000); + + // First get the extensions that supports the language (there should only be one but just in case let's include more results) + let result; + try { + result = await this.extensionGalleryService.query({ + text: `tag:"lp-${Language.value()}"`, + pageSize: 5 + }, queryTimeout.token); + } catch (err) { + this.logService.error(err); + return undefined; + } + + const languagePackExtensions = result.firstPage.find(e => e.properties.localizedLanguages?.length); + if (!languagePackExtensions) { + this.logService.trace(`No language pack found for language ${Language.value()}`); + return undefined; + } + + // Then get the manifest for that extension + const manifestTimeout = new CancellationTokenSource(); + setTimeout(() => queryTimeout.cancel(), 1000); + const manifest = await this.extensionGalleryService.getManifest(languagePackExtensions, manifestTimeout.token); + + // Find the translation from the language pack + const localization = manifest?.contributes?.localizations?.find(l => l.languageId === Language.value()); + const translation = localization?.translations.find(t => t.id === id); + if (!translation) { + this.logService.trace(`No translation found for id '${id}, in ${manifest?.name}`); + return undefined; + } + + // get the resource uri and return it + const uri = this.extensionResourceLoaderService.getExtensionGalleryResourceURL({ + // If translation is defined then manifest should have been defined. + name: manifest!.name, + publisher: manifest!.publisher, + version: manifest!.version + }); + if (!uri) { + this.logService.trace('Gallery does not provide extension resources.'); + return undefined; + } + + return URI.joinPath(uri, translation.path); + } + // Web doesn't have a concept of language packs, so we just return an empty array getInstalledLanguages(): Promise { return Promise.resolve([]); diff --git a/src/vs/platform/languagePacks/common/languagePacks.ts b/src/vs/platform/languagePacks/common/languagePacks.ts index d316ba89c3e..93f337da2ff 100644 --- a/src/vs/platform/languagePacks/common/languagePacks.ts +++ b/src/vs/platform/languagePacks/common/languagePacks.ts @@ -6,6 +6,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Disposable } from 'vs/base/common/lifecycle'; import { language } from 'vs/base/common/platform'; +import { URI } from 'vs/base/common/uri'; import { IQuickPickItem } from 'vs/base/parts/quickinput/common/quickInput'; import { localize } from 'vs/nls'; import { IExtensionGalleryService, IGalleryExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; @@ -22,16 +23,19 @@ export interface ILanguagePackService { readonly _serviceBrand: undefined; getAvailableLanguages(): Promise>; getInstalledLanguages(): Promise>; + getBuiltInExtensionTranslationsUri(id: string): Promise; getLocale(extension: IGalleryExtension): string | undefined; } export abstract class LanguagePackBaseService extends Disposable implements ILanguagePackService { declare readonly _serviceBrand: undefined; - constructor(@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService) { + constructor(@IExtensionGalleryService protected readonly extensionGalleryService: IExtensionGalleryService) { super(); } + abstract getBuiltInExtensionTranslationsUri(id: string): Promise; + abstract getInstalledLanguages(): Promise>; async getAvailableLanguages(): Promise { diff --git a/src/vs/platform/languagePacks/node/languagePacks.ts b/src/vs/platform/languagePacks/node/languagePacks.ts index defe0957b17..db283b29318 100644 --- a/src/vs/platform/languagePacks/node/languagePacks.ts +++ b/src/vs/platform/languagePacks/node/languagePacks.ts @@ -16,6 +16,8 @@ import { areSameExtensions } from 'vs/platform/extensionManagement/common/extens import { ILogService } from 'vs/platform/log/common/log'; import { ILocalizationContribution } from 'vs/platform/extensions/common/extensions'; import { ILanguagePackItem, LanguagePackBaseService } from 'vs/platform/languagePacks/common/languagePacks'; +import { Language } from 'vs/base/common/platform'; +import { URI } from 'vs/base/common/uri'; interface ILanguagePack { hash: string; @@ -48,6 +50,18 @@ export class NativeLanguagePackService extends LanguagePackBaseService { }); } + async getBuiltInExtensionTranslationsUri(id: string): Promise { + const packs = await this.cache.getLanguagePacks(); + const pack = packs[Language.value()]; + if (!pack) { + this.logService.warn(`No language pack found for ${Language.value()}`); + return undefined; + } + + const translation = pack.translations[id]; + return translation ? URI.file(translation) : undefined; + } + async getInstalledLanguages(): Promise> { const languagePacks = await this.cache.getLanguagePacks(); const languages = Object.keys(languagePacks).map(locale => { diff --git a/src/vs/platform/lifecycle/electron-browser/sharedProcessLifecycleService.ts b/src/vs/platform/lifecycle/electron-browser/sharedProcessLifecycleService.ts new file mode 100644 index 00000000000..ebcc62825c1 --- /dev/null +++ b/src/vs/platform/lifecycle/electron-browser/sharedProcessLifecycleService.ts @@ -0,0 +1,76 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Promises } from 'vs/base/common/async'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ILogService } from 'vs/platform/log/common/log'; + +export const ISharedProcessLifecycleService = createDecorator('lifecycleSharedProcessService'); + +export interface ISharedProcessLifecycleService { + readonly _serviceBrand: undefined; + + /** + * An event that fires after after no window has vetoed the shutdown sequence. At + * this point listeners are ensured that the application will quit without veto. + */ + readonly onWillShutdown: Event; +} + +export interface ShutdownEvent { + + /** + * Allows to join the shutdown. The promise can be a long running operation but it + * will block the application from closing. + */ + join(promise: Promise): void; +} + +export class SharedProcessLifecycleService extends Disposable implements ISharedProcessLifecycleService { + + declare readonly _serviceBrand: undefined; + + private pendingWillShutdownPromise: Promise | undefined = undefined; + + private readonly _onWillShutdown = this._register(new Emitter()); + readonly onWillShutdown = this._onWillShutdown.event; + + constructor( + @ILogService private readonly logService: ILogService + ) { + super(); + } + + public fireOnWillShutdown(): Promise { + if (this.pendingWillShutdownPromise) { + return this.pendingWillShutdownPromise; // shutdown is already running + } + + this.logService.trace('Lifecycle#onWillShutdown.fire()'); + + const joiners: Promise[] = []; + + this._onWillShutdown.fire({ + join(promise) { + joiners.push(promise); + } + }); + + this.pendingWillShutdownPromise = (async () => { + + // Settle all shutdown event joiners + try { + await Promises.settled(joiners); + } catch (error) { + this.logService.error(error); + } + })(); + + return this.pendingWillShutdownPromise; + } + +} diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 197fb970f66..5fb143fc948 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -1291,7 +1291,7 @@ class WorkbenchTreeInternals { updateStyleOverrides(overrideStyles?: IColorMapping): void { dispose(this.styler); - this.styler = overrideStyles ? attachListStyler(this.tree, this.themeService, overrideStyles) : Disposable.None; + this.styler = attachListStyler(this.tree, this.themeService, overrideStyles); } dispose(): void { diff --git a/src/vs/platform/log/common/fileLog.ts b/src/vs/platform/log/common/fileLog.ts index 0be7b474679..e16363b8b6e 100644 --- a/src/vs/platform/log/common/fileLog.ts +++ b/src/vs/platform/log/common/fileLog.ts @@ -8,7 +8,6 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { basename, dirname, joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { ByteSize, FileOperationError, FileOperationResult, IFileService, whenProviderRegistered } from 'vs/platform/files/common/files'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { BufferLogService } from 'vs/platform/log/common/bufferLog'; import { AbstractLogger, AbstractLoggerService, format, ILogger, ILoggerOptions, ILoggerService, ILogService, LogLevel } from 'vs/platform/log/common/log'; @@ -21,7 +20,7 @@ export class FileLogger extends AbstractLogger implements ILogger { private backupIndex: number = 1; constructor( - private readonly name: string, + name: string, private readonly resource: URI, level: LogLevel, private readonly donotUseFormatters: boolean, @@ -101,7 +100,7 @@ export class FileLogger extends AbstractLogger implements ILogger { if (this.donotUseFormatters) { content += message; } else { - content += `[${this.getCurrentTimestamp()}] [${this.name}] [${this.stringifyLogLevel(level)}] ${message}\n`; + content += `${this.getCurrentTimestamp()} [${this.stringifyLogLevel(level)}] ${message}\n`; } await this.fileService.writeFile(this.resource, VSBuffer.fromString(content)); }); @@ -146,7 +145,6 @@ export class FileLoggerService extends AbstractLoggerService implements ILoggerS constructor( @ILogService logService: ILogService, - @IInstantiationService private readonly instantiationService: IInstantiationService, @IFileService private readonly fileService: IFileService, ) { super(logService.getLevel(), logService.onDidChangeLogLevel); @@ -154,7 +152,7 @@ export class FileLoggerService extends AbstractLoggerService implements ILoggerS protected doCreateLogger(resource: URI, logLevel: LogLevel, options?: ILoggerOptions): ILogger { const logger = new BufferLogService(logLevel); - whenProviderRegistered(resource, this.fileService).then(() => (logger).logger = this.instantiationService.createInstance(FileLogger, options?.name || basename(resource), resource, logger.getLevel(), !!options?.donotUseFormatters)); + whenProviderRegistered(resource, this.fileService).then(() => (logger).logger = new FileLogger(options?.name || basename(resource), resource, logger.getLevel(), !!options?.donotUseFormatters, this.fileService)); return logger; } } diff --git a/src/vs/platform/log/common/log.ts b/src/vs/platform/log/common/log.ts index 88cc084a202..cc0cb8142e9 100644 --- a/src/vs/platform/log/common/log.ts +++ b/src/vs/platform/log/common/log.ts @@ -6,6 +6,7 @@ import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { ResourceMap } from 'vs/base/common/map'; import { isWindows } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -111,12 +112,22 @@ export interface ILoggerService { /** * Creates a logger, or gets one if it already exists. */ - createLogger(file: URI, options?: ILoggerOptions): ILogger; + createLogger(resource: URI, options?: ILoggerOptions, logLevel?: LogLevel): ILogger; /** * Gets an existing logger, if any. */ - getLogger(file: URI): ILogger | undefined; + getLogger(resource: URI): ILogger | undefined; + + /** + * Set log level for a logger. + */ + setLevel(resource: URI, level: LogLevel | undefined): void; + + /** + * Get log level for a logger. + */ + getLogLevel(resource: URI): LogLevel | undefined; } export abstract class AbstractLogger extends Disposable { @@ -504,52 +515,81 @@ export class LogService extends Disposable implements ILogService { } } +interface ILoggerItem { + readonly logger: ILogger; + logLevel: LogLevel | undefined; +} + export abstract class AbstractLoggerService extends Disposable implements ILoggerService { declare readonly _serviceBrand: undefined; - private readonly loggers = new Map(); - private readonly logLevelChangeableLoggers: ILogger[] = []; + private readonly loggerItems = new ResourceMap(); constructor( private logLevel: LogLevel, onDidChangeLogLevel: Event, ) { super(); - this._register(onDidChangeLogLevel(logLevel => { - this.logLevel = logLevel; - this.logLevelChangeableLoggers.forEach(logger => logger.setLevel(logLevel)); - })); + this._register(onDidChangeLogLevel(logLevel => this.setLevel(logLevel))); } - getLogger(resource: URI) { - return this.loggers.get(resource.toString()); + getLoggers(): ILogger[] { + return [...this.loggerItems.values()].map(({ logger }) => logger); } - createLogger(resource: URI, options?: ILoggerOptions): ILogger { - let logger = this.loggers.get(resource.toString()); + getLogger(resource: URI): ILogger | undefined { + return this.loggerItems.get(resource)?.logger; + } + + createLogger(resource: URI, options?: ILoggerOptions, logLevel?: LogLevel): ILogger { + let logger = this.loggerItems.get(resource)?.logger; if (!logger) { - logger = this.doCreateLogger(resource, options?.always ? LogLevel.Trace : this.logLevel, options); - this.loggers.set(resource.toString(), logger); - if (!options?.always) { - this.logLevelChangeableLoggers.push(logger); - } + logLevel = options?.always ? LogLevel.Trace : logLevel; + logger = this.doCreateLogger(resource, logLevel ?? this.logLevel, options); + this.loggerItems.set(resource, { logger, logLevel }); } return logger; } + setLevel(logLevel: LogLevel): void; + setLevel(resource: URI, logLevel: LogLevel): void; + setLevel(arg1: any, arg2?: any): void { + const resource = URI.isUri(arg1) ? arg1 : undefined; + const logLevel = resource ? arg2 : arg1; + + if (resource) { + const logger = this.loggerItems.get(resource); + if (logger && logger.logLevel !== logLevel) { + logger.logLevel = logLevel; + logger.logger.setLevel(logLevel); + } + } else { + this.logLevel = logLevel; + this.loggerItems.forEach(({ logLevel, logger }) => { + if (logLevel === undefined) { + logger.setLevel(this.logLevel); + } + }); + } + + } + + getLogLevel(resource: URI): LogLevel | undefined { + const logger = this.loggerItems.get(resource); + return logger?.logLevel; + } + override dispose(): void { - this.logLevelChangeableLoggers.splice(0, this.logLevelChangeableLoggers.length); - this.loggers.forEach(logger => logger.dispose()); - this.loggers.clear(); + this.loggerItems.forEach(({ logger }) => logger.dispose()); + this.loggerItems.clear(); super.dispose(); } protected abstract doCreateLogger(resource: URI, logLevel: LogLevel, options?: ILoggerOptions): ILogger; } -export class NullLogService implements ILogService { - declare readonly _serviceBrand: undefined; +export class NullLogger implements ILogger { readonly onDidChangeLogLevel: Event = new Emitter().event; setLevel(level: LogLevel): void { } getLevel(): LogLevel { return LogLevel.Info; } @@ -563,6 +603,19 @@ export class NullLogService implements ILogService { flush(): void { } } +export class NullLogService extends NullLogger implements ILogService { + declare readonly _serviceBrand: undefined; +} + +export class NullLoggerService extends AbstractLoggerService { + + constructor() { super(LogLevel.Info, Event.None); } + + protected doCreateLogger(resource: URI, logLevel: LogLevel, options?: ILoggerOptions | undefined): ILogger { + return new NullLogger(); + } +} + export function getLogLevel(environmentService: IEnvironmentService): LogLevel { if (environmentService.verbose) { return LogLevel.Trace; diff --git a/src/vs/platform/log/common/logIpc.ts b/src/vs/platform/log/common/logIpc.ts index 8783df9cbb7..84e6b347ddb 100644 --- a/src/vs/platform/log/common/logIpc.ts +++ b/src/vs/platform/log/common/logIpc.ts @@ -12,8 +12,11 @@ export class LogLevelChannel implements IServerChannel { onDidChangeLogLevel: Event; - constructor(private service: ILogService) { - this.onDidChangeLogLevel = Event.buffer(service.onDidChangeLogLevel, true); + constructor( + private readonly logService: ILogService, + private readonly loggerService: ILoggerService + ) { + this.onDidChangeLogLevel = Event.buffer(logService.onDidChangeLogLevel, true); } listen(_: unknown, event: string): Event { @@ -26,7 +29,7 @@ export class LogLevelChannel implements IServerChannel { async call(_: unknown, command: string, arg?: any): Promise { switch (command) { - case 'setLevel': return this.service.setLevel(arg); + case 'setLevel': return arg[1] ? this.loggerService.setLevel(URI.revive(arg[1]), arg[0]) : this.logService.setLevel(arg[0]); } throw new Error(`Call not found: ${command}`); @@ -42,12 +45,12 @@ export class LogLevelChannelClient { return this.channel.listen('onDidChangeLogLevel'); } - setLevel(level: LogLevel): void { - LogLevelChannelClient.setLevel(this.channel, level); + setLevel(level: LogLevel, resource?: URI): void { + LogLevelChannelClient.setLevel(this.channel, level, resource); } - public static setLevel(channel: IChannel, level: LogLevel): Promise { - return channel.call('setLevel', level); + public static setLevel(channel: IChannel, level: LogLevel, resource?: URI): Promise { + return channel.call('setLevel', [level, resource]); } } diff --git a/src/vs/platform/log/node/loggerService.ts b/src/vs/platform/log/node/loggerService.ts index 8ad7ad94f48..9e1c2e72581 100644 --- a/src/vs/platform/log/node/loggerService.ts +++ b/src/vs/platform/log/node/loggerService.ts @@ -3,30 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Schemas } from 'vs/base/common/network'; -import { basename } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; -import { IFileService } from 'vs/platform/files/common/files'; -import { FileLogger } from 'vs/platform/log/common/fileLog'; import { AbstractLoggerService, ILogger, ILoggerOptions, ILoggerService, ILogService, LogLevel } from 'vs/platform/log/common/log'; import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog'; export class LoggerService extends AbstractLoggerService implements ILoggerService { constructor( - @ILogService logService: ILogService, - @IFileService private readonly fileService: IFileService + @ILogService logService: ILogService ) { super(logService.getLevel(), logService.onDidChangeLogLevel); } protected doCreateLogger(resource: URI, logLevel: LogLevel, options?: ILoggerOptions): ILogger { - if (resource.scheme === Schemas.file) { - return new SpdLogLogger(options?.name || generateUuid(), resource.fsPath, !options?.donotRotate, !!options?.donotUseFormatters, logLevel); - } else { - return new FileLogger(options?.name ?? basename(resource), resource, logLevel, !!options?.donotUseFormatters, this.fileService); - } + return new SpdLogLogger(options?.name || generateUuid(), resource.fsPath, !options?.donotRotate, !!options?.donotUseFormatters, logLevel); } } diff --git a/src/vs/platform/log/node/spdlogLog.ts b/src/vs/platform/log/node/spdlogLog.ts index af7a576df1c..b00a30a3dbf 100644 --- a/src/vs/platform/log/node/spdlogLog.ts +++ b/src/vs/platform/log/node/spdlogLog.ts @@ -15,6 +15,8 @@ async function createSpdLogLogger(name: string, logfilePath: string, filesize: n const logger = await _spdlog.createAsyncRotatingLogger(name, logfilePath, filesize, filecount); if (donotUseFormatters) { logger.clearFormatters(); + } else { + logger.setPattern('%Y-%m-%d %H:%M:%S.%e [%l] %v'); } return logger; } catch (e) { diff --git a/src/vs/platform/native/common/native.ts b/src/vs/platform/native/common/native.ts index 2a87cf7d285..d2321eeef5b 100644 --- a/src/vs/platform/native/common/native.ts +++ b/src/vs/platform/native/common/native.ts @@ -163,6 +163,11 @@ export interface ICommonNativeHostService { toggleSharedProcessWindow(): Promise; sendInputEvent(event: MouseInputEvent): Promise; + // Perf Introspection + startHeartbeat(session: string): Promise; + sendHeartbeat(session: string): Promise; + stopHeartbeat(session: string): Promise; + // Connectivity resolveProxy(url: string): Promise; findFreePort(startPort: number, giveUpAfter: number, timeout: number, stride?: number): Promise; diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index d5da363f058..8bc07ced50c 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -41,6 +41,8 @@ import { isWorkspaceIdentifier, toWorkspaceIdentifier } from 'vs/platform/worksp import { IWorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService'; import { VSBuffer } from 'vs/base/common/buffer'; import { hasWSLFeatureInstalled } from 'vs/platform/remote/node/wsl'; +import { WindowProfiler } from 'vs/platform/profiling/electron-main/windowProfiling'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; export interface INativeHostMainService extends AddFirstParameterToFunctions /* only methods, not events */, number | undefined /* window ID */> { } @@ -59,7 +61,8 @@ export class NativeHostMainService extends Disposable implements INativeHostMain @ILogService private readonly logService: ILogService, @IProductService private readonly productService: IProductService, @IThemeMainService private readonly themeMainService: IThemeMainService, - @IWorkspacesManagementMainService private readonly workspacesManagementMainService: IWorkspacesManagementMainService + @IWorkspacesManagementMainService private readonly workspacesManagementMainService: IWorkspacesManagementMainService, + @ITelemetryService private readonly telemetryService: ITelemetryService, ) { super(); } @@ -777,6 +780,44 @@ export class NativeHostMainService extends Disposable implements INativeHostMain //#endregion + // #region Performance + + private readonly _profilingSessions = new Map(); + + async startHeartbeat(windowId: number | undefined, sessionId: string): Promise { + const win = this.windowById(windowId); + if (!win || !win.win) { + return false; + } + if (!this._profilingSessions.has(win.id)) { + const session = new WindowProfiler(win.win, sessionId, this.logService, this.telemetryService); + this._profilingSessions.set(win.id, session); + session.start(); + } + return true; + } + + async sendHeartbeat(windowId: number | undefined, _sessionId: string): Promise { + const win = this.windowById(windowId); + if (!win || !this._profilingSessions.has(win.id)) { + return false; + } + this._profilingSessions.get(win.id)!.receiveHeartbeat(); + return false; + } + + async stopHeartbeat(windowId: number | undefined, _sessionId: string): Promise { + const win = this.windowById(windowId); + if (!win || !this._profilingSessions.has(win.id)) { + return false; + } + this._profilingSessions.get(win.id)!.stop(); + this._profilingSessions.delete(win.id); + return false; + } + + // #endregion + //#region Registry (windows) diff --git a/src/vs/platform/profiling/common/profiling.ts b/src/vs/platform/profiling/common/profiling.ts index 7c3e9bf142c..c4bfe9ddd24 100644 --- a/src/vs/platform/profiling/common/profiling.ts +++ b/src/vs/platform/profiling/common/profiling.ts @@ -45,10 +45,14 @@ export interface IV8InspectProfilingService { export namespace Utils { + export function isValidProfile(profile: IV8Profile): profile is Required { + return Boolean(profile.samples && profile.timeDeltas); + } + export function rewriteAbsolutePaths(profile: IV8Profile, replace: string = 'noAbsolutePaths') { for (const node of profile.nodes) { if (node.callFrame && node.callFrame.url) { - if (isAbsolute(node.callFrame.url) || /^\w[\w\d+.-]*:\/\/\//.test(node.callFrame.url)) { + if (isAbsolute(node.callFrame.url) || /^\w[\w\d+.-]*:\/\/\/?/.test(node.callFrame.url)) { node.callFrame.url = join(replace, basename(node.callFrame.url)); } } diff --git a/src/vs/platform/profiling/electron-main/windowProfiling.ts b/src/vs/platform/profiling/electron-main/windowProfiling.ts new file mode 100644 index 00000000000..c2469272bac --- /dev/null +++ b/src/vs/platform/profiling/electron-main/windowProfiling.ts @@ -0,0 +1,166 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type { Profile, ProfileResult } from 'v8-inspect-profiler'; +import { BrowserWindow } from 'electron'; +import { timeout } from 'vs/base/common/async'; +import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { ILogService } from 'vs/platform/log/common/log'; +import { Promises } from 'vs/base/node/pfs'; +import { tmpdir } from 'os'; +import { join } from 'vs/base/common/path'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { Utils } from 'vs/platform/profiling/common/profiling'; +import { bottomUp, } from 'vs/platform/profiling/node/profilingModel'; + + +type TelemetrySampleData = { + sessionId: string; + selfTime: number; + totalTime: number; + percentage: number; + functionName: string; + callstack: string; +}; + +type TelemetrySampleDataClassification = { + owner: 'jrieken'; + comment: 'A callstack that took a long time to execute'; + sessionId: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Session identifier that allows to correlate samples from one profile' }; + selfTime: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'Self time of the sample' }; + totalTime: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'Total time of the sample' }; + percentage: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'Relative time (percentage) of the sample' }; + functionName: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The name of the sample' }; + callstack: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth'; comment: 'The stacktrace leading into the sample' }; +}; + +export class WindowProfiler { + + private _profileAtOrAfter: number = 0; + private _session = new DisposableStore(); + private _isProfiling?: Promise; + + private _isStarted: boolean = false; + + constructor( + private readonly _window: BrowserWindow, + private readonly _sessionId: string, + @ILogService private readonly _logService: ILogService, + @ITelemetryService private readonly _telemetryService: ITelemetryService, + ) { + // noop + } + + async stop() { + + await this._isProfiling; + + this._logService.warn('[perf] STOPPING to monitor renderer', this._sessionId); + this._session.clear(); + + try { + const inspector = this._window.webContents.debugger; + await inspector.sendCommand('Profiler.disable'); + inspector.detach(); + } catch (error) { + this._logService.error('[perf] FAILED to disable profiler', this._sessionId); + } + } + + receiveHeartbeat(): void { + this._profileAtOrAfter = Date.now() + 1000; + // this._logService.info('[perf] received heartbeat', this.id); + } + + async start() { + if (this._isStarted) { + this._logService.warn('[perf] already STARTED, ignoring request', this._sessionId); + return; + } + + try { + const inspector = this._window.webContents.debugger; + inspector.attach(); + await inspector.sendCommand('Profiler.enable'); + } catch (error) { + this._logService.error('[perf] FAILED to enable profiler', this._sessionId); + return; + } + + this._logService.warn('[perf] started to EXPECT frequent heartbeat', this._sessionId); + + this._session.clear(); + this._profileAtOrAfter = Date.now(); + + const handle = setInterval(() => { + if (Date.now() >= this._profileAtOrAfter) { + clearInterval(handle); + this._captureRendererProfile(); + } + }, 500); + + this._session.add(toDisposable(() => { + this._isStarted = false; + clearInterval(handle); + })); + } + + + private async _captureRendererProfile(): Promise { + this._logService.warn('[perf] MISSED heartbeat, trying to profile renderer', this._sessionId); + + const profiling = (async () => { + const inspector = this._window.webContents.debugger; + await inspector.sendCommand('Profiler.start'); + this._logService.warn('[perf] profiling STARTED', this._sessionId); + await timeout(5000); + const res: ProfileResult = await inspector.sendCommand('Profiler.stop'); + this._logService.warn('[perf] profiling DONE', this._sessionId); + await this._store(res.profile); + this._digest(res.profile); + })(); + + this._isProfiling = profiling + .catch(err => { + this._logService.error('[perf] profiling the renderer FAILED', this._sessionId); + this._logService.error(err); + }).finally(() => { + this._isProfiling = undefined; + }); + } + + private async _store(profile: Profile): Promise { + try { + const path = join(tmpdir(), `renderer-profile-${Date.now()}.cpuprofile`); + await Promises.writeFile(path, JSON.stringify(profile)); + this._logService.info('[perf] stored profile to DISK', this._sessionId, path); + } catch (error) { + this._logService.error('[perf] FAILED to write profile to disk', this._sessionId, error); + } + } + + private _digest(profile: Profile): void { + // https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#type-Profile + + if (!Utils.isValidProfile(profile)) { + this._logService.warn('[perf] INVALID profile: no samples or timeDeltas', this._sessionId); + return; + } + + const samples = bottomUp(profile, 5, false); + + for (const sample of samples) { + const data: TelemetrySampleData = { + sessionId: this._sessionId, + selfTime: sample.selfTime, + totalTime: sample.totalTime, + percentage: sample.percentage, + functionName: sample.location, + callstack: sample.caller.map(c => `${c.percentage}|${c.location}`).join('<') + }; + this._telemetryService.publicLog2('prof.freeze.sample', data); + } + } +} diff --git a/src/vs/platform/profiling/node/profilingModel.ts b/src/vs/platform/profiling/node/profilingModel.ts new file mode 100644 index 00000000000..fd5978eecec --- /dev/null +++ b/src/vs/platform/profiling/node/profilingModel.ts @@ -0,0 +1,404 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type { Profile, ProfileNode } from 'v8-inspect-profiler'; +import { basename } from 'vs/base/common/path'; + +// #region +// https://github.com/microsoft/vscode-js-profile-visualizer/blob/6e7401128ee860be113a916f80fcfe20ac99418e/packages/vscode-js-profile-core/src/cpu/model.ts#L4 + +interface IProfileModel { + nodes: ReadonlyArray; + locations: ReadonlyArray; + samples: ReadonlyArray; + timeDeltas: ReadonlyArray; + rootPath?: string; + duration: number; +} + +interface IComputedNode { + id: number; + selfTime: number; + aggregateTime: number; + children: number[]; + parent?: number; + locationId: number; +} + +interface ISourceLocation { + lineNumber: number; + columnNumber: number; + // source: Dap.Source; + relativePath?: string; +} + +interface CdpCallFrame { + functionName: string; + scriptId: string; + url: string; + lineNumber: number; + columnNumber: number; +} + +interface CdpPositionTickInfo { + line: number; + ticks: number; +} + +interface INode { + id: number; + // category: Category; + callFrame: CdpCallFrame; + src?: ISourceLocation; +} + +interface ILocation extends INode { + selfTime: number; + aggregateTime: number; + ticks: number; +} + +interface IAnnotationLocation { + callFrame: CdpCallFrame; + locations: ISourceLocation[]; +} + +interface IProfileNode extends ProfileNode { + locationId?: number; + positionTicks?: (CdpPositionTickInfo & { + startLocationId?: number; + endLocationId?: number; + })[]; +} + +interface ICpuProfileRaw extends Profile { + // $vscode?: IJsDebugAnnotations; + nodes: IProfileNode[]; +} + + +/** + * Recursive function that computes and caches the aggregate time for the + * children of the computed now. + */ +const computeAggregateTime = (index: number, nodes: IComputedNode[]): number => { + const row = nodes[index]; + if (row.aggregateTime) { + return row.aggregateTime; + } + + let total = row.selfTime; + for (const child of row.children) { + total += computeAggregateTime(child, nodes); + } + + return (row.aggregateTime = total); +}; + +const ensureSourceLocations = (profile: ICpuProfileRaw): ReadonlyArray => { + + let locationIdCounter = 0; + const locationsByRef = new Map(); + + const getLocationIdFor = (callFrame: CdpCallFrame) => { + const ref = [ + callFrame.functionName, + callFrame.url, + callFrame.scriptId, + callFrame.lineNumber, + callFrame.columnNumber, + ].join(':'); + + const existing = locationsByRef.get(ref); + if (existing) { + return existing.id; + } + const id = locationIdCounter++; + locationsByRef.set(ref, { + id, + callFrame, + location: { + lineNumber: callFrame.lineNumber + 1, + columnNumber: callFrame.columnNumber + 1, + // source: { + // name: maybeFileUrlToPath(callFrame.url), + // path: maybeFileUrlToPath(callFrame.url), + // sourceReference: 0, + // }, + }, + }); + + return id; + }; + + for (const node of profile.nodes) { + node.locationId = getLocationIdFor(node.callFrame); + node.positionTicks = node.positionTicks?.map(tick => ({ + ...tick, + // weirdly, line numbers here are 1-based, not 0-based. The position tick + // only gives line-level granularity, so 'mark' the entire range of source + // code the tick refers to + startLocationId: getLocationIdFor({ + ...node.callFrame, + lineNumber: tick.line - 1, + columnNumber: 0, + }), + endLocationId: getLocationIdFor({ + ...node.callFrame, + lineNumber: tick.line, + columnNumber: 0, + }), + })); + } + + return [...locationsByRef.values()] + .sort((a, b) => a.id - b.id) + .map(l => ({ locations: [l.location], callFrame: l.callFrame })); +}; + +/** + * Computes the model for the given profile. + */ +export const buildModel = (profile: ICpuProfileRaw): IProfileModel => { + if (!profile.timeDeltas || !profile.samples) { + return { + nodes: [], + locations: [], + samples: profile.samples || [], + timeDeltas: profile.timeDeltas || [], + // rootPath: profile.$vscode?.rootPath, + duration: profile.endTime - profile.startTime, + }; + } + + const { samples, timeDeltas } = profile; + const sourceLocations = ensureSourceLocations(profile); + const locations: ILocation[] = sourceLocations.map((l, id) => { + const src = l.locations[0]; //getBestLocation(profile, l.locations); + + return { + id, + selfTime: 0, + aggregateTime: 0, + ticks: 0, + // category: categorize(l.callFrame, src), + callFrame: l.callFrame, + src, + }; + }); + + const idMap = new Map(); + const mapId = (nodeId: number) => { + let id = idMap.get(nodeId); + if (id === undefined) { + id = idMap.size; + idMap.set(nodeId, id); + } + + return id; + }; + + // 1. Created a sorted list of nodes. It seems that the profile always has + // incrementing IDs, although they are just not initially sorted. + const nodes = new Array(profile.nodes.length); + for (let i = 0; i < profile.nodes.length; i++) { + const node = profile.nodes[i]; + + // make them 0-based: + const id = mapId(node.id); + nodes[id] = { + id, + selfTime: 0, + aggregateTime: 0, + locationId: node.locationId as number, + children: node.children?.map(mapId) || [], + }; + + for (const child of node.positionTicks || []) { + if (child.startLocationId) { + locations[child.startLocationId].ticks += child.ticks; + } + } + } + + for (const node of nodes) { + for (const child of node.children) { + nodes[child].parent = node.id; + } + } + + // 2. The profile samples are the 'bottom-most' node, the currently running + // code. Sum of these in the self time. + const duration = profile.endTime - profile.startTime; + let lastNodeTime = duration - timeDeltas[0]; + for (let i = 0; i < timeDeltas.length - 1; i++) { + const d = timeDeltas[i + 1]; + nodes[mapId(samples[i])].selfTime += d; + lastNodeTime -= d; + } + + // Add in an extra time delta for the last sample. `timeDeltas[0]` is the + // time before the first sample, and the time of the last sample is only + // derived (approximately) by the missing time in the sum of deltas. Save + // some work by calculating it here. + if (nodes.length) { + nodes[mapId(samples[timeDeltas.length - 1])].selfTime += lastNodeTime; + timeDeltas.push(lastNodeTime); + } + + // 3. Add the aggregate times for all node children and locations + for (let i = 0; i < nodes.length; i++) { + const node = nodes[i]; + const location = locations[node.locationId]; + location.aggregateTime += computeAggregateTime(i, nodes); + location.selfTime += node.selfTime; + } + + return { + nodes, + locations, + samples: samples.map(mapId), + timeDeltas, + // rootPath: profile.$vscode?.rootPath, + duration, + }; +}; + +class BottomUpNode { + public static root() { + return new BottomUpNode({ + id: -1, + selfTime: 0, + aggregateTime: 0, + ticks: 0, + callFrame: { + functionName: '(root)', + lineNumber: -1, + columnNumber: -1, + scriptId: '0', + url: '', + }, + }); + } + + public children: { [id: number]: BottomUpNode } = {}; + public aggregateTime = 0; + public selfTime = 0; + public ticks = 0; + public childrenSize = 0; + + public get id() { + return this.location.id; + } + + public get callFrame() { + return this.location.callFrame; + } + + public get src() { + return this.location.src; + } + + constructor(public readonly location: ILocation, public readonly parent?: BottomUpNode) { } + + public addNode(node: IComputedNode) { + this.selfTime += node.selfTime; + this.aggregateTime += node.aggregateTime; + } + +} + +const processNode = (aggregate: BottomUpNode, node: IComputedNode, model: IProfileModel, initialNode = node) => { + let child = aggregate.children[node.locationId]; + if (!child) { + child = new BottomUpNode(model.locations[node.locationId], aggregate); + aggregate.childrenSize++; + aggregate.children[node.locationId] = child; + } + + child.addNode(initialNode); + + if (node.parent) { + processNode(child, model.nodes[node.parent], model, initialNode); + } +}; + +//#endregion + +function isSpecial(call: CdpCallFrame): boolean { + return call.functionName.startsWith('(') && call.functionName.endsWith(')'); +} + +export interface BottomUpSample { + selfTime: number; + totalTime: number; + location: string; + caller: { percentage: number; location: string }[]; + percentage: number; + isSpecial: boolean; +} + +export function bottomUp(p: Profile, topN: number, fullPaths: boolean = false) { + const model = buildModel(p); + const root = BottomUpNode.root(); + for (const node of model.nodes) { + processNode(root, node, model); + root.addNode(node); + } + + const result = Object.values(root.children) + .sort((a, b) => b.selfTime - a.selfTime) + // .filter(a => !isSpecial(a.callFrame)) + .slice(0, topN); + + + const samples: BottomUpSample[] = []; + + function printCallFrame(frame: CdpCallFrame): string { + let result = frame.functionName || '(anonymous)'; + if (frame.url) { + result += '#'; + result += fullPaths ? frame.url : basename(frame.url); + if (frame.lineNumber >= 0) { + result += ':'; + result += frame.lineNumber + 1; + } + } + return result; + } + + for (const node of result) { + + const sample: BottomUpSample = { + selfTime: node.selfTime / 1000, + totalTime: node.aggregateTime / 1000, + location: printCallFrame(node.callFrame), + caller: [], + percentage: Math.round(node.selfTime / (model.duration / 100)), + isSpecial: isSpecial(node.callFrame) + }; + + // follow the heaviest caller paths + const stack = [node]; + while (stack.length) { + const node = stack.pop()!; + let top: BottomUpNode | undefined; + for (const candidate of Object.values(node.children)) { + if (!top || top.selfTime < candidate.selfTime) { + top = candidate; + } + } + if (top) { + const percentage = Math.round(top.selfTime / (node.selfTime / 100)); + sample.caller.push({ percentage, location: printCallFrame(top.callFrame) }); + stack.push(top); + } + } + + samples.push(sample); + } + + return samples; +} diff --git a/src/vs/platform/protocol/electron-main/protocolMainService.ts b/src/vs/platform/protocol/electron-main/protocolMainService.ts index b4b9544fccf..43ad2167684 100644 --- a/src/vs/platform/protocol/electron-main/protocolMainService.ts +++ b/src/vs/platform/protocol/electron-main/protocolMainService.ts @@ -8,7 +8,7 @@ import { validatedIpcMain } from 'vs/base/parts/ipc/electron-main/ipcMain'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { TernarySearchTree } from 'vs/base/common/map'; import { COI, FileAccess, Schemas } from 'vs/base/common/network'; -import { extname, normalize } from 'vs/base/common/path'; +import { basename, extname, normalize } from 'vs/base/common/path'; import { isLinux } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; @@ -96,7 +96,7 @@ export class ProtocolMainService extends Disposable implements IProtocolMainServ let headers: Record | undefined; if (this.environmentService.crossOriginIsolated) { - if (path.endsWith('/workbench.html') || path.endsWith('/workbench-dev.html')) { + if (basename(path) === 'workbench.html' || basename(path) === 'workbench-dev.html') { headers = COI.CoopAndCoep; } else { headers = COI.getHeadersFromQuery(request.url); diff --git a/src/vs/platform/quickinput/browser/helpQuickAccess.ts b/src/vs/platform/quickinput/browser/helpQuickAccess.ts index 90d85542cca..e77794d189b 100644 --- a/src/vs/platform/quickinput/browser/helpQuickAccess.ts +++ b/src/vs/platform/quickinput/browser/helpQuickAccess.ts @@ -7,7 +7,7 @@ import { localize } from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { Extensions, IQuickAccessProvider, IQuickAccessRegistry } from 'vs/platform/quickinput/common/quickAccess'; +import { Extensions, IQuickAccessProvider, IQuickAccessProviderDescriptor, IQuickAccessRegistry } from 'vs/platform/quickinput/common/quickAccess'; import { IQuickInputService, IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; interface IHelpQuickAccessPickItem extends IQuickPickItem { @@ -46,34 +46,33 @@ export class HelpQuickAccessProvider implements IQuickAccessProvider { })); // Fill in all providers - picker.items = this.getQuickAccessProviders(); + picker.items = this.getQuickAccessProviders().filter(p => p.prefix !== HelpQuickAccessProvider.PREFIX); return disposables; } - private getQuickAccessProviders(): IHelpQuickAccessPickItem[] { - const providers: IHelpQuickAccessPickItem[] = []; - - for (const provider of this.registry.getQuickAccessProviders().sort((providerA, providerB) => providerA.prefix.localeCompare(providerB.prefix))) { - if (provider.prefix === HelpQuickAccessProvider.PREFIX) { - continue; // exclude help which is already active - } - - for (const helpEntry of provider.helpEntries) { - const prefix = helpEntry.prefix || provider.prefix; - const label = prefix || '\u2026' /* ... */; - - providers.push({ - prefix, - label, - keybinding: helpEntry.commandId ? this.keybindingService.lookupKeybinding(helpEntry.commandId) : undefined, - ariaLabel: localize('helpPickAriaLabel', "{0}, {1}", label, helpEntry.description), - description: helpEntry.description - }); - } - } + public getQuickAccessProviders(): IHelpQuickAccessPickItem[] { + const providers: IHelpQuickAccessPickItem[] = this.registry + .getQuickAccessProviders() + .sort((providerA, providerB) => providerA.prefix.localeCompare(providerB.prefix)) + .flatMap(provider => this.createPicks(provider)); return providers; } + + private createPicks(provider: IQuickAccessProviderDescriptor): IHelpQuickAccessPickItem[] { + return provider.helpEntries.map(helpEntry => { + const prefix = helpEntry.prefix || provider.prefix; + const label = prefix || '\u2026' /* ... */; + + return { + prefix, + label, + keybinding: helpEntry.commandId ? this.keybindingService.lookupKeybinding(helpEntry.commandId) : undefined, + ariaLabel: localize('helpPickAriaLabel', "{0}, {1}", label, helpEntry.description), + description: helpEntry.description + }; + }); + } } diff --git a/src/vs/platform/quickinput/browser/pickerQuickAccess.ts b/src/vs/platform/quickinput/browser/pickerQuickAccess.ts index bba15d63cfc..eb07b7d3dce 100644 --- a/src/vs/platform/quickinput/browser/pickerQuickAccess.ts +++ b/src/vs/platform/quickinput/browser/pickerQuickAccess.ts @@ -7,7 +7,7 @@ import { timeout } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { Disposable, DisposableStore, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { IKeyMods, IQuickPickDidAcceptEvent, IQuickPickSeparator } from 'vs/base/parts/quickinput/common/quickInput'; -import { IQuickAccessProvider } from 'vs/platform/quickinput/common/quickAccess'; +import { IQuickAccessProvider, IQuickAccessProviderRunOptions } from 'vs/platform/quickinput/common/quickAccess'; import { IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; export enum TriggerAction { @@ -97,7 +97,7 @@ export abstract class PickerQuickAccessProvider, token: CancellationToken): IDisposable { + provide(picker: IQuickPick, token: CancellationToken, runOptions?: IQuickAccessProviderRunOptions): IDisposable { const disposables = new DisposableStore(); // Apply options if any @@ -122,7 +122,7 @@ export abstract class PickerQuickAccessProvider, skipEmpty?: boolean): boolean => { let items: readonly Pick[]; @@ -338,5 +338,5 @@ export abstract class PickerQuickAccessProvider | Promise> | FastAndSlowPicks | null; + protected abstract _getPicks(filter: string, disposables: DisposableStore, token: CancellationToken, runOptions?: IQuickAccessProviderRunOptions): Picks | Promise> | FastAndSlowPicks | null; } diff --git a/src/vs/platform/quickinput/browser/quickAccess.ts b/src/vs/platform/quickinput/browser/quickAccess.ts index b2ed573cc72..8b131cde8f9 100644 --- a/src/vs/platform/quickinput/browser/quickAccess.ts +++ b/src/vs/platform/quickinput/browser/quickAccess.ts @@ -8,7 +8,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { once } from 'vs/base/common/functional'; import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { DefaultQuickAccessFilterValue, Extensions, IQuickAccessController, IQuickAccessOptions, IQuickAccessProvider, IQuickAccessProviderDescriptor, IQuickAccessRegistry } from 'vs/platform/quickinput/common/quickAccess'; +import { DefaultQuickAccessFilterValue, Extensions, IQuickAccessController, IQuickAccessOptions, IQuickAccessProvider, IQuickAccessProviderDescriptor, IQuickAccessProviderRunOptions, IQuickAccessRegistry } from 'vs/platform/quickinput/common/quickAccess'; import { IQuickInputService, IQuickPick, IQuickPickItem, ItemActivation } from 'vs/platform/quickinput/common/quickInput'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -122,14 +122,14 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon } // Register listeners - disposables.add(this.registerPickerListeners(picker, provider, descriptor, value)); + disposables.add(this.registerPickerListeners(picker, provider, descriptor, value, options?.providerOptions)); // Ask provider to fill the picker as needed if we have one // and pass over a cancellation token that will indicate when // the picker is hiding without a pick being made. const cts = disposables.add(new CancellationTokenSource()); if (provider) { - disposables.add(provider.provide(picker, cts.token)); + disposables.add(provider.provide(picker, cts.token, options?.providerOptions)); } // Finally, trigger disposal and cancellation when the picker @@ -173,7 +173,13 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon picker.valueSelection = valueSelection; } - private registerPickerListeners(picker: IQuickPick, provider: IQuickAccessProvider | undefined, descriptor: IQuickAccessProviderDescriptor | undefined, value: string): IDisposable { + private registerPickerListeners( + picker: IQuickPick, + provider: IQuickAccessProvider | undefined, + descriptor: IQuickAccessProviderDescriptor | undefined, + value: string, + providerOptions?: IQuickAccessProviderRunOptions + ): IDisposable { const disposables = new DisposableStore(); // Remember as last visible picker and clean up once picker get's disposed @@ -189,7 +195,12 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon disposables.add(picker.onDidChangeValue(value => { const [providerForValue] = this.getOrInstantiateProvider(value); if (providerForValue !== provider) { - this.show(value, { preserveValue: true } /* do not rewrite value from user typing! */); + this.show(value, { + // do not rewrite value from user typing! + preserveValue: true, + // persist the value of the providerOptions from the original showing + providerOptions: providerOptions + }); } else { visibleQuickAccess.value = value; // remember the value in our visible one } diff --git a/src/vs/platform/quickinput/common/quickAccess.ts b/src/vs/platform/quickinput/common/quickAccess.ts index 18976f270ba..a15069be8aa 100644 --- a/src/vs/platform/quickinput/common/quickAccess.ts +++ b/src/vs/platform/quickinput/common/quickAccess.ts @@ -10,6 +10,19 @@ import { ItemActivation } from 'vs/base/parts/quickinput/common/quickInput'; import { IQuickNavigateConfiguration, IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { Registry } from 'vs/platform/registry/common/platform'; +/** + * Provider specific options for this particular showing of the + * quick access. + */ +export interface IQuickAccessProviderRunOptions { } + +/** + * The specific options for the AnythingQuickAccessProvider. Put here to share between layers. + */ +export interface AnythingQuickAccessProviderRunOptions extends IQuickAccessProviderRunOptions { + includeHelp?: boolean; +} + export interface IQuickAccessOptions { /** @@ -28,6 +41,12 @@ export interface IQuickAccessOptions { * from any existing value if quick access is visible. */ preserveValue?: boolean; + + /** + * Provider specific options for this particular showing of the + * quick access. + */ + providerOptions?: IQuickAccessProviderRunOptions; } export interface IQuickAccessController { @@ -80,10 +99,12 @@ export interface IQuickAccessProvider { * a long running operation or from event handlers because it could be that the * picker has been closed or changed meanwhile. The token can be used to find out * that the picker was closed without picking an entry (e.g. was canceled by the user). + * @param options additional configuration specific for this provider that will + * influence what picks will be shown. * @return a disposable that will automatically be disposed when the picker * closes or is replaced by another picker. */ - provide(picker: IQuickPick, token: CancellationToken): IDisposable; + provide(picker: IQuickPick, token: CancellationToken, options?: IQuickAccessProviderRunOptions): IDisposable; } export interface IQuickAccessProviderHelp { diff --git a/src/vs/platform/remote/browser/browserSocketFactory.ts b/src/vs/platform/remote/browser/browserSocketFactory.ts index cac305317db..46a6dd3a7a7 100644 --- a/src/vs/platform/remote/browser/browserSocketFactory.ts +++ b/src/vs/platform/remote/browser/browserSocketFactory.ts @@ -274,7 +274,7 @@ export class BrowserSocketFactory implements ISocketFactory { connect(host: string, port: number, path: string, query: string, debugLabel: string, callback: IConnectCallback): void { const webSocketSchema = (/^https:/.test(window.location.href) ? 'wss' : 'ws'); - const socket = this._webSocketFactory.create(`${webSocketSchema}://${/:/.test(host) ? `[${host}]` : host}:${port}${path}?${query}&skipWebSocketFrames=false`, debugLabel); + const socket = this._webSocketFactory.create(`${webSocketSchema}://${(/:/.test(host) && !/\[/.test(host)) ? `[${host}]` : host}:${port}${path}?${query}&skipWebSocketFrames=false`, debugLabel); const errorListener = socket.onError((err) => callback(err, undefined)); socket.onOpen(() => { errorListener.dispose(); diff --git a/src/vs/platform/remoteTunnel/common/remoteTunnel.ts b/src/vs/platform/remoteTunnel/common/remoteTunnel.ts new file mode 100644 index 00000000000..f7c9e2a3609 --- /dev/null +++ b/src/vs/platform/remoteTunnel/common/remoteTunnel.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 { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { Event } from 'vs/base/common/event'; + + +export interface IRemoteTunnelAccount { + readonly authenticationProviderId: string; + readonly token: string; +} + +export const IRemoteTunnelService = createDecorator('IRemoteTunnelService'); +export interface IRemoteTunnelService { + readonly _serviceBrand: undefined; + + readonly onDidTokenFailed: Event; + readonly onDidChangeTunnelStatus: Event; + + getAccount(): Promise; + readonly onDidChangeAccount: Event; + updateAccount(account: IRemoteTunnelAccount | undefined): Promise; + +} + +export const enum TunnelStatus { + Uninitialized = 'uninitialized', + Disconnected = 'disconnected', + Connecting = 'connecting', + Connected = 'connected', +} diff --git a/src/vs/platform/remoteTunnel/electron-browser/remoteTunnelService.ts b/src/vs/platform/remoteTunnel/electron-browser/remoteTunnelService.ts new file mode 100644 index 00000000000..5ee3758c59d --- /dev/null +++ b/src/vs/platform/remoteTunnel/electron-browser/remoteTunnelService.ts @@ -0,0 +1,199 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IRemoteTunnelAccount, IRemoteTunnelService, TunnelStatus } from 'vs/platform/remoteTunnel/common/remoteTunnel'; +import { Emitter } from 'vs/base/common/event'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { ILogger, ILoggerService } from 'vs/platform/log/common/log'; +import { URI } from 'vs/base/common/uri'; +import { dirname, join } from 'vs/base/common/path'; +import { ChildProcess, spawn } from 'child_process'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { isWindows } from 'vs/base/common/platform'; +import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; +import { ISharedProcessLifecycleService } from 'vs/platform/lifecycle/electron-browser/sharedProcessLifecycleService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; + + +type RemoteTunnelEnablementClassification = { + owner: 'aeschli'; + comment: 'Reporting when Remote Tunnel access is turned on or off'; + enabled?: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Flag indicating if Remote Tunnel Access is enabled or not' }; +}; + +type RemoteTunnelEnablementEvent = { + enabled: boolean; +}; + +/** + * This service runs on the shared service. It is running the `code-tunnel` command + * to make the current machine available for remote access. + */ +export class RemoteTunnelService extends Disposable implements IRemoteTunnelService { + + declare readonly _serviceBrand: undefined; + + private readonly _onDidTokenFailedEmitter = new Emitter(); + public readonly onDidTokenFailed = this._onDidTokenFailedEmitter.event; + + private readonly _onDidChangeTunnelStatusEmitter = new Emitter(); + public readonly onDidChangeTunnelStatus = this._onDidChangeTunnelStatusEmitter.event; + + private readonly _onDidChangeAccountEmitter = new Emitter(); + public readonly onDidChangeAccount = this._onDidChangeAccountEmitter.event; + + private readonly _logger: ILogger; + + private _account: IRemoteTunnelAccount | undefined; + private _tunnelProcess: CancelablePromise | undefined; + + private _tunnelStatus: TunnelStatus = TunnelStatus.Disconnected; + + constructor( + @ITelemetryService private readonly telemetryService: ITelemetryService, + @IProductService private readonly productService: IProductService, + @INativeEnvironmentService private readonly environmentService: INativeEnvironmentService, + @ILoggerService loggerService: ILoggerService, + @ISharedProcessLifecycleService sharedProcessLifecycleService: ISharedProcessLifecycleService, + @IConfigurationService configurationService: IConfigurationService + ) { + super(); + const logFileUri = URI.file(join(dirname(environmentService.logsPath), 'remoteTunnel.log')); + this._logger = this._register(loggerService.createLogger(logFileUri, { name: 'remoteTunnel' })); + + this._register(sharedProcessLifecycleService.onWillShutdown(e => { + if (this._tunnelProcess) { + this._tunnelProcess.cancel(); + this._tunnelProcess = undefined; + } + this.dispose(); + })); + } + + async getAccount(): Promise { + return this._account; + } + + async updateAccount(account: IRemoteTunnelAccount | undefined): Promise { + if (account && this._account ? account.token !== this._account.token || account.authenticationProviderId !== this._account.authenticationProviderId : account !== this._account) { + this._account = account; + this._onDidChangeAccountEmitter.fire(account); + + this._logger.info(`Account updated: ${account ? account.authenticationProviderId : 'undefined'}`); + + this.telemetryService.publicLog2('remoteTunnel.enablement', { enabled: !!account }); + + try { + this.updateTunnelProcess(); + } catch (e) { + this._logger.error(e); + } + } + + } + + private async updateTunnelProcess(): Promise { + if (this._tunnelProcess) { + this._tunnelProcess.cancel(); + this._tunnelProcess = undefined; + } + if (!this._account) { + this.setTunnelStatus(TunnelStatus.Disconnected); + return; + } + this.setTunnelStatus(TunnelStatus.Connecting); + const loginProcess = this.runCodeTunneCommand('login', ['user', 'login', '--provider', this._account.authenticationProviderId, '--access-token', this._account.token]); + this._tunnelProcess = loginProcess; + try { + await loginProcess; + } catch (e) { + this._logger.error(e); + this._tunnelProcess = undefined; + this._onDidTokenFailedEmitter.fire(true); + this.setTunnelStatus(TunnelStatus.Disconnected); + } + if (this._tunnelProcess === loginProcess) { + const serveCommand = this.runCodeTunneCommand('tunnel', ['--random-name'], (message: string) => { + }); + this._tunnelProcess = serveCommand; + serveCommand.finally(() => { + if (serveCommand === this._tunnelProcess) { + // process exited unexpectedly + this._logger.info(`tunnel process terminated`); + this._tunnelProcess = undefined; + this._account = undefined; + + this.setTunnelStatus(TunnelStatus.Disconnected); + } + }); + + } + } + + private setTunnelStatus(tunnelStatus: TunnelStatus) { + if (tunnelStatus !== this._tunnelStatus) { + this._tunnelStatus = tunnelStatus; + this._onDidChangeTunnelStatusEmitter.fire(tunnelStatus); + } + } + + + private runCodeTunneCommand(logLabel: string, commandArgs: string[], onOutput: (message: string, isError: boolean) => void = () => { }): CancelablePromise { + return createCancelablePromise(token => { + return new Promise((resolve, reject) => { + if (token.isCancellationRequested) { + resolve(); + } + let tunnelProcess: ChildProcess | undefined; + token.onCancellationRequested(() => { + if (tunnelProcess) { + this._logger.info(`${logLabel} terminating (${tunnelProcess.pid})`); + tunnelProcess.kill(); + } + }); + if (process.env['VSCODE_DEV']) { + this._logger.info(`${logLabel} Spawning: cargo run --bin code-tunnel -- ${commandArgs.join(' ')}`); + tunnelProcess = spawn('cargo', ['run', '--bin', 'code-tunnel', '--', ...commandArgs], { cwd: join(this.environmentService.appRoot, 'cli') }); + } else { + const tunnelCommand = join(dirname(process.execPath), 'bin', `${this.productService.tunnelApplicationName}${isWindows ? '.exe' : ''}`); + this._logger.info(`${logLabel} Spawning: ${tunnelCommand} ${commandArgs.join(' ')}`); + tunnelProcess = spawn(tunnelCommand, commandArgs); + } + + tunnelProcess.stdout!.on('data', data => { + if (tunnelProcess) { + const message = data.toString(); + onOutput(message, false); + this._logger.info(`${logLabel} stdout (${tunnelProcess.pid}): + ${message}`); + } + }); + tunnelProcess.stderr!.on('data', data => { + if (tunnelProcess) { + const message = data.toString(); + onOutput(message, true); + this._logger.info(`${logLabel} stderr (${tunnelProcess.pid}): + ${message}`); + } + }); + tunnelProcess.on('exit', e => { + if (tunnelProcess) { + this._logger.info(`${logLabel} exit (${tunnelProcess.pid}): + ${e}`); + tunnelProcess = undefined; + resolve(); + } + }); + tunnelProcess.on('error', e => { + if (tunnelProcess) { + this._logger.info(`${logLabel} error (${tunnelProcess.pid}): + ${e}`); + tunnelProcess = undefined; + reject(); + } + }); + }); + }); + } + +} diff --git a/src/vs/platform/remoteTunnel/electron-sandbox/remoteTunnelService.ts b/src/vs/platform/remoteTunnel/electron-sandbox/remoteTunnelService.ts new file mode 100644 index 00000000000..4d626c9de47 --- /dev/null +++ b/src/vs/platform/remoteTunnel/electron-sandbox/remoteTunnelService.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. + *--------------------------------------------------------------------------------------------*/ + +import { registerSharedProcessRemoteService } from 'vs/platform/ipc/electron-sandbox/services'; +import { IRemoteTunnelService } from 'vs/platform/remoteTunnel/common/remoteTunnel'; + +registerSharedProcessRemoteService(IRemoteTunnelService, 'remoteTunnel'); diff --git a/src/vs/platform/telemetry/common/telemetryService.ts b/src/vs/platform/telemetry/common/telemetryService.ts index bfe90f7fed7..a2c2152f7b8 100644 --- a/src/vs/platform/telemetry/common/telemetryService.ts +++ b/src/vs/platform/telemetry/common/telemetryService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { DisposableStore } from 'vs/base/common/lifecycle'; -import { cloneAndChange, mixin } from 'vs/base/common/objects'; +import { mixin } from 'vs/base/common/objects'; import { MutableObservableValue } from 'vs/base/common/observableValue'; import { isWeb } from 'vs/base/common/platform'; import { escapeRegExpCharacters } from 'vs/base/common/strings'; @@ -16,7 +16,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { Registry } from 'vs/platform/registry/common/platform'; import { ClassifiedEvent, IGDPRProperty, OmitMetadata, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings'; import { ITelemetryData, ITelemetryInfo, ITelemetryService, TelemetryConfiguration, TelemetryLevel, TELEMETRY_OLD_SETTING_ID, TELEMETRY_SECTION_ID, TELEMETRY_SETTING_ID } from 'vs/platform/telemetry/common/telemetry'; -import { getTelemetryLevel, ITelemetryAppender } from 'vs/platform/telemetry/common/telemetryUtils'; +import { cleanData, getTelemetryLevel, ITelemetryAppender } from 'vs/platform/telemetry/common/telemetryUtils'; export interface ITelemetryServiceConfig { appenders: ITelemetryAppender[]; @@ -117,12 +117,7 @@ export class TelemetryService implements ITelemetryService { data = mixin(data, this._experimentProperties); // (last) remove all PII from data - data = cloneAndChange(data, value => { - if (typeof value === 'string') { - return this._cleanupInfo(value, anonymizeFilePaths); - } - return undefined; - }); + data = cleanData(data as Record, this._cleanupPatterns); // Log to the appenders of sufficient level this._appenders.forEach(a => a.log(eventName, data)); @@ -153,88 +148,6 @@ export class TelemetryService implements ITelemetryService { publicLogError2> = never, T extends IGDPRProperty = never>(eventName: string, data?: StrictPropertyCheck): Promise { return this.publicLogError(eventName, data as ITelemetryData); } - - private _anonymizeFilePaths(stack: string): string { - let updatedStack = stack; - - const cleanUpIndexes: [number, number][] = []; - for (const regexp of this._cleanupPatterns) { - while (true) { - const result = regexp.exec(stack); - if (!result) { - break; - } - cleanUpIndexes.push([result.index, regexp.lastIndex]); - } - } - - const nodeModulesRegex = /^[\\\/]?(node_modules|node_modules\.asar)[\\\/]/; - const fileRegex = /(file:\/\/)?([a-zA-Z]:(\\\\|\\|\/)|(\\\\|\\|\/))?([\w-\._]+(\\\\|\\|\/))+[\w-\._]*/g; - let lastIndex = 0; - updatedStack = ''; - - while (true) { - const result = fileRegex.exec(stack); - if (!result) { - break; - } - // Anoynimize user file paths that do not need to be retained or cleaned up. - if (!nodeModulesRegex.test(result[0]) && cleanUpIndexes.every(([x, y]) => result.index < x || result.index >= y)) { - updatedStack += stack.substring(lastIndex, result.index) + ''; - lastIndex = fileRegex.lastIndex; - } - } - if (lastIndex < stack.length) { - updatedStack += stack.substr(lastIndex); - } - - return updatedStack; - } - - private _removePropertiesWithPossibleUserInfo(property: string): string { - // If for some reason it is undefined we skip it (this shouldn't be possible); - if (!property) { - return property; - } - - const value = property.toLowerCase(); - - const userDataRegexes = [ - { label: 'Google API Key', regex: /AIza[A-Za-z0-9_\\\-]{35}/ }, - { label: 'Slack Token', regex: /xox[pbar]\-[A-Za-z0-9]/ }, - { label: 'Generic Secret', regex: /(key|token|sig|secret|signature|password|passwd|pwd|android:value)[^a-zA-Z0-9]/ }, - { label: 'Email', regex: /@[a-zA-Z0-9-.]+/ } // Regex which matches @*.site - ]; - - // Check for common user data in the telemetry events - for (const secretRegex of userDataRegexes) { - if (secretRegex.regex.test(value)) { - return ``; - } - } - - return property; - } - - - private _cleanupInfo(property: string, anonymizeFilePaths?: boolean): string { - let updatedProperty = property; - - // anonymize file paths - if (anonymizeFilePaths) { - updatedProperty = this._anonymizeFilePaths(updatedProperty); - } - - // sanitize with configured cleanup patterns - for (const regexp of this._cleanupPatterns) { - updatedProperty = updatedProperty.replace(regexp, ''); - } - - // remove possible user info - updatedProperty = this._removePropertiesWithPossibleUserInfo(updatedProperty); - - return updatedProperty; - } } function getTelemetryLevelSettingDescription(): string { diff --git a/src/vs/platform/telemetry/common/telemetryUtils.ts b/src/vs/platform/telemetry/common/telemetryUtils.ts index fd8e1a4f972..1f2044b947e 100644 --- a/src/vs/platform/telemetry/common/telemetryUtils.ts +++ b/src/vs/platform/telemetry/common/telemetryUtils.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IDisposable } from 'vs/base/common/lifecycle'; -import { safeStringify } from 'vs/base/common/objects'; +import { cloneAndChange, safeStringify } from 'vs/base/common/objects'; import { staticObservableValue } from 'vs/base/common/observableValue'; import { isObject } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; @@ -191,7 +191,7 @@ export function validateTelemetryData(data?: any): { properties: Properties; mea }; } -const telemetryAllowedAuthorities: readonly string[] = ['ssh-remote', 'dev-container', 'attached-container', 'wsl', 'tunneling']; +const telemetryAllowedAuthorities: readonly string[] = ['ssh-remote', 'dev-container', 'attached-container', 'wsl', 'tunneling', 'codespaces']; export function cleanRemoteAuthority(remoteAuthority?: string): string { if (!remoteAuthority) { @@ -276,3 +276,109 @@ interface IPathEnvironment { export function getPiiPathsFromEnvironment(paths: IPathEnvironment): string[] { return [paths.appRoot, paths.extensionsPath, paths.userHome.fsPath, paths.tmpDir.fsPath, paths.userDataPath]; } + +//#region Telemetry Cleaning + +/** + * Cleans a given stack of possible paths + * @param stack The stack to sanitize + * @param cleanupPatterns Cleanup patterns to remove from the stack + * @returns The cleaned stack + */ +function anonymizeFilePaths(stack: string, cleanupPatterns: RegExp[]): string { + let updatedStack = stack; + + const cleanUpIndexes: [number, number][] = []; + for (const regexp of cleanupPatterns) { + while (true) { + const result = regexp.exec(stack); + if (!result) { + break; + } + cleanUpIndexes.push([result.index, regexp.lastIndex]); + } + } + + const nodeModulesRegex = /^[\\\/]?(node_modules|node_modules\.asar)[\\\/]/; + const fileRegex = /(file:\/\/)?([a-zA-Z]:(\\\\|\\|\/)|(\\\\|\\|\/))?([\w-\._]+(\\\\|\\|\/))+[\w-\._]*/g; + let lastIndex = 0; + updatedStack = ''; + + while (true) { + const result = fileRegex.exec(stack); + if (!result) { + break; + } + // Anoynimize user file paths that do not need to be retained or cleaned up. + if (!nodeModulesRegex.test(result[0]) && cleanUpIndexes.every(([x, y]) => result.index < x || result.index >= y)) { + updatedStack += stack.substring(lastIndex, result.index) + ''; + lastIndex = fileRegex.lastIndex; + } + } + if (lastIndex < stack.length) { + updatedStack += stack.substr(lastIndex); + } + + return updatedStack; +} + +/** + * Attempts to remove commonly leaked PII + * @param property The property which will be removed if it contains user data + * @returns The new value for the property + */ +function removePropertiesWithPossibleUserInfo(property: string): string { + // If for some reason it is undefined we skip it (this shouldn't be possible); + if (!property) { + return property; + } + + const value = property.toLowerCase(); + + const userDataRegexes = [ + { label: 'Google API Key', regex: /AIza[A-Za-z0-9_\\\-]{35}/ }, + { label: 'Slack Token', regex: /xox[pbar]\-[A-Za-z0-9]/ }, + { label: 'Generic Secret', regex: /(key|token|sig|secret|signature|password|passwd|pwd|android:value)[^a-zA-Z0-9]/ }, + { label: 'Email', regex: /@[a-zA-Z0-9-]+\.[a-zA-Z0-9-]+/ } // Regex which matches @*.site + ]; + + // Check for common user data in the telemetry events + for (const secretRegex of userDataRegexes) { + if (secretRegex.regex.test(value)) { + return ``; + } + } + + return property; +} + + +/** + * Does a best possible effort to clean a data object from any possible PII. + * @param data The data object to clean + * @param paths Any additional patterns that should be removed from the data set + * @returns A new object with the PII removed + */ +export function cleanData(data: Record, cleanUpPatterns: RegExp[]): Record { + return cloneAndChange(data, value => { + // We only know how to clean strings + if (typeof value === 'string') { + let updatedProperty = value; + // First we anonymize any possible file paths + updatedProperty = anonymizeFilePaths(updatedProperty, cleanUpPatterns); + + // Then we do a simple regex replace with the defined patterns + for (const regexp of cleanUpPatterns) { + updatedProperty = updatedProperty.replace(regexp, ''); + } + + // Lastly, remove commonly leaked PII + updatedProperty = removePropertiesWithPossibleUserInfo(updatedProperty); + + return updatedProperty; + } + return undefined; + }); +} + +//#endregion diff --git a/src/vs/platform/telemetry/test/common/telemetryLogAppender.test.ts b/src/vs/platform/telemetry/test/common/telemetryLogAppender.test.ts index c43b0e06b7c..b1cb552a339 100644 --- a/src/vs/platform/telemetry/test/common/telemetryLogAppender.test.ts +++ b/src/vs/platform/telemetry/test/common/telemetryLogAppender.test.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { Event } from 'vs/base/common/event'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { AbstractLogger, DEFAULT_LOG_LEVEL, ILogger, ILoggerService, LogLevel } from 'vs/platform/log/common/log'; @@ -57,7 +58,7 @@ class TestTelemetryLogger extends AbstractLogger implements ILogger { flush(): void { } } -class TestTelemetryLoggerService implements ILoggerService { +export class TestTelemetryLoggerService implements ILoggerService { _serviceBrand: undefined; logger?: TestTelemetryLogger; @@ -75,6 +76,11 @@ class TestTelemetryLoggerService implements ILoggerService { return this.logger; } + + onDidChangeLogLevel = Event.None; + setLevel(): void { } + getLogLevel() { return undefined; } + getDefaultLogLevel() { return this.logLevel; } } suite('TelemetryLogAdapter', () => { diff --git a/src/vs/platform/terminal/common/capabilities/capabilities.ts b/src/vs/platform/terminal/common/capabilities/capabilities.ts index 5b00519a62d..d68a14d21b8 100644 --- a/src/vs/platform/terminal/common/capabilities/capabilities.ts +++ b/src/vs/platform/terminal/common/capabilities/capabilities.ts @@ -153,6 +153,7 @@ export interface ICommandDetectionCapability { readonly hasInput: boolean | undefined; readonly onCommandStarted: Event; readonly onCommandFinished: Event; + readonly onCommandExecuted: Event; readonly onCommandInvalidated: Event; readonly onCurrentCommandInvalidated: Event; setCwd(value: string): void; @@ -220,10 +221,35 @@ export interface ITerminalCommand { commandStartLineContent?: string; markProperties?: IMarkProperties; getOutput(): string | undefined; - getOutputMatch(outputMatcher: { lineMatcher: string | RegExp; anchor?: 'top' | 'bottom'; offset?: number; length?: number }): RegExpMatchArray | undefined; + getOutputMatch(outputMatcher: ITerminalOutputMatcher): RegExpMatchArray | undefined; hasOutput(): boolean; } + +/** + * A matcher that runs on a sub-section of a terminal command's output + */ +export interface ITerminalOutputMatcher { + /** + * A string or regex to match against the unwrapped line. If this is a regex with the multiline + * flag, it will scan an amount of lines equal to `\n` instances in the regex + 1. + */ + lineMatcher: string | RegExp; + /** + * Which side of the output to anchor the {@link offset} and {@link length} against. + */ + anchor: 'top' | 'bottom'; + /** + * How far from either the top or the bottom of the butter to start matching against. + */ + offset: number; + /** + * The number of rows to match against, this should be as small as possible for performance + * reasons. + */ + length: number; +} + /** * A clone of the IMarker from xterm which cannot be imported from common */ diff --git a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts index 48606b7fafa..5ecd49bee30 100644 --- a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts +++ b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts @@ -7,7 +7,7 @@ import { timeout } from 'vs/base/common/async'; import { debounce } from 'vs/base/common/decorators'; import { Emitter } from 'vs/base/common/event'; import { ILogService } from 'vs/platform/log/common/log'; -import { ICommandDetectionCapability, TerminalCapability, ITerminalCommand, IHandleCommandOptions, ICommandInvalidationRequest, CommandInvalidationReason, ISerializedCommand, ISerializedCommandDetectionCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; +import { ICommandDetectionCapability, TerminalCapability, ITerminalCommand, IHandleCommandOptions, ICommandInvalidationRequest, CommandInvalidationReason, ISerializedCommand, ISerializedCommandDetectionCapability, ITerminalOutputMatcher } from 'vs/platform/terminal/common/capabilities/capabilities'; // Importing types is safe in any layer // eslint-disable-next-line local/code-import-patterns @@ -97,6 +97,8 @@ export class CommandDetectionCapability implements ICommandDetectionCapability { readonly onBeforeCommandFinished = this._onBeforeCommandFinished.event; private readonly _onCommandFinished = new Emitter(); readonly onCommandFinished = this._onCommandFinished.event; + private readonly _onCommandExecuted = new Emitter(); + readonly onCommandExecuted = this._onCommandExecuted.event; private readonly _onCommandInvalidated = new Emitter(); readonly onCommandInvalidated = this._onCommandInvalidated.event; private readonly _onCurrentCommandInvalidated = new Emitter(); @@ -422,6 +424,7 @@ export class CommandDetectionCapability implements ICommandDetectionCapability { if (y === commandExecutedLine) { this._currentCommand.command += this._terminal.buffer.active.getLine(commandExecutedLine)?.translateToString(true, undefined, this._currentCommand.commandExecutedX) || ''; } + this._onCommandExecuted.fire(); } private _handleCommandExecutedWindows(): void { @@ -431,6 +434,7 @@ export class CommandDetectionCapability implements ICommandDetectionCapability { this._onCursorMoveListener = undefined; this._evaluateCommandMarkersWindows(); this._currentCommand.commandExecutedX = this._terminal.buffer.active.cursorX; + this._onCommandExecuted.fire(); this._logService.debug('CommandDetectionCapability#handleCommandExecuted', this._currentCommand.commandExecutedX, this._currentCommand.commandExecutedMarker?.line); } @@ -486,7 +490,7 @@ export class CommandDetectionCapability implements ICommandDetectionCapability { commandStartLineContent: this._currentCommand.commandStartLineContent, hasOutput: () => !executedMarker?.isDisposed && !endMarker?.isDisposed && !!(executedMarker && endMarker && executedMarker?.line < endMarker!.line), getOutput: () => getOutputForCommand(executedMarker, endMarker, buffer), - getOutputMatch: (outputMatcher?: { lineMatcher: string | RegExp; anchor?: 'top' | 'bottom'; offset?: number; length?: number }) => getOutputMatchForCommand(executedMarker, endMarker, buffer, this._terminal.cols, outputMatcher), + getOutputMatch: (outputMatcher: ITerminalOutputMatcher) => getOutputMatchForCommand(executedMarker, endMarker, buffer, this._terminal.cols, outputMatcher), markProperties: options?.markProperties }; this._commands.push(newCommand); @@ -611,7 +615,7 @@ export class CommandDetectionCapability implements ICommandDetectionCapability { exitCode: e.exitCode, hasOutput: () => !executedMarker?.isDisposed && !endMarker?.isDisposed && !!(executedMarker && endMarker && executedMarker.line < endMarker.line), getOutput: () => getOutputForCommand(executedMarker, endMarker, buffer), - getOutputMatch: (outputMatcher: { lineMatcher: string | RegExp; anchor?: 'top' | 'bottom'; offset?: number; length?: number }) => getOutputMatchForCommand(executedMarker, endMarker, buffer, this._terminal.cols, outputMatcher), + getOutputMatch: (outputMatcher: ITerminalOutputMatcher) => getOutputMatchForCommand(executedMarker, endMarker, buffer, this._terminal.cols, outputMatcher), markProperties: e.markProperties }; this._commands.push(newCommand); @@ -643,36 +647,47 @@ function getOutputForCommand(executedMarker: IMarker | undefined, endMarker: IMa return output === '' ? undefined : output; } -export function getOutputMatchForCommand(executedMarker: IMarker | undefined, endMarker: IMarker | undefined, buffer: IBuffer, cols: number, outputMatcher: { lineMatcher: string | RegExp; anchor?: 'top' | 'bottom'; offset?: number; length?: number } | undefined): RegExpMatchArray | undefined { +export function getOutputMatchForCommand(executedMarker: IMarker | undefined, endMarker: IMarker | undefined, buffer: IBuffer, cols: number, outputMatcher: ITerminalOutputMatcher): RegExpMatchArray | undefined { if (!executedMarker || !endMarker) { return undefined; } const startLine = executedMarker.line; const endLine = endMarker.line; - if (startLine === endLine) { - return undefined; - } - if (outputMatcher?.length && (endLine - startLine) < outputMatcher.length) { - return undefined; - } - let output = ''; - let line: string | undefined; - if (outputMatcher?.anchor === 'bottom') { + const matcher = outputMatcher.lineMatcher; + const linesToCheck = typeof matcher === 'string' ? 1 : outputMatcher.length || countNewLines(matcher); + const lines: string[] = []; + if (outputMatcher.anchor === 'bottom') { for (let i = endLine - (outputMatcher.offset || 0); i >= startLine; i--) { - line = getXtermLineContent(buffer, i, i, cols); - output = line + output; - const match = output.match(outputMatcher.lineMatcher); + let wrappedLineStart = i; + const wrappedLineEnd = i; + while (wrappedLineStart >= startLine && buffer.getLine(wrappedLineStart)?.isWrapped) { + wrappedLineStart--; + } + i = wrappedLineStart; + lines.unshift(getXtermLineContent(buffer, wrappedLineStart, wrappedLineEnd, cols)); + if (lines.length > linesToCheck) { + lines.pop(); + } + const match = lines.join('\n').match(matcher); if (match) { return match; } } } else { - for (let i = startLine + (outputMatcher?.offset || 0); i < endLine; i++) { - line = getXtermLineContent(buffer, i, i, cols); - output += line; + for (let i = startLine + (outputMatcher.offset || 0); i < endLine; i++) { + const wrappedLineStart = i; + let wrappedLineEnd = i; + while (wrappedLineEnd + 1 < endLine && buffer.getLine(wrappedLineEnd + 1)?.isWrapped) { + wrappedLineEnd++; + } + i = wrappedLineEnd; + lines.push(getXtermLineContent(buffer, wrappedLineStart, wrappedLineEnd, cols)); + if (lines.length === linesToCheck) { + lines.shift(); + } if (outputMatcher) { - const match = output.match(outputMatcher.lineMatcher); + const match = lines.join('\n').match(matcher); if (match) { return match; } @@ -698,3 +713,17 @@ function getXtermLineContent(buffer: IBuffer, lineStart: number, lineEnd: number } return content; } + +function countNewLines(regex: RegExp): number { + if (!regex.multiline) { + return 1; + } + const source = regex.source; + let count = 1; + let i = source.indexOf('\\n'); + while (i !== -1) { + count++; + i = source.indexOf('\\n', i + 1); + } + return count; +} diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index d45df31b65a..60ec1dacd1f 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -331,7 +331,7 @@ export interface IPtyService extends IPtyHostController { reduceConnectionGraceTime(): Promise; requestDetachInstance(workspaceId: string, instanceId: number): Promise; acceptDetachInstanceReply(requestId: number, persistentProcessId?: number): Promise; - freePortKillProcess?(id: number, port: string): Promise<{ port: string; processId: string }>; + freePortKillProcess?(port: string): Promise<{ port: string; processId: string }>; /** * Serializes and returns terminal state. * @param ids The persistent terminal IDs to serialize. @@ -486,7 +486,7 @@ export interface IShellLaunchConfig { * Whether the terminal process environment should be exactly as provided in * `TerminalOptions.env`. When this is false (default), the environment will be based on the * window's environment and also apply configured platform settings like - * `terminal.integrated.windows.env` on top. When this is true, the complete environment must be + * `terminal.integrated.env.windows` on top. When this is true, the complete environment must be * provided as nothing will be inherited from the process or any configuration. */ strictEnv?: boolean; diff --git a/src/vs/platform/terminal/common/terminalEnvironment.ts b/src/vs/platform/terminal/common/terminalEnvironment.ts index 1d24a24f60d..7f7ab14a2db 100644 --- a/src/vs/platform/terminal/common/terminalEnvironment.ts +++ b/src/vs/platform/terminal/common/terminalEnvironment.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { OperatingSystem, OS } from 'vs/base/common/platform'; + export function escapeNonWindowsPath(path: string): string { let newPath = path; if (newPath.indexOf('\\') !== 0) { @@ -35,3 +37,20 @@ export function collapseTildePath(path: string | undefined, userHome: string | u } return `~${separator}${path.slice(userHome.length + 1)}`; } + +/** + * Sanitizes a cwd string, removing any wrapping quotes and making the Windows drive letter + * uppercase. + * @param cwd The directory to sanitize. + */ +export function sanitizeCwd(cwd: string): string { + // Sanity check that the cwd is not wrapped in quotes (see #160109) + if (cwd.match(/^['"].*['"]$/)) { + cwd = cwd.substring(1, cwd.length - 1); + } + // Make the drive letter uppercase on Windows (see #9448) + if (OS === OperatingSystem.Windows && cwd && cwd[1] === ':') { + return cwd[0].toUpperCase() + cwd.substring(1); + } + return cwd; +} diff --git a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts index 47ebe176985..b1a1e4498f9 100644 --- a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts +++ b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts @@ -20,6 +20,7 @@ import { BufferMarkCapability } from 'vs/platform/terminal/common/capabilities/b // eslint-disable-next-line local/code-import-patterns import type { ITerminalAddon, Terminal } from 'xterm-headless'; import { URI } from 'vs/base/common/uri'; +import { sanitizeCwd } from 'vs/platform/terminal/common/terminalEnvironment'; /** @@ -383,6 +384,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati } private _updateCwd(value: string) { + value = sanitizeCwd(value); this._createOrGetCwdDetection().updateCwd(value); const commandDetection = this.capabilities.get(TerminalCapability.CommandDetection); commandDetection?.setCwd(value); diff --git a/src/vs/platform/terminal/node/ptyHostMain.ts b/src/vs/platform/terminal/node/ptyHostMain.ts index 3de63e7d9e6..da8071c9084 100644 --- a/src/vs/platform/terminal/node/ptyHostMain.ts +++ b/src/vs/platform/terminal/node/ptyHostMain.ts @@ -4,13 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import { join } from 'vs/base/common/path'; +import { URI } from 'vs/base/common/uri'; import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc'; import { Server } from 'vs/base/parts/ipc/node/ipc.cp'; import { OPTIONS, parseArgs } from 'vs/platform/environment/node/argv'; import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; -import { ConsoleLogger, getLogLevel, LogService, MultiplexLogService } from 'vs/platform/log/common/log'; +import { BufferLogService } from 'vs/platform/log/common/bufferLog'; +import { ConsoleLogger, LogService, MultiplexLogService } from 'vs/platform/log/common/log'; import { LogLevelChannel } from 'vs/platform/log/common/logIpc'; -import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog'; +import { LoggerService } from 'vs/platform/log/node/loggerService'; import product from 'vs/platform/product/common/product'; import { IProductService } from 'vs/platform/product/common/productService'; import { IReconnectConstants, TerminalIpcChannels, TerminalLogConstants } from 'vs/platform/terminal/common/terminal'; @@ -25,11 +27,14 @@ delete process.env.VSCODE_LAST_PTY_ID; // Logging const productService: IProductService = { _serviceBrand: undefined, ...product }; const environmentService = new NativeEnvironmentService(parseArgs(process.argv, OPTIONS), productService); +const bufferLogService = new BufferLogService(); const logService = new LogService(new MultiplexLogService([ new ConsoleLogger(), - new SpdLogLogger(TerminalLogConstants.FileName, join(environmentService.logsPath, `${TerminalLogConstants.FileName}.log`), true, false, getLogLevel(environmentService)) + bufferLogService ])); -const logLevelChannel = new LogLevelChannel(logService); +const loggerService = new LoggerService(logService); +bufferLogService.logger = loggerService.createLogger(URI.file(join(environmentService.logsPath, `${TerminalLogConstants.FileName}.log`)), { name: TerminalLogConstants.FileName }); +const logLevelChannel = new LogLevelChannel(logService, loggerService); server.registerChannel(TerminalIpcChannels.Log, logLevelChannel); const heartbeatService = new HeartbeatService(); @@ -44,7 +49,7 @@ delete process.env.VSCODE_RECONNECT_GRACE_TIME; delete process.env.VSCODE_RECONNECT_SHORT_GRACE_TIME; delete process.env.VSCODE_RECONNECT_SCROLLBACK; -const ptyService = new PtyService(lastPtyId, logService, reconnectConstants); +const ptyService = new PtyService(lastPtyId, logService, productService, reconnectConstants); server.registerChannel(TerminalIpcChannels.PtyHost, ProxyChannel.fromService(ptyService)); process.once('exit', () => { diff --git a/src/vs/platform/terminal/node/ptyHostService.ts b/src/vs/platform/terminal/node/ptyHostService.ts index 41cb6ae3a14..98bc9f4180e 100644 --- a/src/vs/platform/terminal/node/ptyHostService.ts +++ b/src/vs/platform/terminal/node/ptyHostService.ts @@ -316,11 +316,11 @@ export class PtyHostService extends Disposable implements IPtyService { return this._proxy.acceptDetachInstanceReply(requestId, persistentProcessId); } - async freePortKillProcess(id: number, port: string): Promise<{ port: string; processId: string }> { + async freePortKillProcess(port: string): Promise<{ port: string; processId: string }> { if (!this._proxy.freePortKillProcess) { throw new Error('freePortKillProcess does not exist on the pty proxy'); } - return this._proxy.freePortKillProcess(id, port); + return this._proxy.freePortKillProcess(port); } async serializeTerminalState(ids: number[]): Promise { diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index 282bf02ff66..325a911670c 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -28,6 +28,7 @@ import { ErrorNoTelemetry } from 'vs/base/common/errors'; import { ShellIntegrationAddon } from 'vs/platform/terminal/common/xterm/shellIntegrationAddon'; import { formatMessageForTerminal } from 'vs/platform/terminal/common/terminalStrings'; import { IPtyHostProcessReplayEvent } from 'vs/platform/terminal/common/capabilities/capabilities'; +import { IProductService } from 'vs/platform/product/common/productService'; type WorkspaceId = string; @@ -64,6 +65,7 @@ export class PtyService extends Disposable implements IPtyService { constructor( private _lastPtyId: number, private readonly _logService: ILogService, + private readonly _productService: IProductService, private readonly _reconnectConstants: IReconnectConstants ) { super(); @@ -103,64 +105,29 @@ export class PtyService extends Disposable implements IPtyService { this._detachInstanceRequestStore.acceptReply(requestId, processDetails); } - async freePortKillProcess(id: number, port: string): Promise<{ port: string; processId: string }> { - let result: { port: string; processId: string } | undefined; - if (!isWindows) { - const stdout = await new Promise((resolve, reject) => { - exec(`lsof -nP -iTCP -sTCP:LISTEN | grep ${port}`, {}, (err, stdout) => { - if (err) { - return reject('Problem occurred when listing active processes'); - } - resolve(stdout); - }); - }); - const processesForPort = stdout.split('\n'); - if (processesForPort.length >= 1) { - const capturePid = /\s+(\d+)\s+/; - const processId = processesForPort[0].match(capturePid)?.[1]; - if (processId) { - await new Promise((resolve, reject) => { - exec(`kill ${processId}`, {}, (err, stdout) => { - if (err) { - return reject(`Problem occurred when killing the process w ID: ${processId}`); - } - resolve(stdout); - }); - result = { port, processId }; - }); + async freePortKillProcess(port: string): Promise<{ port: string; processId: string }> { + const stdout = await new Promise((resolve, reject) => { + exec(isWindows ? `netstat -ano | findstr "${port}"` : `lsof -nP -iTCP -sTCP:LISTEN | grep ${port}`, {}, (err, stdout) => { + if (err) { + return reject('Problem occurred when listing active processes'); } - } - } else { - const stdout = await new Promise((resolve, reject) => { - exec(`netstat -ano | findstr "${port}"`, {}, (err, stdout) => { - if (err) { - return reject('Problem occurred when listing active processes'); - } - resolve(stdout); - }); + resolve(stdout); }); - const processesForPort = stdout.split('\n'); - if (processesForPort.length >= 1) { - const capturePid = /LISTENING\s+(\d{3})/; - const processId = processesForPort[0].match(capturePid)?.[1]; - if (processId) { - await new Promise((resolve, reject) => { - exec(`Taskkill /F /PID ${processId}`, {}, (err, stdout) => { - if (err) { - return reject(`Problem occurred when killing the process w ID: ${processId}`); - } - resolve(stdout); - }); - result = { port, processId }; - }); - } + }); + const processesForPort = stdout.split('\n'); + if (processesForPort.length >= 1) { + const capturePid = /\s+(\d+)\s+/; + const processId = processesForPort[0].match(capturePid)?.[1]; + if (processId) { + try { + process.kill(Number.parseInt(processId)); + } catch { } + } else { + throw new Error(`Processes for port ${port} were not found`); } + return { port, processId }; } - - if (result) { - return result; - } - throw new Error(`Processes for port ${port} were not found`); + throw new Error(`Could not kill process with port ${port}`); } async serializeTerminalState(ids: number[]): Promise { @@ -244,7 +211,7 @@ export class PtyService extends Disposable implements IPtyService { throw new Error('Attempt to create a process when attach object was provided'); } const id = ++this._lastPtyId; - const process = new TerminalProcess(shellLaunchConfig, cwd, cols, rows, env, executableEnv, options, this._logService); + const process = new TerminalProcess(shellLaunchConfig, cwd, cols, rows, env, executableEnv, options, this._logService, this._productService); process.onProcessData(event => this._onProcessData.fire({ id, event })); const processLaunchOptions: IPersistentTerminalProcessLaunchConfig = { env, diff --git a/src/vs/platform/terminal/node/terminalEnvironment.ts b/src/vs/platform/terminal/node/terminalEnvironment.ts index d1884b8c3ab..9ff09c4322e 100644 --- a/src/vs/platform/terminal/node/terminalEnvironment.ts +++ b/src/vs/platform/terminal/node/terminalEnvironment.ts @@ -13,6 +13,7 @@ import { format } from 'vs/base/common/strings'; import { isString } from 'vs/base/common/types'; import * as pfs from 'vs/base/node/pfs'; import { ILogService } from 'vs/platform/log/common/log'; +import { IProductService } from 'vs/platform/product/common/productService'; import { IShellLaunchConfig, ITerminalEnvironment, ITerminalProcessOptions } from 'vs/platform/terminal/common/terminal'; export function getWindowsBuildNumber(): number { @@ -103,15 +104,16 @@ export interface IShellIntegrationConfigInjection { */ export function getShellIntegrationInjection( shellLaunchConfig: IShellLaunchConfig, - options: ITerminalProcessOptions['shellIntegration'], + options: Pick, env: ITerminalEnvironment | undefined, - logService: ILogService + logService: ILogService, + productService: IProductService ): IShellIntegrationConfigInjection | undefined { // Shell integration arg injection is disabled when: // - The global setting is disabled // - There is no executable (not sure what script to run) // - The terminal is used by a feature like tasks or debugging - if (!options.enabled || !shellLaunchConfig.executable || shellLaunchConfig.isFeatureTerminal || shellLaunchConfig.hideFromUser || shellLaunchConfig.ignoreShellIntegration) { + if (!options.shellIntegration.enabled || !shellLaunchConfig.executable || shellLaunchConfig.isFeatureTerminal || shellLaunchConfig.hideFromUser || shellLaunchConfig.ignoreShellIntegration || (isWindows && !options.windowsEnableConpty)) { return undefined; } @@ -184,8 +186,9 @@ export function getShellIntegrationInjection( } newArgs = [...newArgs]; // Shallow clone the array to avoid setting the default array newArgs[newArgs.length - 1] = format(newArgs[newArgs.length - 1], appRoot); + // Move .zshrc into $ZDOTDIR as the way to activate the script - const zdotdir = path.join(os.tmpdir(), `${os.userInfo().username}-vscode-zsh`); + const zdotdir = path.join(os.tmpdir(), `${os.userInfo().username}-${productService.applicationName}-zsh`); envMixin['ZDOTDIR'] = zdotdir; const userZdotdir = env?.ZDOTDIR ?? os.homedir() ?? `~`; envMixin['USER_ZDOTDIR'] = userZdotdir; diff --git a/src/vs/platform/terminal/node/terminalProcess.ts b/src/vs/platform/terminal/node/terminalProcess.ts index 366d57b4d5f..9a670fda3e8 100644 --- a/src/vs/platform/terminal/node/terminalProcess.ts +++ b/src/vs/platform/terminal/node/terminalProcess.ts @@ -6,17 +6,16 @@ import { exec } from 'child_process'; import { promises as fs } from 'fs'; import type * as pty from 'node-pty'; -import { tmpdir } from 'os'; import { timeout } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { FileAccess } from 'vs/base/common/network'; import * as path from 'vs/base/common/path'; import { IProcessEnvironment, isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { Promises } from 'vs/base/node/pfs'; import { localize } from 'vs/nls'; import { ILogService } from 'vs/platform/log/common/log'; +import { IProductService } from 'vs/platform/product/common/productService'; import { FlowControlConstants, IShellLaunchConfig, ITerminalChildProcess, ITerminalLaunchError, IProcessProperty, IProcessPropertyMap as IProcessPropertyMap, ProcessPropertyType, TerminalShellType, IProcessReadyEvent, ITerminalProcessOptions, PosixShellType } from 'vs/platform/terminal/common/terminal'; import { ChildProcessMonitor } from 'vs/platform/terminal/node/childProcessMonitor'; import { findExecutable, getShellIntegrationInjection, getWindowsBuildNumber, IShellIntegrationConfigInjection } from 'vs/platform/terminal/node/terminalEnvironment'; @@ -145,7 +144,8 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess */ private readonly _executableEnv: IProcessEnvironment, private readonly _options: ITerminalProcessOptions, - @ILogService private readonly _logService: ILogService + @ILogService private readonly _logService: ILogService, + @IProductService private readonly _productService: IProductService ) { super(); let name: string; @@ -201,7 +201,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess let injection: IShellIntegrationConfigInjection | undefined; if (this._options.shellIntegration.enabled) { - injection = getShellIntegrationInjection(this.shellLaunchConfig, this._options.shellIntegration, this._ptyOptions.env, this._logService); + injection = getShellIntegrationInjection(this.shellLaunchConfig, { shellIntegration: this._options.shellIntegration, windowsEnableConpty: this._options.windowsEnableConpty }, this._ptyOptions.env, this._logService, this._productService); if (injection) { this._onDidChangeProperty.fire({ type: ProcessPropertyType.UsedShellIntegrationInjection, value: true }); if (injection.envMixin) { @@ -228,25 +228,6 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess } } - // Handle zsh shell integration - Set $ZDOTDIR to a temp dir and create $ZDOTDIR/.zshrc - if (this.shellLaunchConfig.env?.['_ZDOTDIR'] === '1') { - const zdotdir = path.join(tmpdir(), 'vscode-zsh'); - await fs.mkdir(zdotdir, { recursive: true }); - const source = path.join(path.dirname(FileAccess.asFileUri('', require).fsPath), 'out/vs/workbench/contrib/terminal/browser/media/shellIntegration-rc.zsh'); - // TODO: Does filesToCopy make this unnecessary now? - try { - await fs.copyFile(source, path.join(zdotdir, '.zshrc')); - } catch { - // Swallow error, this should only happen when multiple users are on the same - // machine. Since the shell integration scripts rarely change, plus the other user - // should be using the same version of the server in this case, assume the script is - // fine if copy fails and swallow the error. - } - this._ptyOptions.env = this._ptyOptions.env || {}; - this._ptyOptions.env['ZDOTDIR'] = zdotdir; - delete this._ptyOptions.env['_ZDOTDIR']; - } - try { await this.setupPtyProcess(this.shellLaunchConfig, this._ptyOptions, injection); return undefined; diff --git a/src/vs/platform/terminal/test/common/terminalEnvironment.test.ts b/src/vs/platform/terminal/test/common/terminalEnvironment.test.ts index 2c58f9ec1fd..5f8aaca7e15 100644 --- a/src/vs/platform/terminal/test/common/terminalEnvironment.test.ts +++ b/src/vs/platform/terminal/test/common/terminalEnvironment.test.ts @@ -4,7 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { strictEqual } from 'assert'; -import { collapseTildePath } from 'vs/platform/terminal/common/terminalEnvironment'; +import { OperatingSystem, OS } from 'vs/base/common/platform'; +import { collapseTildePath, sanitizeCwd } from 'vs/platform/terminal/common/terminalEnvironment'; suite('terminalEnvironment', () => { suite('collapseTildePath', () => { @@ -37,4 +38,15 @@ suite('terminalEnvironment', () => { strictEqual(collapseTildePath('/foo/bar/baz', '/foo/', '/'), '~/bar/baz'); }); }); + suite('sanitizeCwd', () => { + if (OS === OperatingSystem.Windows) { + test('should make the Windows drive letter uppercase', () => { + strictEqual(sanitizeCwd('c:\\foo\\bar'), 'C:\\foo\\bar'); + }); + } + test('should remove any wrapping quotes', () => { + strictEqual(sanitizeCwd('\'/foo/bar\''), '/foo/bar'); + strictEqual(sanitizeCwd('"/foo/bar"'), '/foo/bar'); + }); + }); }); diff --git a/src/vs/platform/terminal/test/node/terminalEnvironment.test.ts b/src/vs/platform/terminal/test/node/terminalEnvironment.test.ts index 2ce4d1864e9..3ddda6e00d7 100644 --- a/src/vs/platform/terminal/test/node/terminalEnvironment.test.ts +++ b/src/vs/platform/terminal/test/node/terminalEnvironment.test.ts @@ -5,24 +5,33 @@ import { deepStrictEqual, ok, strictEqual } from 'assert'; import { homedir, userInfo } from 'os'; +import { isWindows } from 'vs/base/common/platform'; import { NullLogService } from 'vs/platform/log/common/log'; +import { IProductService } from 'vs/platform/product/common/productService'; import { ITerminalProcessOptions } from 'vs/platform/terminal/common/terminal'; import { getShellIntegrationInjection, IShellIntegrationConfigInjection } from 'vs/platform/terminal/node/terminalEnvironment'; -const enabledProcessOptions: ITerminalProcessOptions['shellIntegration'] = { enabled: true }; -const disabledProcessOptions: ITerminalProcessOptions['shellIntegration'] = { enabled: false }; +const enabledProcessOptions: Pick = { shellIntegration: { enabled: true }, windowsEnableConpty: true }; +const disabledProcessOptions: Pick = { shellIntegration: { enabled: false }, windowsEnableConpty: true }; +const winptyProcessOptions: Pick = { shellIntegration: { enabled: true }, windowsEnableConpty: false }; const pwshExe = process.platform === 'win32' ? 'pwsh.exe' : 'pwsh'; const repoRoot = process.platform === 'win32' ? process.cwd()[0].toLowerCase() + process.cwd().substring(1) : process.cwd(); const logService = new NullLogService(); +const productService = { applicationName: 'vscode' } as IProductService; const defaultEnvironment = {}; suite('platform - terminalEnvironment', () => { suite('getShellIntegrationInjection', () => { suite('should not enable', () => { test('when isFeatureTerminal or when no executable is provided', () => { - ok(!getShellIntegrationInjection({ executable: pwshExe, args: ['-l', '-NoLogo'], isFeatureTerminal: true }, enabledProcessOptions, defaultEnvironment, logService)); - ok(getShellIntegrationInjection({ executable: pwshExe, args: ['-l', '-NoLogo'], isFeatureTerminal: false }, enabledProcessOptions, defaultEnvironment, logService)); + ok(!getShellIntegrationInjection({ executable: pwshExe, args: ['-l', '-NoLogo'], isFeatureTerminal: true }, enabledProcessOptions, defaultEnvironment, logService, productService)); + ok(getShellIntegrationInjection({ executable: pwshExe, args: ['-l', '-NoLogo'], isFeatureTerminal: false }, enabledProcessOptions, defaultEnvironment, logService, productService)); }); + if (isWindows) { + test('when on windows with conpty false', () => { + ok(!getShellIntegrationInjection({ executable: pwshExe, args: ['-l'], isFeatureTerminal: false }, winptyProcessOptions, defaultEnvironment, logService, productService)); + }); + } }); suite('pwsh', () => { @@ -41,21 +50,21 @@ suite('platform - terminalEnvironment', () => { } }); test('when undefined, []', () => { - deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: [] }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult); - deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: undefined }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: [] }, enabledProcessOptions, defaultEnvironment, logService, productService), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: undefined }, enabledProcessOptions, defaultEnvironment, logService, productService), enabledExpectedResult); }); suite('when no logo', () => { test('array - case insensitive', () => { - deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-NoLogo'] }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult); - deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-NOLOGO'] }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult); - deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-nol'] }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult); - deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-NOL'] }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-NoLogo'] }, enabledProcessOptions, defaultEnvironment, logService, productService), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-NOLOGO'] }, enabledProcessOptions, defaultEnvironment, logService, productService), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-nol'] }, enabledProcessOptions, defaultEnvironment, logService, productService), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-NOL'] }, enabledProcessOptions, defaultEnvironment, logService, productService), enabledExpectedResult); }); test('string - case insensitive', () => { - deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-NoLogo' }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult); - deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-NOLOGO' }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult); - deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-nol' }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult); - deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-NOL' }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-NoLogo' }, enabledProcessOptions, defaultEnvironment, logService, productService), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-NOLOGO' }, enabledProcessOptions, defaultEnvironment, logService, productService), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-nol' }, enabledProcessOptions, defaultEnvironment, logService, productService), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-NOL' }, enabledProcessOptions, defaultEnvironment, logService, productService), enabledExpectedResult); }); }); }); @@ -72,23 +81,23 @@ suite('platform - terminalEnvironment', () => { } }); test('when array contains no logo and login', () => { - deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-l', '-NoLogo'] }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-l', '-NoLogo'] }, enabledProcessOptions, defaultEnvironment, logService, productService), enabledExpectedResult); }); test('when string', () => { - deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-l' }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-l' }, enabledProcessOptions, defaultEnvironment, logService, productService), enabledExpectedResult); }); }); suite('should not modify args', () => { test('when shell integration is disabled', () => { - strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-l'] }, disabledProcessOptions, defaultEnvironment, logService), undefined); - strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-l' }, disabledProcessOptions, defaultEnvironment, logService), undefined); - strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: undefined }, disabledProcessOptions, defaultEnvironment, logService), undefined); + strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-l'] }, disabledProcessOptions, defaultEnvironment, logService, productService), undefined); + strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-l' }, disabledProcessOptions, defaultEnvironment, logService, productService), undefined); + strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: undefined }, disabledProcessOptions, defaultEnvironment, logService, productService), undefined); }); test('when using unrecognized arg', () => { - strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-l', '-NoLogo', '-i'] }, disabledProcessOptions, defaultEnvironment, logService), undefined); + strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-l', '-NoLogo', '-i'] }, disabledProcessOptions, defaultEnvironment, logService, productService), undefined); }); test('when using unrecognized arg (string)', () => { - strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-i' }, disabledProcessOptions, defaultEnvironment, logService), undefined); + strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-i' }, disabledProcessOptions, defaultEnvironment, logService, productService), undefined); }); }); }); @@ -127,37 +136,37 @@ suite('platform - terminalEnvironment', () => { ok(result.filesToCopy[3].source.match(expectedSources[3])); } test('when undefined, []', () => { - const result1 = getShellIntegrationInjection({ executable: 'zsh', args: [] }, enabledProcessOptions, defaultEnvironment, logService); + const result1 = getShellIntegrationInjection({ executable: 'zsh', args: [] }, enabledProcessOptions, defaultEnvironment, logService, productService); deepStrictEqual(result1?.newArgs, ['-i']); assertIsEnabled(result1); - const result2 = getShellIntegrationInjection({ executable: 'zsh', args: undefined }, enabledProcessOptions, defaultEnvironment, logService); + const result2 = getShellIntegrationInjection({ executable: 'zsh', args: undefined }, enabledProcessOptions, defaultEnvironment, logService, productService); deepStrictEqual(result2?.newArgs, ['-i']); assertIsEnabled(result2); }); suite('should incorporate login arg', () => { test('when array', () => { - const result = getShellIntegrationInjection({ executable: 'zsh', args: ['-l'] }, enabledProcessOptions, defaultEnvironment, logService); + const result = getShellIntegrationInjection({ executable: 'zsh', args: ['-l'] }, enabledProcessOptions, defaultEnvironment, logService, productService); deepStrictEqual(result?.newArgs, ['-il']); assertIsEnabled(result); }); }); suite('should not modify args', () => { test('when shell integration is disabled', () => { - strictEqual(getShellIntegrationInjection({ executable: 'zsh', args: ['-l'] }, disabledProcessOptions, defaultEnvironment, logService), undefined); - strictEqual(getShellIntegrationInjection({ executable: 'zsh', args: undefined }, disabledProcessOptions, defaultEnvironment, logService), undefined); + strictEqual(getShellIntegrationInjection({ executable: 'zsh', args: ['-l'] }, disabledProcessOptions, defaultEnvironment, logService, productService), undefined); + strictEqual(getShellIntegrationInjection({ executable: 'zsh', args: undefined }, disabledProcessOptions, defaultEnvironment, logService, productService), undefined); }); test('when using unrecognized arg', () => { - strictEqual(getShellIntegrationInjection({ executable: 'zsh', args: ['-l', '-fake'] }, disabledProcessOptions, defaultEnvironment, logService), undefined); + strictEqual(getShellIntegrationInjection({ executable: 'zsh', args: ['-l', '-fake'] }, disabledProcessOptions, defaultEnvironment, logService, productService), undefined); }); }); suite('should incorporate global ZDOTDIR env variable', () => { test('when custom ZDOTDIR', () => { - const result1 = getShellIntegrationInjection({ executable: 'zsh', args: [] }, enabledProcessOptions, { ...defaultEnvironment, ZDOTDIR: customZdotdir }, logService); + const result1 = getShellIntegrationInjection({ executable: 'zsh', args: [] }, enabledProcessOptions, { ...defaultEnvironment, ZDOTDIR: customZdotdir }, logService, productService); deepStrictEqual(result1?.newArgs, ['-i']); assertIsEnabled(result1, customZdotdir); }); test('when undefined', () => { - const result1 = getShellIntegrationInjection({ executable: 'zsh', args: [] }, enabledProcessOptions, undefined, logService); + const result1 = getShellIntegrationInjection({ executable: 'zsh', args: [] }, enabledProcessOptions, undefined, logService, productService); deepStrictEqual(result1?.newArgs, ['-i']); assertIsEnabled(result1); }); @@ -176,9 +185,9 @@ suite('platform - terminalEnvironment', () => { VSCODE_INJECTION: '1' } }); - deepStrictEqual(getShellIntegrationInjection({ executable: 'bash', args: [] }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult); - deepStrictEqual(getShellIntegrationInjection({ executable: 'bash', args: '' }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult); - deepStrictEqual(getShellIntegrationInjection({ executable: 'bash', args: undefined }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: 'bash', args: [] }, enabledProcessOptions, defaultEnvironment, logService, productService), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: 'bash', args: '' }, enabledProcessOptions, defaultEnvironment, logService, productService), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: 'bash', args: undefined }, enabledProcessOptions, defaultEnvironment, logService, productService), enabledExpectedResult); }); suite('should set login env variable and not modify args', () => { const enabledExpectedResult = Object.freeze({ @@ -192,16 +201,16 @@ suite('platform - terminalEnvironment', () => { } }); test('when array', () => { - deepStrictEqual(getShellIntegrationInjection({ executable: 'bash', args: ['-l'] }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: 'bash', args: ['-l'] }, enabledProcessOptions, defaultEnvironment, logService, productService), enabledExpectedResult); }); }); suite('should not modify args', () => { test('when shell integration is disabled', () => { - strictEqual(getShellIntegrationInjection({ executable: 'bash', args: ['-l'] }, disabledProcessOptions, defaultEnvironment, logService), undefined); - strictEqual(getShellIntegrationInjection({ executable: 'bash', args: undefined }, disabledProcessOptions, defaultEnvironment, logService), undefined); + strictEqual(getShellIntegrationInjection({ executable: 'bash', args: ['-l'] }, disabledProcessOptions, defaultEnvironment, logService, productService), undefined); + strictEqual(getShellIntegrationInjection({ executable: 'bash', args: undefined }, disabledProcessOptions, defaultEnvironment, logService, productService), undefined); }); test('when custom array entry', () => { - strictEqual(getShellIntegrationInjection({ executable: 'bash', args: ['-l', '-i'] }, disabledProcessOptions, defaultEnvironment, logService), undefined); + strictEqual(getShellIntegrationInjection({ executable: 'bash', args: ['-l', '-i'] }, disabledProcessOptions, defaultEnvironment, logService, productService), undefined); }); }); }); diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 01f9f4743ad..d4ba695131b 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -387,8 +387,8 @@ export const editorActiveLinkForeground = registerColor('editorLink.activeForegr /** * Inline hints */ -export const editorInlayHintForeground = registerColor('editorInlayHint.foreground', { dark: transparent(badgeForeground, .8), light: transparent(badgeForeground, .8), hcDark: badgeForeground, hcLight: badgeForeground }, nls.localize('editorInlayHintForeground', 'Foreground color of inline hints')); -export const editorInlayHintBackground = registerColor('editorInlayHint.background', { dark: transparent(badgeBackground, .6), light: transparent(badgeBackground, .3), hcDark: badgeBackground, hcLight: badgeBackground }, nls.localize('editorInlayHintBackground', 'Background color of inline hints')); +export const editorInlayHintForeground = registerColor('editorInlayHint.foreground', { dark: badgeForeground, light: badgeForeground, hcDark: Color.black, hcLight: badgeForeground }, nls.localize('editorInlayHintForeground', 'Foreground color of inline hints')); +export const editorInlayHintBackground = registerColor('editorInlayHint.background', { dark: transparent(badgeBackground, .8), light: transparent(badgeBackground, .6), hcDark: '#f38518', hcLight: badgeBackground }, nls.localize('editorInlayHintBackground', 'Background color of inline hints')); export const editorInlayHintTypeForeground = registerColor('editorInlayHint.typeForeground', { dark: editorInlayHintForeground, light: editorInlayHintForeground, hcDark: editorInlayHintForeground, hcLight: editorInlayHintForeground }, nls.localize('editorInlayHintForegroundTypes', 'Foreground color of inline hints for types')); export const editorInlayHintTypeBackground = registerColor('editorInlayHint.typeBackground', { dark: editorInlayHintBackground, light: editorInlayHintBackground, hcDark: editorInlayHintBackground, hcLight: editorInlayHintBackground }, nls.localize('editorInlayHintBackgroundTypes', 'Background color of inline hints for types')); export const editorInlayHintParameterForeground = registerColor('editorInlayHint.parameterForeground', { dark: editorInlayHintForeground, light: editorInlayHintForeground, hcDark: editorInlayHintForeground, hcLight: editorInlayHintForeground }, nls.localize('editorInlayHintForegroundParameter', 'Foreground color of inline hints for parameters')); @@ -406,8 +406,8 @@ export const editorLightBulbAutoFixForeground = registerColor('editorLightBulbAu export const defaultInsertColor = new Color(new RGBA(155, 185, 85, .2)); export const defaultRemoveColor = new Color(new RGBA(255, 0, 0, .2)); -export const diffInserted = registerColor('diffEditor.insertedTextBackground', { dark: '#9ccc2c33', light: '#9ccc2c66', hcDark: null, hcLight: null }, nls.localize('diffEditorInserted', 'Background color for text that got inserted. The color must not be opaque so as not to hide underlying decorations.'), true); -export const diffRemoved = registerColor('diffEditor.removedTextBackground', { dark: '#ff000066', light: '#ff00004d', hcDark: null, hcLight: null }, nls.localize('diffEditorRemoved', 'Background color for text that got removed. The color must not be opaque so as not to hide underlying decorations.'), true); +export const diffInserted = registerColor('diffEditor.insertedTextBackground', { dark: '#9ccc2c33', light: '#9ccc2c40', hcDark: null, hcLight: null }, nls.localize('diffEditorInserted', 'Background color for text that got inserted. The color must not be opaque so as not to hide underlying decorations.'), true); +export const diffRemoved = registerColor('diffEditor.removedTextBackground', { dark: '#ff000033', light: '#ff000033', hcDark: null, hcLight: null }, nls.localize('diffEditorRemoved', 'Background color for text that got removed. The color must not be opaque so as not to hide underlying decorations.'), true); export const diffInsertedLine = registerColor('diffEditor.insertedLineBackground', { dark: defaultInsertColor, light: defaultInsertColor, hcDark: null, hcLight: null }, nls.localize('diffEditorInsertedLines', 'Background color for lines that got inserted. The color must not be opaque so as not to hide underlying decorations.'), true); export const diffRemovedLine = registerColor('diffEditor.removedLineBackground', { dark: defaultRemoveColor, light: defaultRemoveColor, hcDark: null, hcLight: null }, nls.localize('diffEditorRemovedLines', 'Background color for lines that got removed. The color must not be opaque so as not to hide underlying decorations.'), true); diff --git a/src/vs/platform/tunnel/common/tunnel.ts b/src/vs/platform/tunnel/common/tunnel.ts index 29b07680481..9fd03586d78 100644 --- a/src/vs/platform/tunnel/common/tunnel.ts +++ b/src/vs/platform/tunnel/common/tunnel.ts @@ -126,9 +126,11 @@ export interface ITunnelService { canTunnel(uri: URI): boolean; openTunnel(addressProvider: IAddressProvider | undefined, remoteHost: string | undefined, remotePort: number, localPort?: number, elevateIfNeeded?: boolean, privacy?: string, protocol?: string): Promise | undefined; + setEnvironmentTunnel(remoteHost: string, remotePort: number, localAddress: string, privacy: string, protocol: string): void; closeTunnel(remoteHost: string, remotePort: number): Promise; setTunnelProvider(provider: ITunnelProvider | undefined): IDisposable; setTunnelFeatures(features: TunnelProviderFeatures): void; + isPortPrivileged(port: number): boolean; } export function extractLocalHostUriMetaDataForPortMapping(uri: URI): { address: string; port: number } | undefined { @@ -269,6 +271,17 @@ export abstract class AbstractTunnelService implements ITunnelService { this._tunnels.clear(); } + setEnvironmentTunnel(remoteHost: string, remotePort: number, localAddress: string, privacy: string, protocol: string): void { + this.addTunnelToMap(remoteHost, remotePort, Promise.resolve({ + tunnelRemoteHost: remoteHost, + tunnelRemotePort: remotePort, + localAddress, + privacy, + protocol, + dispose: () => Promise.resolve() + })); + } + openTunnel(addressProvider: IAddressProvider | undefined, remoteHost: string | undefined, remotePort: number, localPort?: number, elevateIfNeeded: boolean = false, privacy?: string, protocol?: string): Promise | undefined { this.logService.trace(`ForwardedPorts: (TunnelService) openTunnel request for ${remoteHost}:${remotePort} on local port ${localPort}.`); if (!addressProvider) { @@ -402,6 +415,24 @@ export abstract class AbstractTunnelService implements ITunnelService { return !!extractLocalHostUriMetaDataForPortMapping(uri); } + public abstract isPortPrivileged(port: number): boolean; + + protected doIsPortPrivileged(port: number, isWindows: boolean, isMacintosh: boolean, osRelease: string): boolean { + if (isWindows) { + return false; + } else if (isMacintosh) { + const osVersion = (/(\d+)\.(\d+)\.(\d+)/g).exec(osRelease); + if (osVersion?.length === 4) { + const major = parseInt(osVersion[1]); + const minor = parseInt(osVersion[2]); + if (((major > 10) || (major === 10 && minor >= 14))) { + return false; + } + } + } + return port < 1024; + } + protected abstract retainOrCreateTunnel(addressProvider: IAddressProvider, remoteHost: string, remotePort: number, localPort: number | undefined, elevateIfNeeded: boolean, privacy?: string, protocol?: string): Promise | undefined; protected createWithProvider(tunnelProvider: ITunnelProvider, remoteHost: string, remotePort: number, localPort: number | undefined, elevateIfNeeded: boolean, privacy?: string, protocol?: string): Promise | undefined { @@ -409,7 +440,7 @@ export abstract class AbstractTunnelService implements ITunnelService { const key = remotePort; this._factoryInProgress.add(key); const preferredLocalPort = localPort === undefined ? remotePort : localPort; - const creationInfo = { elevationRequired: elevateIfNeeded ? isPortPrivileged(preferredLocalPort) : false }; + const creationInfo = { elevationRequired: elevateIfNeeded ? this.isPortPrivileged(preferredLocalPort) : false }; const tunnelOptions: TunnelOptions = { remoteAddress: { host: remoteHost, port: remotePort }, localAddressPort: localPort, privacy, public: privacy ? (privacy !== TunnelPrivacyId.Private) : undefined, protocol }; const tunnel = tunnelProvider.forwardPort(tunnelOptions, creationInfo); if (tunnel) { diff --git a/src/vs/platform/tunnel/node/tunnelService.ts b/src/vs/platform/tunnel/node/tunnelService.ts index 7cdd91afd53..50a7cb920af 100644 --- a/src/vs/platform/tunnel/node/tunnelService.ts +++ b/src/vs/platform/tunnel/node/tunnelService.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as net from 'net'; +import * as os from 'os'; import { BROWSER_RESTRICTED_PORTS, findFreePortFaster } from 'vs/base/node/ports'; import { NodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; import { nodeSocketFactory } from 'vs/platform/remote/node/nodeSocketFactory'; @@ -16,6 +17,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { connectRemoteAgentTunnel, IAddressProvider, IConnectionOptions, ISocketFactory } from 'vs/platform/remote/common/remoteAgentConnection'; import { AbstractTunnelService, isAllInterfaces, ISharedTunnelsService as ISharedTunnelsService, isLocalhost, ITunnelService, RemoteTunnel, TunnelPrivacyId } from 'vs/platform/tunnel/common/tunnel'; import { ISignService } from 'vs/platform/sign/common/sign'; +import { isMacintosh, isWindows } from 'vs/base/common/platform'; async function createRemoteTunnel(options: IConnectionOptions, defaultTunnelHost: string, tunnelRemoteHost: string, tunnelRemotePort: number, tunnelLocalPort?: number): Promise { let readyTunnel: NodeRemoteTunnel | undefined; @@ -165,6 +167,10 @@ export class BaseTunnelService extends AbstractTunnelService { return (!settingValue || settingValue === 'localhost') ? '127.0.0.1' : '0.0.0.0'; } + public isPortPrivileged(port: number): boolean { + return this.doIsPortPrivileged(port, isWindows, isMacintosh, os.release()); + } + protected retainOrCreateTunnel(addressProvider: IAddressProvider, remoteHost: string, remotePort: number, localPort: number | undefined, elevateIfNeeded: boolean, privacy?: string, protocol?: string): Promise | undefined { const existing = this.getTunnelFromMap(remoteHost, remotePort); if (existing) { diff --git a/src/vs/platform/uriIdentity/common/uriIdentityService.ts b/src/vs/platform/uriIdentity/common/uriIdentityService.ts index a0bbc55fa66..a4a23090906 100644 --- a/src/vs/platform/uriIdentity/common/uriIdentityService.ts +++ b/src/vs/platform/uriIdentity/common/uriIdentityService.ts @@ -5,7 +5,7 @@ import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { URI } from 'vs/base/common/uri'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IFileService, FileSystemProviderCapabilities, IFileSystemProviderCapabilitiesChangeEvent, IFileSystemProviderRegistrationEvent } from 'vs/platform/files/common/files'; import { ExtUri, IExtUri, normalizePath } from 'vs/base/common/resources'; import { SkipList } from 'vs/base/common/skipList'; @@ -114,4 +114,4 @@ export class UriIdentityService implements IUriIdentityService { } } -registerSingleton(IUriIdentityService, UriIdentityService, true); +registerSingleton(IUriIdentityService, UriIdentityService, InstantiationType.Delayed); diff --git a/src/vs/platform/userDataProfile/common/userDataProfile.ts b/src/vs/platform/userDataProfile/common/userDataProfile.ts index 668a16b7c50..2207f1a08e4 100644 --- a/src/vs/platform/userDataProfile/common/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/common/userDataProfile.ts @@ -106,6 +106,7 @@ export interface IUserDataProfilesService { readonly onDidResetWorkspaces: Event; + isEnabled(): boolean; createNamedProfile(name: string, options?: IUserDataProfileOptions, workspaceIdentifier?: WorkspaceIdentifier): Promise; createTransientProfile(workspaceIdentifier?: WorkspaceIdentifier): Promise; createProfile(id: string, name: string, options?: IUserDataProfileOptions, workspaceIdentifier?: WorkspaceIdentifier): Promise; @@ -225,6 +226,10 @@ export class UserDataProfilesService extends Disposable implements IUserDataProf } } + isEnabled(): boolean { + return this.enabled; + } + protected _profilesObject: UserDataProfilesObject | undefined; protected get profilesObject(): UserDataProfilesObject { if (!this._profilesObject) { @@ -354,7 +359,12 @@ export class UserDataProfilesService extends Disposable implements IUserDataProf joiners.push(promise); } }); - await Promises.settled(joiners); + + try { + await Promise.allSettled(joiners); + } catch (error) { + this.logService.error(error); + } if (profile.id === this.profilesObject.emptyWindow?.id) { this.profilesObject.emptyWindow = undefined; diff --git a/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts b/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts index a5776c1fa3d..f5c902cc32a 100644 --- a/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts @@ -18,7 +18,6 @@ import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; export const IUserDataProfilesMainService = refineServiceDecorator(IUserDataProfilesService); export interface IUserDataProfilesMainService extends IUserDataProfilesService { - isEnabled(): boolean; getOrSetProfileForWorkspace(workspaceIdentifier: WorkspaceIdentifier, profileToSet?: IUserDataProfile): IUserDataProfile; setProfileForWorkspaceSync(workspaceIdentifier: WorkspaceIdentifier, profileToSet: IUserDataProfile): void; checkAndCreateProfileFromCli(args: NativeParsedArgs): Promise | undefined; @@ -48,10 +47,6 @@ export class UserDataProfilesMainService extends UserDataProfilesService impleme } } - isEnabled(): boolean { - return this.enabled; - } - checkAndCreateProfileFromCli(args: NativeParsedArgs): Promise | undefined { if (!this.isEnabled()) { return undefined; diff --git a/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts b/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts index af23d7e7f30..6bcd8ab1c3e 100644 --- a/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts @@ -29,6 +29,8 @@ export class UserDataProfilesNativeService extends Disposable implements IUserDa readonly onDidResetWorkspaces: Event; + private enabled: boolean = true; + constructor( profiles: readonly UriDto[], @IMainProcessService mainProcessService: IMainProcessService, @@ -48,6 +50,14 @@ export class UserDataProfilesNativeService extends Disposable implements IUserDa this.onDidResetWorkspaces = this.channel.listen('onDidResetWorkspaces'); } + setEnablement(enabled: boolean) { + this.enabled = enabled; + } + + isEnabled(): boolean { + return this.enabled; + } + async createNamedProfile(name: string, options?: IUserDataProfileOptions, workspaceIdentifier?: WorkspaceIdentifier): Promise { const result = await this.channel.call>('createNamedProfile', [name, options, workspaceIdentifier]); return reviveProfile(result, this.profilesHome.scheme); diff --git a/src/vs/platform/userDataSync/common/extensionsMerge.ts b/src/vs/platform/userDataSync/common/extensionsMerge.ts index 8def92906ef..1b93b734b38 100644 --- a/src/vs/platform/userDataSync/common/extensionsMerge.ts +++ b/src/vs/platform/userDataSync/common/extensionsMerge.ts @@ -6,7 +6,7 @@ import { IStringDictionary } from 'vs/base/common/collections'; import { deepClone, equals } from 'vs/base/common/objects'; import * as semver from 'vs/base/common/semver/semver'; -import { isUndefined } from 'vs/base/common/types'; +import { assertIsDefined } from 'vs/base/common/types'; import { IExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { ISyncExtension, ISyncExtensionWithVersion } from 'vs/platform/userDataSync/common/userDataSync'; @@ -15,7 +15,7 @@ export interface IMergeResult { readonly remote: { added: ISyncExtension[]; removed: ISyncExtension[]; updated: ISyncExtension[]; all: ISyncExtension[] } | null; } -export function merge(localExtensions: ISyncExtensionWithVersion[], remoteExtensions: ISyncExtension[] | null, lastSyncExtensions: ISyncExtension[] | null, skippedExtensions: ISyncExtension[], ignoredExtensions: string[]): IMergeResult { +export function merge(localExtensions: ISyncExtensionWithVersion[], remoteExtensions: ISyncExtension[] | null, lastSyncExtensions: ISyncExtension[] | null, skippedExtensions: ISyncExtension[], ignoredExtensions: string[], lastSyncBuiltinExtensions: IExtensionIdentifier[]): IMergeResult { const added: ISyncExtension[] = []; const removed: IExtensionIdentifier[] = []; const updated: ISyncExtensionWithVersion[] = []; @@ -57,138 +57,164 @@ export function merge(localExtensions: ISyncExtensionWithVersion[], remoteExtens }; const localExtensionsMap: Map = localExtensions.reduce(addExtensionToMap, new Map()); const remoteExtensionsMap = remoteExtensions.reduce(addExtensionToMap, new Map()); - const newRemoteExtensionsMap = remoteExtensions.reduce((map: Map, extension: ISyncExtension) => { - const key = getKey(extension); - extension = deepClone(extension); - const localExtension = localExtensionsMap.get(key); - if (localExtension) { - if (localExtension.installed) { - extension.installed = true; - } - if (!extension.version) { - extension.version = localExtension.version; - } - } - return addExtensionToMap(map, extension); - }, new Map()); + const newRemoteExtensionsMap = remoteExtensions.reduce((map: Map, extension: ISyncExtension) => addExtensionToMap(map, deepClone(extension)), new Map()); const lastSyncExtensionsMap = lastSyncExtensions ? lastSyncExtensions.reduce(addExtensionToMap, new Map()) : null; const skippedExtensionsMap = skippedExtensions.reduce(addExtensionToMap, new Map()); const ignoredExtensionsSet = ignoredExtensions.reduce((set, id) => { const uuid = uuids.get(id.toLowerCase()); return set.add(uuid ? `uuid:${uuid}` : `id:${id.toLowerCase()}`); }, new Set()); + const lastSyncBuiltinExtensionsSet = lastSyncBuiltinExtensions.reduce((set, { id, uuid }) => { + uuid = uuid ?? uuids.get(id.toLowerCase()); + return set.add(uuid ? `uuid:${uuid}` : `id:${id.toLowerCase()}`); + }, new Set()); - const localToRemote = compare(localExtensionsMap, remoteExtensionsMap, ignoredExtensionsSet); + const localToRemote = compare(localExtensionsMap, remoteExtensionsMap, ignoredExtensionsSet, false); if (localToRemote.added.size > 0 || localToRemote.removed.size > 0 || localToRemote.updated.size > 0) { - const baseToLocal = compare(lastSyncExtensionsMap, localExtensionsMap, ignoredExtensionsSet); - const baseToRemote = compare(lastSyncExtensionsMap, remoteExtensionsMap, ignoredExtensionsSet); + const baseToLocal = compare(lastSyncExtensionsMap, localExtensionsMap, ignoredExtensionsSet, false); + const baseToRemote = compare(lastSyncExtensionsMap, remoteExtensionsMap, ignoredExtensionsSet, true); - const merge = (key: string, updatedInRemote: boolean): ISyncExtensionWithVersion | undefined => { - const localExtension = localExtensionsMap.get(key); - if (localExtension) { - const remoteExtension = remoteExtensionsMap.get(key)!; - const mergedExtension = updatedInRemote ? remoteExtension : localExtension; - return { - ...mergedExtension, - version: remoteExtension.version && (!localExtension.installed || semver.gt(remoteExtension.version, localExtension.version)) ? remoteExtension.version : localExtension.version, - state: mergeExtensionState(localExtension, remoteExtension, lastSyncExtensionsMap?.get(key)), - preRelease: isUndefined(mergedExtension.preRelease) /* from older client*/ ? localExtension.preRelease - : (localExtension.installed ? mergedExtension.preRelease : remoteExtension.preRelease) - }; - - } - return undefined; + const merge = (key: string, localExtension: ISyncExtensionWithVersion, remoteExtension: ISyncExtension, preferred: ISyncExtension): ISyncExtensionWithVersion => { + return { + ...preferred, + installed: localExtension.installed || remoteExtension.installed, + version: remoteExtension.version && (!localExtension.installed || semver.gt(remoteExtension.version, localExtension.version)) ? remoteExtension.version : localExtension.version, + state: mergeExtensionState(localExtension, remoteExtension, lastSyncExtensionsMap?.get(key)), + preRelease: (localExtension.installed ? preferred.preRelease : remoteExtension.preRelease) ?? /* from older client*/ localExtension.preRelease + }; }; - // Remotely removed extension. + // Remotely removed extension => exist in base and does not in remote for (const key of baseToRemote.removed.values()) { - const e = localExtensionsMap.get(key); - if (e) { - removed.push(e.identifier); + const localExtension = localExtensionsMap.get(key); + if (!localExtension) { + continue; } + + const baseExtension = assertIsDefined(lastSyncExtensionsMap?.get(key)); + const wasAnInstalledExtensionDuringLastSync = !lastSyncBuiltinExtensionsSet.has(key) && baseExtension.installed; + if (localExtension.installed && wasAnInstalledExtensionDuringLastSync /* It is an installed extension now and during last sync */) { + // Installed extension is removed from remote. Remove it from local. + removed.push(localExtension.identifier); + } else { + // Add to remote: It is a builtin extenision or got installed after last sync + newRemoteExtensionsMap.set(key, localExtension); + } + } - // Remotely added extension + // Remotely added extension => does not exist in base and exist in remote for (const key of baseToRemote.added.values()) { - // Got added in local - if (baseToLocal.added.has(key)) { + const remoteExtension = assertIsDefined(remoteExtensionsMap.get(key)); + const localExtension = localExtensionsMap.get(key); + + // Also exist in local + if (localExtension) { // Is different from local to remote if (localToRemote.updated.has(key)) { - const mergedExtension = merge(key, true); - if (mergedExtension) { + const mergedExtension = merge(key, localExtension, remoteExtension, remoteExtension); + // Update locally only when the extension has changes in properties other than installed poperty + if (!areSame(localExtension, remoteExtension, false, false)) { updated.push(massageOutgoingExtension(mergedExtension, key)); - newRemoteExtensionsMap.set(key, mergedExtension); } + newRemoteExtensionsMap.set(key, mergedExtension); } } else { - // Add only installed extension to local - const remoteExtension = remoteExtensionsMap.get(key)!; + // Add only if the extension is an installed extension if (remoteExtension.installed) { added.push(massageOutgoingExtension(remoteExtension, key)); } } } - // Remotely updated extensions + // Remotely updated extension => exist in base and remote for (const key of baseToRemote.updated.values()) { - // Update in local always - const mergedExtension = merge(key, true); - if (mergedExtension) { - updated.push(massageOutgoingExtension(mergedExtension, key)); - newRemoteExtensionsMap.set(key, mergedExtension); - } - } + const remoteExtension = assertIsDefined(remoteExtensionsMap.get(key)); + const baseExtension = assertIsDefined(lastSyncExtensionsMap?.get(key)); + const localExtension = localExtensionsMap.get(key); - // Locally added extensions - for (const key of baseToLocal.added.values()) { - // Not there in remote - if (!baseToRemote.added.has(key)) { - newRemoteExtensionsMap.set(key, localExtensionsMap.get(key)!); - } - } - - // Locally updated extensions - for (const key of baseToLocal.updated.values()) { - // If removed in remote - if (baseToRemote.removed.has(key)) { - continue; - } - - // If not updated in remote - if (!baseToRemote.updated.has(key)) { - const mergedExtension = merge(key, false); - if (mergedExtension) { - // Retain installed property - if (newRemoteExtensionsMap.get(key)?.installed) { - mergedExtension.installed = true; - } + // Also exist in local + if (localExtension) { + const wasAnInstalledExtensionDuringLastSync = !lastSyncBuiltinExtensionsSet.has(key) && baseExtension.installed; + if (wasAnInstalledExtensionDuringLastSync && localExtension.installed && !remoteExtension.installed) { + // Remove it locally if it is installed locally and not remotely + removed.push(localExtension.identifier); + } else { + // Update in local always + const mergedExtension = merge(key, localExtension, remoteExtension, remoteExtension); + updated.push(massageOutgoingExtension(mergedExtension, key)); newRemoteExtensionsMap.set(key, mergedExtension); } } + // Add it locally if does not exist locally and installed remotely + else if (remoteExtension.installed) { + added.push(massageOutgoingExtension(remoteExtension, key)); + } + } - // Locally removed extensions - for (const key of baseToLocal.removed.values()) { - // If not skipped and not updated in remote - if (!skippedExtensionsMap.has(key) && !baseToRemote.updated.has(key)) { - // Remove only if it is an installed extension - if (lastSyncExtensionsMap?.get(key)?.installed) { - newRemoteExtensionsMap.delete(key); - } + // Locally added extension => does not exist in base and exist in local + for (const key of baseToLocal.added.values()) { + // If added in remote (already handled) + if (baseToRemote.added.has(key)) { + continue; } + newRemoteExtensionsMap.set(key, assertIsDefined(localExtensionsMap.get(key))); + } + + // Locally updated extension => exist in base and local + for (const key of baseToLocal.updated.values()) { + // If removed in remote (already handled) + if (baseToRemote.removed.has(key)) { + continue; + } + // If updated in remote (already handled) + if (baseToRemote.updated.has(key)) { + continue; + } + const localExtension = assertIsDefined(localExtensionsMap.get(key)); + const remoteExtension = assertIsDefined(remoteExtensionsMap.get(key)); + // Update remotely + newRemoteExtensionsMap.set(key, merge(key, localExtension, remoteExtension, localExtension)); + } + + // Locally removed extensions => exist in base and does not exist in local + for (const key of baseToLocal.removed.values()) { + // If updated in remote (already handled) + if (baseToRemote.updated.has(key)) { + continue; + } + // If removed in remote (already handled) + if (baseToRemote.removed.has(key)) { + continue; + } + // Skipped + if (skippedExtensionsMap.has(key)) { + continue; + } + // Skip if it is a builtin extension + if (!assertIsDefined(remoteExtensionsMap.get(key)).installed) { + continue; + } + // Skip if it was a builtin extension during last sync + if (lastSyncBuiltinExtensionsSet.has(key) || !assertIsDefined(lastSyncExtensionsMap?.get(key)).installed) { + continue; + } + newRemoteExtensionsMap.delete(key); } } const remote: ISyncExtension[] = []; - const remoteChanges = compare(remoteExtensionsMap, newRemoteExtensionsMap, new Set(), { checkInstalledProperty: true, checkVersionProperty: true }); - if (remoteChanges.added.size > 0 || remoteChanges.updated.size > 0 || remoteChanges.removed.size > 0) { + const remoteChanges = compare(remoteExtensionsMap, newRemoteExtensionsMap, new Set(), true); + const hasRemoteChanges = remoteChanges.added.size > 0 || remoteChanges.updated.size > 0 || remoteChanges.removed.size > 0; + if (hasRemoteChanges) { newRemoteExtensionsMap.forEach((value, key) => remote.push(massageOutgoingExtension(value, key))); } return { local: { added, removed, updated }, - remote: remote.length ? { + remote: hasRemoteChanges ? { added: [...remoteChanges.added].map(id => newRemoteExtensionsMap.get(id)!), updated: [...remoteChanges.updated].map(id => newRemoteExtensionsMap.get(id)!), removed: [...remoteChanges.removed].map(id => remoteExtensionsMap.get(id)!), @@ -197,7 +223,7 @@ export function merge(localExtensions: ISyncExtensionWithVersion[], remoteExtens }; } -function compare(from: Map | null, to: Map, ignoredExtensions: Set, { checkInstalledProperty, checkVersionProperty }: { checkInstalledProperty: boolean; checkVersionProperty: boolean } = { checkInstalledProperty: false, checkVersionProperty: false }): { added: Set; removed: Set; updated: Set } { +function compare(from: Map | null, to: Map, ignoredExtensions: Set, checkVersionProperty: boolean): { added: Set; removed: Set; updated: Set } { const fromKeys = from ? [...from.keys()].filter(key => !ignoredExtensions.has(key)) : []; const toKeys = [...to.keys()].filter(key => !ignoredExtensions.has(key)); const added = toKeys.filter(key => fromKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set()); @@ -210,13 +236,7 @@ function compare(from: Map | null, to: Map | null, to: Map | undefined { const localState = localExtension.state; const remoteState = remoteExtension.state; diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index 95f526def9a..118b998774a 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -9,15 +9,19 @@ import { IStringDictionary } from 'vs/base/common/collections'; import { getErrorMessage } from 'vs/base/common/errors'; import { Event } from 'vs/base/common/event'; import { toFormattedString } from 'vs/base/common/jsonFormatter'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { compare } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IExtensionGalleryService, IExtensionManagementService, IGlobalExtensionEnablementService, ILocalExtension, ExtensionManagementError, ExtensionManagementErrorCode, IGalleryExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService'; +import { IExtensionGalleryService, IExtensionManagementService, IGlobalExtensionEnablementService, ILocalExtension, ExtensionManagementError, ExtensionManagementErrorCode, IGalleryExtension, DISABLED_EXTENSIONS_STORAGE_PATH } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { IExtensionStorageService } from 'vs/platform/extensionManagement/common/extensionStorage'; +import { ExtensionStorageService, IExtensionStorageService } from 'vs/platform/extensionManagement/common/extensionStorage'; import { ExtensionType, IExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { ILogService } from 'vs/platform/log/common/log'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -27,17 +31,21 @@ import { AbstractInitializer, AbstractSynchroniser, IAcceptResult, IMergeResult, import { IMergeResult as IExtensionMergeResult, merge } from 'vs/platform/userDataSync/common/extensionsMerge'; import { IIgnoredExtensionsManagementService } from 'vs/platform/userDataSync/common/ignoredExtensions'; import { Change, IRemoteUserData, ISyncData, ISyncExtension, ISyncExtensionWithVersion, ISyncResourceHandle, IUserDataSyncBackupStoreService, IUserDataSynchroniser, IUserDataSyncLogService, IUserDataSyncEnablementService, IUserDataSyncStoreService, SyncResource, USER_DATA_SYNC_SCHEME } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncProfilesStorageService } from 'vs/platform/userDataSync/common/userDataSyncProfilesStorageService'; type IExtensionResourceMergeResult = IAcceptResult & IExtensionMergeResult; interface IExtensionResourcePreview extends IResourcePreview { readonly localExtensions: ISyncExtensionWithVersion[]; + readonly remoteExtensions: ISyncExtension[] | null; readonly skippedExtensions: ISyncExtension[]; + readonly builtinExtensions: IExtensionIdentifier[]; readonly previewResult: IExtensionResourceMergeResult; } interface ILastSyncUserData extends IRemoteUserData { skippedExtensions: ISyncExtension[] | undefined; + builtinExtensions: IExtensionIdentifier[] | undefined; } async function parseAndMigrateExtensions(syncData: ISyncData, extensionManagementService: IExtensionManagementService): Promise { @@ -98,15 +106,16 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse @IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService, @IUserDataSyncBackupStoreService userDataSyncBackupStoreService: IUserDataSyncBackupStoreService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, - @IGlobalExtensionEnablementService private readonly extensionEnablementService: IGlobalExtensionEnablementService, @IIgnoredExtensionsManagementService private readonly ignoredExtensionsManagementService: IIgnoredExtensionsManagementService, @IUserDataSyncLogService logService: IUserDataSyncLogService, @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, @IConfigurationService configurationService: IConfigurationService, @IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService, @ITelemetryService telemetryService: ITelemetryService, - @IExtensionStorageService private readonly extensionStorageService: IExtensionStorageService, - @IUriIdentityService uriIdentityService: IUriIdentityService + @IExtensionStorageService extensionStorageService: IExtensionStorageService, + @IUriIdentityService uriIdentityService: IUriIdentityService, + @IUserDataSyncProfilesStorageService private readonly userDataSyncProfilesStorageService: IUserDataSyncProfilesStorageService, + @IInstantiationService private readonly instantiationService: IInstantiationService, ) { super({ syncResource: SyncResource.Extensions, profile }, collection, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService, uriIdentityService); this.profileLocation = profile.extensionsResource; @@ -114,17 +123,18 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse Event.any( Event.filter(this.extensionManagementService.onDidInstallExtensions, (e => e.some(({ local }) => !!local))), Event.filter(this.extensionManagementService.onDidUninstallExtension, (e => !e.error)), - this.extensionEnablementService.onDidChangeEnablement, - this.extensionStorageService.onDidChangeExtensionStorageToSync)(() => this.triggerLocalChange())); + Event.filter(userDataSyncProfilesStorageService.onDidChange, e => e.valueChanges.some(({ profile, changes }) => this.syncResource.profile.id === profile.id && changes.some(change => change.key === DISABLED_EXTENSIONS_STORAGE_PATH))), + extensionStorageService.onDidChangeExtensionStorageToSync)(() => this.triggerLocalChange())); } protected async generateSyncPreview(remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null): Promise { const remoteExtensions: ISyncExtension[] | null = remoteUserData.syncData ? await parseAndMigrateExtensions(remoteUserData.syncData, this.extensionManagementService) : null; const skippedExtensions: ISyncExtension[] = lastSyncUserData?.skippedExtensions || []; + const builtinExtensions: IExtensionIdentifier[] = lastSyncUserData?.builtinExtensions || []; const lastSyncExtensions: ISyncExtension[] | null = lastSyncUserData?.syncData ? await parseAndMigrateExtensions(lastSyncUserData.syncData, this.extensionManagementService) : null; const installedExtensions = await this.extensionManagementService.getInstalled(undefined, this.profileLocation); - const localExtensions = this.getLocalExtensions(installedExtensions); + const localExtensions = await this.getLocalExtensions(installedExtensions); const ignoredExtensions = this.ignoredExtensionsManagementService.getIgnoredExtensions(installedExtensions); if (remoteExtensions) { @@ -133,7 +143,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse this.logService.trace(`${this.syncResourceLogLabel}: Remote extensions does not exist. Synchronizing extensions for the first time.`); } - const { local, remote } = merge(localExtensions, remoteExtensions, lastSyncExtensions, skippedExtensions, ignoredExtensions); + const { local, remote } = merge(localExtensions, remoteExtensions, lastSyncExtensions, skippedExtensions, ignoredExtensions, builtinExtensions); const previewResult: IExtensionResourceMergeResult = { local, remote, content: this.getPreviewContent(localExtensions, local.added, local.updated, local.removed), @@ -144,12 +154,14 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse const localContent = this.stringify(localExtensions, false); return [{ skippedExtensions, + builtinExtensions, baseResource: this.baseResource, baseContent: lastSyncExtensions ? this.stringify(lastSyncExtensions, false) : localContent, localResource: this.localResource, localContent, localExtensions, remoteResource: this.remoteResource, + remoteExtensions, remoteContent: remoteExtensions ? this.stringify(remoteExtensions, false) : null, previewResource: this.previewResource, previewResult, @@ -162,9 +174,9 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse protected async hasRemoteChanged(lastSyncUserData: ILastSyncUserData): Promise { const lastSyncExtensions: ISyncExtension[] | null = lastSyncUserData.syncData ? await parseAndMigrateExtensions(lastSyncUserData.syncData, this.extensionManagementService) : null; const installedExtensions = await this.extensionManagementService.getInstalled(undefined, this.profileLocation); - const localExtensions = this.getLocalExtensions(installedExtensions); + const localExtensions = await this.getLocalExtensions(installedExtensions); const ignoredExtensions = this.ignoredExtensionsManagementService.getIgnoredExtensions(installedExtensions); - const { remote } = merge(localExtensions, lastSyncExtensions, lastSyncExtensions, lastSyncUserData.skippedExtensions || [], ignoredExtensions); + const { remote } = merge(localExtensions, lastSyncExtensions, lastSyncExtensions, lastSyncUserData.skippedExtensions || [], ignoredExtensions, lastSyncUserData.builtinExtensions || []); return remote !== null; } @@ -219,7 +231,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse private async acceptLocal(resourcePreview: IExtensionResourcePreview): Promise { const installedExtensions = await this.extensionManagementService.getInstalled(undefined, this.profileLocation); const ignoredExtensions = this.ignoredExtensionsManagementService.getIgnoredExtensions(installedExtensions); - const mergeResult = merge(resourcePreview.localExtensions, null, null, resourcePreview.skippedExtensions, ignoredExtensions); + const mergeResult = merge(resourcePreview.localExtensions, null, null, resourcePreview.skippedExtensions, ignoredExtensions, resourcePreview.builtinExtensions); const { local, remote } = mergeResult; return { content: resourcePreview.localContent, @@ -235,7 +247,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse const ignoredExtensions = this.ignoredExtensionsManagementService.getIgnoredExtensions(installedExtensions); const remoteExtensions = resourcePreview.remoteContent ? JSON.parse(resourcePreview.remoteContent) : null; if (remoteExtensions !== null) { - const mergeResult = merge(resourcePreview.localExtensions, remoteExtensions, resourcePreview.localExtensions, [], ignoredExtensions); + const mergeResult = merge(resourcePreview.localExtensions, remoteExtensions, resourcePreview.localExtensions, [], ignoredExtensions, resourcePreview.builtinExtensions); const { local, remote } = mergeResult; return { content: resourcePreview.remoteContent, @@ -279,7 +291,8 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse if (lastSyncUserData?.ref !== remoteUserData.ref) { // update last sync this.logService.trace(`${this.syncResourceLogLabel}: Updating last synchronized extensions...`); - await this.updateLastSyncUserData(remoteUserData, { skippedExtensions }); + const builtinExtensions = localExtensions.filter(e => !e.installed).map(e => e.identifier); + await this.updateLastSyncUserData(remoteUserData, { skippedExtensions, builtinExtensions }); this.logService.info(`${this.syncResourceLogLabel}: Updated last synchronized extensions.${skippedExtensions.length ? ` Skipped: ${JSON.stringify(skippedExtensions.map(e => e.identifier.id))}.` : ''}`); } } @@ -292,7 +305,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse if (this.extUri.isEqual(uri, ExtensionsSynchroniser.EXTENSIONS_DATA_URI)) { const installedExtensions = await this.extensionManagementService.getInstalled(undefined, this.profileLocation); const ignoredExtensions = this.ignoredExtensionsManagementService.getIgnoredExtensions(installedExtensions); - const localExtensions = this.getLocalExtensions(installedExtensions).filter(e => !ignoredExtensions.some(id => areSameExtensions({ id }, e.identifier))); + const localExtensions = (await this.getLocalExtensions(installedExtensions)).filter(e => !ignoredExtensions.some(id => areSameExtensions({ id }, e.identifier))); return this.stringify(localExtensions, true); } @@ -340,7 +353,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse async hasLocalData(): Promise { try { const installedExtensions = await this.extensionManagementService.getInstalled(undefined, this.profileLocation); - const localExtensions = this.getLocalExtensions(installedExtensions); + const localExtensions = await this.getLocalExtensions(installedExtensions); if (localExtensions.some(e => e.installed || e.disabled)) { return true; } @@ -351,159 +364,178 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse } private async updateLocalExtensions(added: ISyncExtension[], removed: IExtensionIdentifier[], updated: ISyncExtension[], skippedExtensions: ISyncExtension[]): Promise { - const removeFromSkipped: IExtensionIdentifier[] = []; - const addToSkipped: ISyncExtension[] = []; - const installedExtensions = await this.extensionManagementService.getInstalled(undefined, this.profileLocation); + return this.withProfileScopedServices(async (extensionEnablementService, extensionStorageService) => { + const removeFromSkipped: IExtensionIdentifier[] = []; + const addToSkipped: ISyncExtension[] = []; + const installedExtensions = await this.extensionManagementService.getInstalled(undefined, this.profileLocation); - if (removed.length) { - const extensionsToRemove = installedExtensions.filter(({ identifier, isBuiltin }) => !isBuiltin && removed.some(r => areSameExtensions(identifier, r))); - await Promises.settled(extensionsToRemove.map(async extensionToRemove => { - this.logService.trace(`${this.syncResourceLogLabel}: Uninstalling local extension...`, extensionToRemove.identifier.id); - await this.extensionManagementService.uninstall(extensionToRemove, { donotIncludePack: true, donotCheckDependents: true, profileLocation: this.profileLocation }); - this.logService.info(`${this.syncResourceLogLabel}: Uninstalled local extension.`, extensionToRemove.identifier.id); - removeFromSkipped.push(extensionToRemove.identifier); - })); - } + if (removed.length) { + const extensionsToRemove = installedExtensions.filter(({ identifier, isBuiltin }) => !isBuiltin && removed.some(r => areSameExtensions(identifier, r))); + await Promises.settled(extensionsToRemove.map(async extensionToRemove => { + this.logService.trace(`${this.syncResourceLogLabel}: Uninstalling local extension...`, extensionToRemove.identifier.id); + await this.extensionManagementService.uninstall(extensionToRemove, { donotIncludePack: true, donotCheckDependents: true, profileLocation: this.profileLocation }); + this.logService.info(`${this.syncResourceLogLabel}: Uninstalled local extension.`, extensionToRemove.identifier.id); + removeFromSkipped.push(extensionToRemove.identifier); + })); + } - if (added.length || updated.length) { - await Promises.settled([...added, ...updated].map(async e => { - const installedExtension = installedExtensions.find(installed => areSameExtensions(installed.identifier, e.identifier)); + if (added.length || updated.length) { + await Promises.settled([...added, ...updated].map(async e => { + const installedExtension = installedExtensions.find(installed => areSameExtensions(installed.identifier, e.identifier)); - // Builtin Extension Sync: Enablement & State - if (installedExtension && installedExtension.isBuiltin) { - if (e.state && installedExtension.manifest.version === e.version) { - this.updateExtensionState(e.state, installedExtension, installedExtension.manifest.version); - } - const isDisabled = this.extensionEnablementService.getDisabledExtensions().some(disabledExtension => areSameExtensions(disabledExtension, e.identifier)); - if (isDisabled !== !!e.disabled) { - if (e.disabled) { - this.logService.trace(`${this.syncResourceLogLabel}: Disabling extension...`, e.identifier.id); - await this.extensionEnablementService.disableExtension(e.identifier); - this.logService.info(`${this.syncResourceLogLabel}: Disabled extension`, e.identifier.id); - } else { - this.logService.trace(`${this.syncResourceLogLabel}: Enabling extension...`, e.identifier.id); - await this.extensionEnablementService.enableExtension(e.identifier); - this.logService.info(`${this.syncResourceLogLabel}: Enabled extension`, e.identifier.id); + // Builtin Extension Sync: Enablement & State + if (installedExtension && installedExtension.isBuiltin) { + if (e.state && installedExtension.manifest.version === e.version) { + this.updateExtensionState(e.state, installedExtension, installedExtension.manifest.version, extensionStorageService); } - } - removeFromSkipped.push(e.identifier); - return; - } - - // User Extension Sync: Install/Update, Enablement & State - const extension = (await this.extensionGalleryService.getExtensions([{ ...e.identifier, preRelease: e.preRelease }], CancellationToken.None))[0]; - - /* Update extension state only if - * extension is installed and version is same as synced version or - * extension is not installed and installable - */ - if (e.state && - (installedExtension ? installedExtension.manifest.version === e.version /* Installed and has same version */ - : !!extension /* Installable */) - ) { - this.updateExtensionState(e.state, installedExtension || extension, installedExtension?.manifest.version); - } - - if (extension) { - try { - const isDisabled = this.extensionEnablementService.getDisabledExtensions().some(disabledExtension => areSameExtensions(disabledExtension, e.identifier)); + const isDisabled = extensionEnablementService.getDisabledExtensions().some(disabledExtension => areSameExtensions(disabledExtension, e.identifier)); if (isDisabled !== !!e.disabled) { if (e.disabled) { - this.logService.trace(`${this.syncResourceLogLabel}: Disabling extension...`, e.identifier.id, extension.version); - await this.extensionEnablementService.disableExtension(extension.identifier); - this.logService.info(`${this.syncResourceLogLabel}: Disabled extension`, e.identifier.id, extension.version); + this.logService.trace(`${this.syncResourceLogLabel}: Disabling extension...`, e.identifier.id); + await extensionEnablementService.disableExtension(e.identifier); + this.logService.info(`${this.syncResourceLogLabel}: Disabled extension`, e.identifier.id); } else { - this.logService.trace(`${this.syncResourceLogLabel}: Enabling extension...`, e.identifier.id, extension.version); - await this.extensionEnablementService.enableExtension(extension.identifier); - this.logService.info(`${this.syncResourceLogLabel}: Enabled extension`, e.identifier.id, extension.version); + this.logService.trace(`${this.syncResourceLogLabel}: Enabling extension...`, e.identifier.id); + await extensionEnablementService.enableExtension(e.identifier); + this.logService.info(`${this.syncResourceLogLabel}: Enabled extension`, e.identifier.id); } } - - if (!installedExtension // Install if the extension does not exist - || installedExtension.preRelease !== e.preRelease // Install if the extension pre-release preference has changed - ) { - if (await this.extensionManagementService.canInstall(extension)) { - this.logService.trace(`${this.syncResourceLogLabel}: Installing extension...`, e.identifier.id, extension.version); - await this.extensionManagementService.installFromGallery(extension, { isMachineScoped: false, donotIncludePackAndDependencies: true, installPreReleaseVersion: e.preRelease, profileLocation: this.profileLocation } /* set isMachineScoped value to prevent install and sync dialog in web */); - this.logService.info(`${this.syncResourceLogLabel}: Installed extension.`, e.identifier.id, extension.version); - removeFromSkipped.push(extension.identifier); - } else { - this.logService.info(`${this.syncResourceLogLabel}: Skipped synchronizing extension because it cannot be installed.`, extension.displayName || extension.identifier.id); - addToSkipped.push(e); - } - } - } catch (error) { - addToSkipped.push(e); - if (error instanceof ExtensionManagementError && [ExtensionManagementErrorCode.Incompatible, ExtensionManagementErrorCode.IncompatiblePreRelease, ExtensionManagementErrorCode.IncompatibleTargetPlatform].includes(error.code)) { - this.logService.info(`${this.syncResourceLogLabel}: Skipped synchronizing extension because the compatible extension is not found.`, extension.displayName || extension.identifier.id); - } else { - this.logService.error(error); - this.logService.info(`${this.syncResourceLogLabel}: Skipped synchronizing extension`, extension.displayName || extension.identifier.id); - } + removeFromSkipped.push(e.identifier); + return; } - } else { - addToSkipped.push(e); - this.logService.info(`${this.syncResourceLogLabel}: Skipped synchronizing extension because the extension is not found.`, e.identifier.id); - } - })); - } - const newSkippedExtensions: ISyncExtension[] = []; - for (const skippedExtension of skippedExtensions) { - if (!removeFromSkipped.some(e => areSameExtensions(e, skippedExtension.identifier))) { - newSkippedExtensions.push(skippedExtension); + // User Extension Sync: Install/Update, Enablement & State + const extension = (await this.extensionGalleryService.getExtensions([{ ...e.identifier, preRelease: e.preRelease }], CancellationToken.None))[0]; + + /* Update extension state only if + * extension is installed and version is same as synced version or + * extension is not installed and installable + */ + if (e.state && + (installedExtension ? installedExtension.manifest.version === e.version /* Installed and has same version */ + : !!extension /* Installable */) + ) { + this.updateExtensionState(e.state, installedExtension || extension, installedExtension?.manifest.version, extensionStorageService); + } + + if (extension) { + try { + const isDisabled = extensionEnablementService.getDisabledExtensions().some(disabledExtension => areSameExtensions(disabledExtension, e.identifier)); + if (isDisabled !== !!e.disabled) { + if (e.disabled) { + this.logService.trace(`${this.syncResourceLogLabel}: Disabling extension...`, e.identifier.id, extension.version); + await extensionEnablementService.disableExtension(extension.identifier); + this.logService.info(`${this.syncResourceLogLabel}: Disabled extension`, e.identifier.id, extension.version); + } else { + this.logService.trace(`${this.syncResourceLogLabel}: Enabling extension...`, e.identifier.id, extension.version); + await extensionEnablementService.enableExtension(extension.identifier); + this.logService.info(`${this.syncResourceLogLabel}: Enabled extension`, e.identifier.id, extension.version); + } + } + + if (!installedExtension // Install if the extension does not exist + || installedExtension.preRelease !== e.preRelease // Install if the extension pre-release preference has changed + ) { + if (await this.extensionManagementService.canInstall(extension)) { + this.logService.trace(`${this.syncResourceLogLabel}: Installing extension...`, e.identifier.id, extension.version); + await this.extensionManagementService.installFromGallery(extension, { isMachineScoped: false, donotIncludePackAndDependencies: true, installPreReleaseVersion: e.preRelease, profileLocation: this.profileLocation } /* set isMachineScoped value to prevent install and sync dialog in web */); + this.logService.info(`${this.syncResourceLogLabel}: Installed extension.`, e.identifier.id, extension.version); + removeFromSkipped.push(extension.identifier); + } else { + this.logService.info(`${this.syncResourceLogLabel}: Skipped synchronizing extension because it cannot be installed.`, extension.displayName || extension.identifier.id); + addToSkipped.push(e); + } + } + } catch (error) { + addToSkipped.push(e); + if (error instanceof ExtensionManagementError && [ExtensionManagementErrorCode.Incompatible, ExtensionManagementErrorCode.IncompatiblePreRelease, ExtensionManagementErrorCode.IncompatibleTargetPlatform].includes(error.code)) { + this.logService.info(`${this.syncResourceLogLabel}: Skipped synchronizing extension because the compatible extension is not found.`, extension.displayName || extension.identifier.id); + } else { + this.logService.error(error); + this.logService.info(`${this.syncResourceLogLabel}: Skipped synchronizing extension`, extension.displayName || extension.identifier.id); + } + } + } else { + addToSkipped.push(e); + this.logService.info(`${this.syncResourceLogLabel}: Skipped synchronizing extension because the extension is not found.`, e.identifier.id); + } + })); } - } - for (const skippedExtension of addToSkipped) { - if (!newSkippedExtensions.some(e => areSameExtensions(e.identifier, skippedExtension.identifier))) { - newSkippedExtensions.push(skippedExtension); + + const newSkippedExtensions: ISyncExtension[] = []; + for (const skippedExtension of skippedExtensions) { + if (!removeFromSkipped.some(e => areSameExtensions(e, skippedExtension.identifier))) { + newSkippedExtensions.push(skippedExtension); + } } - } - return newSkippedExtensions; + for (const skippedExtension of addToSkipped) { + if (!newSkippedExtensions.some(e => areSameExtensions(e.identifier, skippedExtension.identifier))) { + newSkippedExtensions.push(skippedExtension); + } + } + return newSkippedExtensions; + }); } - private updateExtensionState(state: IStringDictionary, extension: ILocalExtension | IGalleryExtension, version: string | undefined): void { - const extensionState = this.extensionStorageService.getExtensionState(extension, true) || {}; - const keys = version ? this.extensionStorageService.getKeysForSync({ id: extension.identifier.id, version }) : undefined; + private updateExtensionState(state: IStringDictionary, extension: ILocalExtension | IGalleryExtension, version: string | undefined, extensionStorageService: IExtensionStorageService): void { + const extensionState = extensionStorageService.getExtensionState(extension, true) || {}; + const keys = version ? extensionStorageService.getKeysForSync({ id: extension.identifier.id, version }) : undefined; if (keys) { keys.forEach(key => { extensionState[key] = state[key]; }); } else { Object.keys(state).forEach(key => extensionState[key] = state[key]); } - this.extensionStorageService.setExtensionState(extension, extensionState, true); + extensionStorageService.setExtensionState(extension, extensionState, true); } private parseExtensions(syncData: ISyncData): ISyncExtension[] { return JSON.parse(syncData.content); } - private getLocalExtensions(installedExtensions: ILocalExtension[]): ISyncExtensionWithVersion[] { - const disabledExtensions = this.extensionEnablementService.getDisabledExtensions(); - return installedExtensions - .map(extension => { - const { identifier, isBuiltin, manifest, preRelease } = extension; - const syncExntesion: ISyncExtensionWithVersion = { identifier, preRelease, version: manifest.version }; - if (disabledExtensions.some(disabledExtension => areSameExtensions(disabledExtension, identifier))) { - syncExntesion.disabled = true; - } - if (!isBuiltin) { - syncExntesion.installed = true; - } - try { - const keys = this.extensionStorageService.getKeysForSync({ id: identifier.id, version: manifest.version }); - if (keys) { - const extensionStorageState = this.extensionStorageService.getExtensionState(extension, true) || {}; - syncExntesion.state = Object.keys(extensionStorageState).reduce((state: IStringDictionary, key) => { - if (keys.includes(key)) { - state[key] = extensionStorageState[key]; - } - return state; - }, {}); + private getLocalExtensions(installedExtensions: ILocalExtension[]): Promise { + return this.withProfileScopedServices(async (extensionEnablementService, extensionStorageService) => { + const disabledExtensions = extensionEnablementService.getDisabledExtensions(); + return installedExtensions + .map(extension => { + const { identifier, isBuiltin, manifest, preRelease } = extension; + const syncExntesion: ISyncExtensionWithVersion = { identifier, preRelease, version: manifest.version }; + if (disabledExtensions.some(disabledExtension => areSameExtensions(disabledExtension, identifier))) { + syncExntesion.disabled = true; } - } catch (error) { - this.logService.info(`${this.syncResourceLogLabel}: Error while parsing extension state`, getErrorMessage(error)); + if (!isBuiltin) { + syncExntesion.installed = true; + } + try { + const keys = extensionStorageService.getKeysForSync({ id: identifier.id, version: manifest.version }); + if (keys) { + const extensionStorageState = extensionStorageService.getExtensionState(extension, true) || {}; + syncExntesion.state = Object.keys(extensionStorageState).reduce((state: IStringDictionary, key) => { + if (keys.includes(key)) { + state[key] = extensionStorageState[key]; + } + return state; + }, {}); + } + } catch (error) { + this.logService.info(`${this.syncResourceLogLabel}: Error while parsing extension state`, getErrorMessage(error)); + } + return syncExntesion; + }); + }); + } + + private async withProfileScopedServices(fn: (extensionEnablementService: IGlobalExtensionEnablementService, extensionStorageService: IExtensionStorageService) => Promise): Promise { + return this.userDataSyncProfilesStorageService.withProfileScopedStorageService(this.syncResource.profile, + async storageService => { + const disposables = new DisposableStore(); + const instantiationService = this.instantiationService.createChild(new ServiceCollection([IStorageService, storageService])); + const extensionEnablementService = disposables.add(instantiationService.createInstance(GlobalExtensionEnablementService)); + const extensionStorageService = disposables.add(instantiationService.createInstance(ExtensionStorageService)); + try { + return await fn(extensionEnablementService, extensionStorageService); + } finally { + disposables.dispose(); } - return syncExntesion; }); } diff --git a/src/vs/platform/userDataSync/common/globalStateMerge.ts b/src/vs/platform/userDataSync/common/globalStateMerge.ts index 8f2e51bb67d..930706f3c15 100644 --- a/src/vs/platform/userDataSync/common/globalStateMerge.ts +++ b/src/vs/platform/userDataSync/common/globalStateMerge.ts @@ -10,18 +10,18 @@ import { IStorageValue, SYNC_SERVICE_URL_TYPE } from 'vs/platform/userDataSync/c export interface IMergeResult { local: { added: IStringDictionary; removed: string[]; updated: IStringDictionary }; - remote: IStringDictionary | null; + remote: { added: string[]; removed: string[]; updated: string[]; all: IStringDictionary | null }; } export function merge(localStorage: IStringDictionary, remoteStorage: IStringDictionary | null, baseStorage: IStringDictionary | null, storageKeys: { machine: ReadonlyArray; unregistered: ReadonlyArray }, logService: ILogService): IMergeResult { if (!remoteStorage) { - return { remote: Object.keys(localStorage).length > 0 ? localStorage : null, local: { added: {}, removed: [], updated: {} } }; + return { remote: { added: Object.keys(localStorage), removed: [], updated: [], all: Object.keys(localStorage).length > 0 ? localStorage : null }, local: { added: {}, removed: [], updated: {} } }; } const localToRemote = compare(localStorage, remoteStorage); if (localToRemote.added.size === 0 && localToRemote.removed.size === 0 && localToRemote.updated.size === 0) { // No changes found between local and remote. - return { remote: null, local: { added: {}, removed: [], updated: {} } }; + return { remote: { added: [], removed: [], updated: [], all: null }, local: { added: {}, removed: [], updated: {} } }; } const baseToRemote = baseStorage ? compare(baseStorage, remoteStorage) : { added: Object.keys(remoteStorage).reduce((r, k) => { r.add(k); return r; }, new Set()), removed: new Set(), updated: new Set() }; @@ -116,7 +116,8 @@ export function merge(localStorage: IStringDictionary, remoteStor local.removed.push(key); } - return { local, remote: areSame(remote, remoteStorage) ? null : remote }; + const result = compare(remoteStorage, remote); + return { local, remote: { added: [...result.added], updated: [...result.updated], removed: [...result.removed], all: result.added.size === 0 && result.removed.size === 0 && result.updated.size === 0 ? null : remote } }; } function compare(from: IStringDictionary, to: IStringDictionary): { added: Set; removed: Set; updated: Set } { @@ -140,8 +141,3 @@ function compare(from: IStringDictionary, to: IStringDictionary): { ad return { added, removed, updated }; } -function areSame(a: IStringDictionary, b: IStringDictionary): boolean { - const { added, removed, updated } = compare(a, b); - return added.size === 0 && removed.size === 0 && updated.size === 0; -} - diff --git a/src/vs/platform/userDataSync/common/globalStateSync.ts b/src/vs/platform/userDataSync/common/globalStateSync.ts index e10896d7bf8..eabf5326051 100644 --- a/src/vs/platform/userDataSync/common/globalStateSync.ts +++ b/src/vs/platform/userDataSync/common/globalStateSync.ts @@ -37,7 +37,7 @@ type StorageKeys = { machine: string[]; user: string[]; unregistered: string[] } interface IGlobalStateResourceMergeResult extends IAcceptResult { readonly local: { added: IStringDictionary; removed: string[]; updated: IStringDictionary }; - readonly remote: IStringDictionary | null; + readonly remote: { added: string[]; removed: string[]; updated: string[]; all: IStringDictionary | null }; } export interface IGlobalStateResourcePreview extends IResourcePreview { @@ -133,7 +133,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs local, remote, localChange: Object.keys(local.added).length > 0 || Object.keys(local.updated).length > 0 || local.removed.length > 0 ? Change.Modified : Change.None, - remoteChange: remote !== null ? Change.Modified : Change.None, + remoteChange: remote.all !== null ? Change.Modified : Change.None, }; const localContent = stringify(localGlobalState, false); @@ -162,7 +162,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs const localGlobalState = await this.getLocalGlobalState(); const storageKeys = await this.getStorageKeys(lastSyncGlobalState); const { remote } = merge(localGlobalState.storage, lastSyncGlobalState.storage, lastSyncGlobalState.storage, storageKeys, this.logService); - return remote !== null; + return remote.all !== null; } protected async getMergeResult(resourcePreview: IGlobalStateResourcePreview, token: CancellationToken): Promise { @@ -193,7 +193,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs return { content: resourcePreview.localContent, local: { added: {}, removed: [], updated: {} }, - remote: resourcePreview.localUserData.storage, + remote: { added: Object.keys(resourcePreview.localUserData.storage), removed: [], updated: [], all: resourcePreview.localUserData.storage }, localChange: Change.None, remoteChange: Change.Modified, }; @@ -214,7 +214,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs return { content: resourcePreview.remoteContent, local: { added: {}, removed: [], updated: {} }, - remote: null, + remote: { added: [], removed: [], updated: [], all: null }, localChange: Change.None, remoteChange: Change.None, }; @@ -240,9 +240,9 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs if (remoteChange !== Change.None) { // update remote this.logService.trace(`${this.syncResourceLogLabel}: Updating remote ui state...`); - const content = JSON.stringify({ storage: remote }); + const content = JSON.stringify({ storage: remote.all }); remoteUserData = await this.updateRemoteUserData(content, force ? null : remoteUserData.ref); - this.logService.info(`${this.syncResourceLogLabel}: Updated remote ui state`); + this.logService.info(`${this.syncResourceLogLabel}: Updated remote ui state.${remote.added.length ? ` Added: ${remote.added}.` : ''}${remote.updated.length ? ` Updated: ${remote.updated}.` : ''}${remote.removed.length ? ` Removed: ${remote.removed}.` : ''}`); } if (lastSyncUserData?.ref !== remoteUserData.ref) { diff --git a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts index 2507e379501..3cf12e871af 100644 --- a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts @@ -204,8 +204,8 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto await this.userDataSyncService.resetLocal(); } } catch (error) { + this.logService.error(error); if (softTurnOffOnError) { - this.logService.error(error); this.updateEnablement(false); } else { throw error; @@ -411,7 +411,6 @@ class AutoSync extends Disposable { this.syncTask?.stop(); this.logService.info('Auto Sync: Stopped'); })); - this.logService.info('Auto Sync: Started'); this.sync(AutoSync.INTERVAL_SYNCING, false); } diff --git a/src/vs/platform/userDataSync/common/userDataSyncProfilesStorageService.ts b/src/vs/platform/userDataSync/common/userDataSyncProfilesStorageService.ts index a557f070ce5..d90616fef82 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncProfilesStorageService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncProfilesStorageService.ts @@ -47,6 +47,11 @@ export interface IUserDataSyncProfilesStorageService { * @param target Storage target of the data */ updateStorageData(profile: IUserDataProfile, data: Map, target: StorageTarget): Promise; + + /** + * Calls a function with a storage service scoped to given profile. + */ + withProfileScopedStorageService(profile: IUserDataProfile, fn: (storageService: IStorageService) => Promise): Promise; } export abstract class AbstractUserDataSyncProfilesStorageService extends Disposable implements IUserDataSyncProfilesStorageService { @@ -62,34 +67,25 @@ export abstract class AbstractUserDataSyncProfilesStorageService extends Disposa } async readStorageData(profile: IUserDataProfile): Promise> { - // Use current storage service if the profile is same - if (this.storageService.hasScope(profile)) { - return this.getItems(this.storageService); - } - - const storageDatabase = await this.createStorageDatabase(profile); - const storageService = new StorageService(storageDatabase); - try { - await storageService.initialize(); - return this.getItems(storageService); - } finally { - storageService.dispose(); - await this.closeAndDispose(storageDatabase); - } + return this.withProfileScopedStorageService(profile, async storageService => this.getItems(storageService)); } async updateStorageData(profile: IUserDataProfile, data: Map, target: StorageTarget): Promise { - // Use current storage service if the profile is same + return this.withProfileScopedStorageService(profile, async storageService => this.writeItems(storageService, data, target)); + } + + async withProfileScopedStorageService(profile: IUserDataProfile, fn: (storageService: IStorageService) => Promise): Promise { if (this.storageService.hasScope(profile)) { - return this.writeItems(this.storageService, data, target); + return fn(this.storageService); } const storageDatabase = await this.createStorageDatabase(profile); const storageService = new StorageService(storageDatabase); try { await storageService.initialize(); - this.writeItems(storageService, data, target); + const result = await fn(storageService); await storageService.flush(); + return result; } finally { storageService.dispose(); await this.closeAndDispose(storageDatabase); diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index 02cd2ff1c7a..813a43ad91a 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -92,7 +92,6 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ @IUserDataSyncEnablementService private readonly userDataSyncEnablementService: IUserDataSyncEnablementService, @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @IProductService private readonly productService: IProductService, - @IConfigurationService private readonly configurationService: IConfigurationService, @IEnvironmentService private readonly environmentService: IEnvironmentService, ) { super(); @@ -104,6 +103,8 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ async createSyncTask(manifest: IUserDataManifest | null, disableCache?: boolean): Promise { this.checkEnablement(); + this.logService.info('Sync started.'); + const startTime = new Date().getTime(); const executionId = generateUuid(); try { const syncHeaders = createSyncHeaders(executionId); @@ -127,7 +128,9 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ throw new Error('Can run a task only once'); } cancellablePromise = createCancelablePromise(token => that.sync(manifest, false, executionId, token)); - return cancellablePromise.finally(() => cancellablePromise = undefined); + await cancellablePromise.finally(() => cancellablePromise = undefined); + that.logService.info(`Sync done. Took ${new Date().getTime() - startTime}ms`); + that.updateLastSyncTime(); }, stop(): Promise { cancellablePromise?.cancel(); @@ -143,9 +146,10 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ throw new UserDataSyncError('Cannot start manual sync when sync is enabled', UserDataSyncErrorCode.LocalError); } + this.logService.info('Sync started.'); + const startTime = new Date().getTime(); const executionId = generateUuid(); const syncHeaders = createSyncHeaders(executionId); - let manifest: IUserDataManifest | null; try { manifest = await this.userDataSyncStoreService.manifest(null, syncHeaders); @@ -166,12 +170,9 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ return that.sync(manifest, true, executionId, cancellableToken.token); }, async apply(): Promise { - for (const profileSynchronizer of that.getActiveProfileSynchronizers()) { - if (cancellableToken.token.isCancellationRequested) { - return; - } - await profileSynchronizer.apply(executionId, cancellableToken.token); - } + await that.applyManualSync(manifest, executionId, cancellableToken.token); + that.logService.info(`Sync done. Took ${new Date().getTime() - startTime}ms`); + that.updateLastSyncTime(); }, stop(): Promise { cancellableToken.cancel(); @@ -185,14 +186,8 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ } private async sync(manifest: IUserDataManifest | null, merge: boolean, executionId: string, token: CancellationToken): Promise { - // Return if cancellation is requested - if (token.isCancellationRequested) { - return; - } - const startTime = new Date().getTime(); this._syncErrors = []; try { - this.logService.info('Sync started.'); if (this.status !== SyncStatus.HasConflicts) { this.setStatus(SyncStatus.Syncing); } @@ -208,27 +203,56 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ if (token.isCancellationRequested) { return; } - for (const syncProfile of syncProfiles) { - if (token.isCancellationRequested) { - return; - } - const profile = this.userDataProfilesService.profiles.find(p => p.id === syncProfile.id); - if (!profile) { - this.logService.error(`Settings Profile with id:${syncProfile.id} and name: ${syncProfile.name} does not exist locally to sync.`); - continue; - } - this.logService.info('Syncing profile.', syncProfile.name); - const profileSynchronizer = this.getOrCreateActiveProfileSynchronizer(profile, syncProfile); - this._syncErrors.push(...await this.syncProfile(profileSynchronizer, manifest, merge, executionId, token)); - } + await this.syncRemoteProfiles(syncProfiles, manifest, merge, executionId, token); } - this.logService.info(`Sync done. Took ${new Date().getTime() - startTime}ms`); - this.updateLastSyncTime(); } finally { this._onSyncErrors.fire(this._syncErrors); } } + private async syncRemoteProfiles(remoteProfiles: ISyncUserDataProfile[], manifest: IUserDataManifest | null, merge: boolean, executionId: string, token: CancellationToken): Promise { + for (const syncProfile of remoteProfiles) { + if (token.isCancellationRequested) { + return; + } + const profile = this.userDataProfilesService.profiles.find(p => p.id === syncProfile.id); + if (!profile) { + this.logService.error(`Settings Profile with id:${syncProfile.id} and name: ${syncProfile.name} does not exist locally to sync.`); + continue; + } + this.logService.info('Syncing profile.', syncProfile.name); + const profileSynchronizer = this.getOrCreateActiveProfileSynchronizer(profile, syncProfile); + this._syncErrors.push(...await this.syncProfile(profileSynchronizer, manifest, merge, executionId, token)); + } + } + + private async applyManualSync(manifest: IUserDataManifest | null, executionId: string, token: CancellationToken): Promise { + const profileSynchronizers = this.getActiveProfileSynchronizers(); + for (const profileSynchronizer of profileSynchronizers) { + if (token.isCancellationRequested) { + return; + } + await profileSynchronizer.apply(executionId, token); + } + + const defaultProfileSynchronizer = profileSynchronizers.find(s => s.profile.isDefault); + if (!defaultProfileSynchronizer) { + return; + } + + const userDataProfileManifestSynchronizer = defaultProfileSynchronizer.enabled.find(s => s.resource === SyncResource.Profiles); + if (!userDataProfileManifestSynchronizer) { + return; + } + + // Sync remote profiles which are not synced locally + const remoteProfiles = (await (userDataProfileManifestSynchronizer as UserDataProfilesManifestSynchroniser).getRemoteSyncedProfiles(manifest?.latest ?? null)) || []; + const remoteProfilesToSync = remoteProfiles.filter(remoteProfile => profileSynchronizers.every(s => s.profile.id !== remoteProfile.id)); + if (remoteProfilesToSync.length) { + await this.syncRemoteProfiles(remoteProfilesToSync, manifest, false, executionId, token); + } + } + private async syncProfile(profileSynchronizer: ProfileSynchronizer, manifest: IUserDataManifest | null, merge: boolean, executionId: string, token: CancellationToken): Promise { const errors = await profileSynchronizer.sync(manifest, merge, executionId, token); return errors.map(([syncResource, error]) => ({ profile: profileSynchronizer.profile, syncResource, error })); @@ -367,16 +391,14 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ async resetLocal(): Promise { this.checkEnablement(); this.storageService.remove(LAST_SYNC_TIME_KEY, StorageScope.APPLICATION); - if (this.activeProfileSynchronizers) { - for (const [synchronizer] of this.activeProfileSynchronizers.values()) { - try { - await synchronizer.resetLocal(); - } catch (e) { - this.logService.error(e); - } + for (const [synchronizer] of this.activeProfileSynchronizers.values()) { + try { + await synchronizer.resetLocal(); + } catch (e) { + this.logService.error(e); } - this.clearActiveProfileSynchronizers(); } + this.clearActiveProfileSynchronizers(); this._onDidResetLocal.fire(); this.logService.info('Did reset the local sync state.'); } @@ -396,7 +418,11 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ return isUndefined(result) ? null : result; } - if (this.environmentService.isBuilt && !(this.productService.enableSyncingProfiles && this.configurationService.getValue('settingsSync.enableSyncingProfiles'))) { + if (this.userDataProfilesService.isEnabled()) { + return null; + } + + if (this.environmentService.isBuilt && (!this.productService.enableSyncingProfiles || isEqual(this.userDataSyncStoreManagementService.userDataSyncStore?.url, this.userDataSyncStoreManagementService.userDataSyncStore?.stableUrl))) { return null; } @@ -521,7 +547,7 @@ class ProfileSynchronizer extends Disposable { @ITelemetryService private readonly telemetryService: ITelemetryService, @IUserDataSyncLogService private readonly logService: IUserDataSyncLogService, @IProductService private readonly productService: IProductService, - @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService, + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @IConfigurationService private readonly configurationService: IConfigurationService, @IEnvironmentService private readonly environmentService: IEnvironmentService, ) { @@ -568,7 +594,10 @@ class ProfileSynchronizer extends Disposable { if (!this._profile.isDefault) { return; } - if (this.environmentService.isBuilt && !(this.productService.enableSyncingProfiles && this.configurationService.getValue('settingsSync.enableSyncingProfiles'))) { + if (!this.userDataProfilesService.isEnabled()) { + return; + } + if (this.environmentService.isBuilt && (!this.productService.enableSyncingProfiles || isEqual(this.userDataSyncStoreManagementService.userDataSyncStore?.url, this.userDataSyncStoreManagementService.userDataSyncStore?.stableUrl))) { this.logService.debug('Skipping profiles sync'); return; } diff --git a/src/vs/platform/userDataSync/common/userDataSyncServiceIpc.ts b/src/vs/platform/userDataSync/common/userDataSyncServiceIpc.ts index 2697c75eb94..c6605c7ad7c 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncServiceIpc.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncServiceIpc.ts @@ -17,8 +17,8 @@ import { type ManualSyncTaskEvent = { manualSyncTaskId: string; data: T }; -function reviewSyncResource(syncResource: IUserDataSyncResource, userDataProfilesService: IUserDataProfilesService) { - return { ...syncResource, profie: reviveProfile(syncResource.profile, userDataProfilesService.profilesHome.scheme) }; +function reviewSyncResource(syncResource: IUserDataSyncResource, userDataProfilesService: IUserDataProfilesService): IUserDataSyncResource { + return { ...syncResource, profile: reviveProfile(syncResource.profile, userDataProfilesService.profilesHome.scheme) }; } export class UserDataSyncChannel implements IServerChannel { diff --git a/src/vs/platform/userDataSync/test/common/extensionsMerge.test.ts b/src/vs/platform/userDataSync/test/common/extensionsMerge.test.ts index 5d3ec0c49f3..039b660c450 100644 --- a/src/vs/platform/userDataSync/test/common/extensionsMerge.test.ts +++ b/src/vs/platform/userDataSync/test/common/extensionsMerge.test.ts @@ -16,7 +16,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'c', uuid: 'c' }, installed: true, version: '1.0.0' }, ]; - const actual = merge(localExtensions, null, null, [], []); + const actual = merge(localExtensions, null, null, [], [], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -35,7 +35,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'c', uuid: 'c' }, installed: true, version: '1.0.0' }, ]; - const actual = merge(localExtensions, null, null, [], ['a']); + const actual = merge(localExtensions, null, null, [], ['a'], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -54,7 +54,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'c', uuid: 'c' }, installed: true, version: '1.0.0' }, ]; - const actual = merge(localExtensions, null, null, [], ['A']); + const actual = merge(localExtensions, null, null, [], ['A'], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -77,7 +77,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'c', uuid: 'c' }, installed: true, version: '1.0.0' }, ]; - const actual = merge(localExtensions, null, null, skippedExtension, []); + const actual = merge(localExtensions, null, null, skippedExtension, [], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -99,7 +99,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'c', uuid: 'c' }, installed: true, version: '1.0.0' }, ]; - const actual = merge(localExtensions, null, null, skippedExtension, ['a']); + const actual = merge(localExtensions, null, null, skippedExtension, ['a'], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -123,7 +123,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'd', uuid: 'd' }, installed: true, version: '1.0.0', preRelease: false }, ]; - const actual = merge(localExtensions, remoteExtensions, null, [], []); + const actual = merge(localExtensions, remoteExtensions, null, [], [], []); assert.deepStrictEqual(actual.local.added, [{ identifier: { id: 'b', uuid: 'b' }, installed: true, version: '1.0.0', preRelease: false }, { identifier: { id: 'c', uuid: 'c' }, installed: true, version: '1.0.0', preRelease: false }]); assert.deepStrictEqual(actual.local.removed, []); @@ -146,7 +146,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'd', uuid: 'd' }, installed: true, version: '1.0.0', preRelease: false }, ]; - const actual = merge(localExtensions, remoteExtensions, null, [], ['a']); + const actual = merge(localExtensions, remoteExtensions, null, [], ['a'], []); assert.deepStrictEqual(actual.local.added, [{ identifier: { id: 'b', uuid: 'b' }, installed: true, version: '1.0.0', preRelease: false }, { identifier: { id: 'c', uuid: 'c' }, installed: true, version: '1.0.0', preRelease: false }]); assert.deepStrictEqual(actual.local.removed, []); @@ -168,7 +168,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'c', uuid: 'c' }, installed: true, version: '1.0.0' }, ]; - const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], []); + const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], [], []); assert.deepStrictEqual(actual.local.added, [{ identifier: { id: 'b', uuid: 'b' }, installed: true, version: '1.0.0', preRelease: false }, { identifier: { id: 'c', uuid: 'c' }, installed: true, version: '1.0.0', preRelease: false }]); assert.deepStrictEqual(actual.local.removed, [{ id: 'a', uuid: 'a' }, { id: 'd', uuid: 'd' }]); @@ -191,7 +191,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'd', uuid: 'd' }, disabled: true, installed: true, version: '1.0.0' }, ]; - const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], []); + const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], [], []); assert.deepStrictEqual(actual.local.added, [{ identifier: { id: 'b', uuid: 'b' }, installed: true, version: '1.0.0', preRelease: false }, { identifier: { id: 'c', uuid: 'c' }, installed: true, version: '1.0.0', preRelease: false }]); assert.deepStrictEqual(actual.local.removed, [{ id: 'a', uuid: 'a' }]); @@ -213,7 +213,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'c', uuid: 'c' }, installed: true, version: '1.0.0' }, ]; - const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], ['a']); + const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], ['a'], []); assert.deepStrictEqual(actual.local.added, [{ identifier: { id: 'b', uuid: 'b' }, installed: true, version: '1.0.0', preRelease: false }, { identifier: { id: 'c', uuid: 'c' }, installed: true, version: '1.0.0', preRelease: false }]); assert.deepStrictEqual(actual.local.removed, [{ id: 'd', uuid: 'd' }]); @@ -237,7 +237,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'c', uuid: 'c' }, installed: true, version: '1.0.0' }, ]; - const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, []); + const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, [], []); assert.deepStrictEqual(actual.local.added, [{ identifier: { id: 'b', uuid: 'b' }, installed: true, version: '1.0.0', preRelease: false }, { identifier: { id: 'c', uuid: 'c' }, installed: true, version: '1.0.0', preRelease: false }]); assert.deepStrictEqual(actual.local.removed, [{ id: 'd', uuid: 'd' }]); @@ -261,7 +261,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'c', uuid: 'c' }, installed: true, version: '1.0.0' }, ]; - const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, ['b']); + const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, ['b'], []); assert.deepStrictEqual(actual.local.added, [{ identifier: { id: 'c', uuid: 'c' }, installed: true, version: '1.0.0', preRelease: false }]); assert.deepStrictEqual(actual.local.removed, [{ id: 'd', uuid: 'd' }]); @@ -270,9 +270,9 @@ suite('ExtensionsMerge', () => { }); test('merge local and remote extensions when local is moved forwarded', () => { - const baseExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' }, installed: true }, - { identifier: { id: 'd', uuid: 'd' }, installed: true }, + const baseExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, installed: true, version: '1.0.0' }, + { identifier: { id: 'd', uuid: 'd' }, installed: true, version: '1.0.0' }, ]; const localExtensions: ISyncExtensionWithVersion[] = [ { identifier: { id: 'b', uuid: 'b' }, installed: true, version: '1.0.0' }, @@ -287,7 +287,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'c', uuid: 'c' }, installed: true, version: '1.0.0', preRelease: false }, ]; - const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], []); + const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], [], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -296,9 +296,9 @@ suite('ExtensionsMerge', () => { }); test('merge local and remote extensions when local is moved forwarded with disabled extensions', () => { - const baseExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' }, installed: true }, - { identifier: { id: 'd', uuid: 'd' }, installed: true }, + const baseExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, installed: true, version: '1.0.0' }, + { identifier: { id: 'd', uuid: 'd' }, installed: true, version: '1.0.0' }, ]; const localExtensions: ISyncExtensionWithVersion[] = [ { identifier: { id: 'a', uuid: 'a' }, disabled: true, installed: true, version: '1.0.0' }, @@ -315,7 +315,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'c', uuid: 'c' }, installed: true, version: '1.0.0', preRelease: false }, ]; - const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], []); + const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], [], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -324,9 +324,9 @@ suite('ExtensionsMerge', () => { }); test('merge local and remote extensions when local is moved forwarded with ignored settings', () => { - const baseExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' }, installed: true }, - { identifier: { id: 'd', uuid: 'd' }, installed: true }, + const baseExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, installed: true, version: '1.0.0' }, + { identifier: { id: 'd', uuid: 'd' }, installed: true, version: '1.0.0' }, ]; const localExtensions: ISyncExtensionWithVersion[] = [ { identifier: { id: 'b', uuid: 'b' }, installed: true, version: '1.0.0' }, @@ -337,7 +337,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'd', uuid: 'd' }, installed: true, version: '1.0.0' }, ]; - const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], ['b']); + const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], ['b'], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -348,9 +348,9 @@ suite('ExtensionsMerge', () => { }); test('merge local and remote extensions when local is moved forwarded with skipped extensions', () => { - const baseExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' }, installed: true }, - { identifier: { id: 'd', uuid: 'd' }, installed: true }, + const baseExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, installed: true, version: '1.0.0' }, + { identifier: { id: 'd', uuid: 'd' }, installed: true, version: '1.0.0' }, ]; const skippedExtensions: ISyncExtension[] = [ { identifier: { id: 'd', uuid: 'd' }, installed: true }, @@ -369,7 +369,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'c', uuid: 'c' }, installed: true, version: '1.0.0', preRelease: false }, ]; - const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, []); + const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, [], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -378,9 +378,9 @@ suite('ExtensionsMerge', () => { }); test('merge local and remote extensions when local is moved forwarded with skipped and ignored extensions', () => { - const baseExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' }, installed: true }, - { identifier: { id: 'd', uuid: 'd' }, installed: true }, + const baseExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, installed: true, version: '1.0.0' }, + { identifier: { id: 'd', uuid: 'd' }, installed: true, version: '1.0.0' }, ]; const skippedExtensions: ISyncExtension[] = [ { identifier: { id: 'd', uuid: 'd' }, installed: true }, @@ -398,7 +398,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'b', uuid: 'b' }, installed: true, version: '1.0.0', preRelease: false }, ]; - const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, ['c']); + const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, ['c'], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -407,9 +407,9 @@ suite('ExtensionsMerge', () => { }); test('merge local and remote extensions when both moved forwarded', () => { - const baseExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' }, installed: true }, - { identifier: { id: 'd', uuid: 'd' }, installed: true }, + const baseExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, installed: true, version: '1.0.0' }, + { identifier: { id: 'd', uuid: 'd' }, installed: true, version: '1.0.0' }, ]; const localExtensions: ISyncExtensionWithVersion[] = [ { identifier: { id: 'a', uuid: 'a' }, installed: true, version: '1.0.0' }, @@ -427,7 +427,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'c', uuid: 'c' }, installed: true, version: '1.0.0', preRelease: false }, ]; - const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], []); + const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], [], []); assert.deepStrictEqual(actual.local.added, [{ identifier: { id: 'e', uuid: 'e' }, installed: true, version: '1.0.0', preRelease: false }]); assert.deepStrictEqual(actual.local.removed, [{ id: 'a', uuid: 'a' }]); @@ -436,9 +436,9 @@ suite('ExtensionsMerge', () => { }); test('merge local and remote extensions when both moved forwarded with ignored extensions', () => { - const baseExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' }, installed: true }, - { identifier: { id: 'd', uuid: 'd' }, installed: true }, + const baseExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, installed: true, version: '1.0.0' }, + { identifier: { id: 'd', uuid: 'd' }, installed: true, version: '1.0.0' }, ]; const localExtensions: ISyncExtensionWithVersion[] = [ { identifier: { id: 'a', uuid: 'a' }, installed: true, version: '1.0.0' }, @@ -456,7 +456,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'c', uuid: 'c' }, installed: true, version: '1.0.0', preRelease: false }, ]; - const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], ['a', 'e']); + const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], ['a', 'e'], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -465,9 +465,9 @@ suite('ExtensionsMerge', () => { }); test('merge local and remote extensions when both moved forwarded with skipped extensions', () => { - const baseExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' }, installed: true }, - { identifier: { id: 'd', uuid: 'd' }, installed: true }, + const baseExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, installed: true, version: '1.0.0' }, + { identifier: { id: 'd', uuid: 'd' }, installed: true, version: '1.0.0' }, ]; const skippedExtensions: ISyncExtension[] = [ { identifier: { id: 'a', uuid: 'a' }, installed: true }, @@ -487,7 +487,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'c', uuid: 'c' }, installed: true, version: '1.0.0', preRelease: false }, ]; - const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, []); + const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, [], []); assert.deepStrictEqual(actual.local.added, [{ identifier: { id: 'e', uuid: 'e' }, installed: true, version: '1.0.0', preRelease: false }]); assert.deepStrictEqual(actual.local.removed, []); @@ -496,9 +496,9 @@ suite('ExtensionsMerge', () => { }); test('merge local and remote extensions when both moved forwarded with skipped and ignoredextensions', () => { - const baseExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' }, installed: true }, - { identifier: { id: 'd', uuid: 'd' }, installed: true }, + const baseExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, installed: true, version: '1.0.0' }, + { identifier: { id: 'd', uuid: 'd' }, installed: true, version: '1.0.0' }, ]; const skippedExtensions: ISyncExtension[] = [ { identifier: { id: 'a', uuid: 'a' }, installed: true }, @@ -518,7 +518,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'c', uuid: 'c' }, installed: true, version: '1.0.0', preRelease: false }, ]; - const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, ['e']); + const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, ['e'], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -543,7 +543,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'c', uuid: 'c' }, installed: true, version: '1.0.0', preRelease: false }, ]; - const actual = merge(localExtensions, remoteExtensions, null, [], []); + const actual = merge(localExtensions, remoteExtensions, null, [], [], []); assert.deepStrictEqual(actual.local.added, [{ identifier: { id: 'd', uuid: 'd' }, installed: true, version: '1.0.0', preRelease: false }]); assert.deepStrictEqual(actual.local.removed, []); @@ -560,7 +560,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'b', uuid: 'b' }, version: '1.0.0' }, ]; - const actual = merge(localExtensions, remoteExtensions, null, [], []); + const actual = merge(localExtensions, remoteExtensions, null, [], [], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -579,7 +579,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'a', uuid: 'a' }, installed: true, version: '1.0.0', preRelease: false }, ]; - const actual = merge(localExtensions, remoteExtensions, null, [], []); + const actual = merge(localExtensions, remoteExtensions, null, [], [], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -596,7 +596,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'b', uuid: 'b' }, version: '1.0.0' }, ]; - const actual = merge(localExtensions, remoteExtensions, remoteExtensions, [], []); + const actual = merge(localExtensions, remoteExtensions, remoteExtensions, [], [], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -615,7 +615,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'a', uuid: 'a' }, installed: true, disabled: true, version: '1.0.0', preRelease: false }, ]; - const actual = merge(localExtensions, remoteExtensions, remoteExtensions, [], []); + const actual = merge(localExtensions, remoteExtensions, remoteExtensions, [], [], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -631,7 +631,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'a', uuid: 'a' }, installed: true, disabled: true, version: '1.0.0' }, ]; - const actual = merge(localExtensions, remoteExtensions, localExtensions, [], []); + const actual = merge(localExtensions, remoteExtensions, localExtensions, [], [], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -653,7 +653,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', preRelease: false }, ]; - const actual = merge(localExtensions, remoteExtensions, null, [], []); + const actual = merge(localExtensions, remoteExtensions, null, [], [], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -667,7 +667,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', installed: true, preRelease: true }, ]; - const actual = merge(localExtensions, remoteExtensions, null, [], []); + const actual = merge(localExtensions, remoteExtensions, null, [], [], []); assert.deepStrictEqual(actual.local.added, [{ identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', installed: true, preRelease: true }]); assert.deepStrictEqual(actual.local.removed, []); @@ -681,7 +681,7 @@ suite('ExtensionsMerge', () => { ]; const remoteExtensions: ISyncExtensionWithVersion[] = []; - const actual = merge(localExtensions, remoteExtensions, null, [], []); + const actual = merge(localExtensions, remoteExtensions, null, [], [], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -697,7 +697,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', installed: true, preRelease: true }, ]; - const actual = merge(localExtensions, remoteExtensions, null, [], []); + const actual = merge(localExtensions, remoteExtensions, null, [], [], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -713,7 +713,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', installed: true }, ]; - const actual = merge(localExtensions, remoteExtensions, null, [], []); + const actual = merge(localExtensions, remoteExtensions, null, [], [], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -729,7 +729,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', installed: true, preRelease: true }, ]; - const actual = merge(localExtensions, remoteExtensions, localExtensions, [], []); + const actual = merge(localExtensions, remoteExtensions, localExtensions, [], [], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -745,7 +745,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', installed: true, preRelease: false }, ]; - const actual = merge(localExtensions, remoteExtensions, localExtensions, [], []); + const actual = merge(localExtensions, remoteExtensions, localExtensions, [], [], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -761,7 +761,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', installed: true }, ]; - const actual = merge(localExtensions, remoteExtensions, remoteExtensions, [], []); + const actual = merge(localExtensions, remoteExtensions, remoteExtensions, [], [], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -777,7 +777,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', installed: true, preRelease: true }, ]; - const actual = merge(localExtensions, remoteExtensions, remoteExtensions, [], []); + const actual = merge(localExtensions, remoteExtensions, remoteExtensions, [], [], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -793,7 +793,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', installed: true, preRelease: true }, ]; - const actual = merge(localExtensions, remoteExtensions, remoteExtensions, [], []); + const actual = merge(localExtensions, remoteExtensions, remoteExtensions, [], [], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -809,7 +809,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', installed: true, preRelease: true }, ]; - const actual = merge(localExtensions, remoteExtensions, remoteExtensions, [], []); + const actual = merge(localExtensions, remoteExtensions, remoteExtensions, [], [], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -828,7 +828,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', installed: true, preRelease: true, disabled: true }, ]; - const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], []); + const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], [], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -844,7 +844,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'a', uuid: 'a' }, version: '1.1.0', installed: true, preRelease: false }, ]; - const actual = merge(localExtensions, remoteExtensions, remoteExtensions, [], []); + const actual = merge(localExtensions, remoteExtensions, remoteExtensions, [], [], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -860,7 +860,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'a', uuid: 'a' }, version: '1.1.0', installed: true, preRelease: false }, ]; - const actual = merge(localExtensions, remoteExtensions, remoteExtensions, [], []); + const actual = merge(localExtensions, remoteExtensions, remoteExtensions, [], [], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -879,7 +879,7 @@ suite('ExtensionsMerge', () => { { identifier: { id: 'a', uuid: 'a' }, version: '1.1.0', installed: true, preRelease: false, disabled: true }, ]; - const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], []); + const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], [], []); assert.deepStrictEqual(actual.local.added, []); assert.deepStrictEqual(actual.local.removed, []); @@ -887,4 +887,146 @@ suite('ExtensionsMerge', () => { assert.deepStrictEqual(actual.remote, null); }); + test('merge: base has builtin extension, local does not have extension, remote has extension installed', () => { + const localExtensions: ISyncExtensionWithVersion[] = []; + const baseExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, version: '1.1.0', installed: false, preRelease: false }, + ]; + const remoteExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, version: '1.1.0', installed: true, preRelease: false }, + ]; + + const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], [], []); + + assert.deepStrictEqual(actual.local.added, [{ identifier: { id: 'a', uuid: 'a' }, version: '1.1.0', installed: true, preRelease: false }]); + assert.deepStrictEqual(actual.local.removed, []); + assert.deepStrictEqual(actual.local.updated, []); + assert.deepStrictEqual(actual.remote, null); + }); + + test('merge: base has installed extension, local has installed extension, remote has extension builtin', () => { + const localExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', installed: true }, + ]; + const baseExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', installed: true }, + ]; + const remoteExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', installed: false }, + ]; + + const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], [], []); + + assert.deepStrictEqual(actual.local.added, []); + assert.deepStrictEqual(actual.local.removed, [{ id: 'a', uuid: 'a' }]); + assert.deepStrictEqual(actual.local.updated, []); + assert.deepStrictEqual(actual.remote, null); + }); + + test('merge: base has installed extension, local has builtin extension, remote does not has extension', () => { + const localExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', installed: false }, + ]; + const baseExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', installed: true }, + ]; + const remoteExtensions: ISyncExtensionWithVersion[] = []; + + const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], [], []); + + assert.deepStrictEqual(actual.local.added, []); + assert.deepStrictEqual(actual.local.removed, []); + assert.deepStrictEqual(actual.local.updated, []); + assert.deepStrictEqual(actual.remote?.all, [{ identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', preRelease: false }]); + }); + + test('merge: base has builtin extension, local has installed extension, remote has builtin extension with updated state', () => { + const localExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', installed: true }, + ]; + const baseExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', installed: false }, + ]; + const remoteExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', installed: false, state: { 'a': 1 } }, + ]; + + const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], [], [{ id: 'a', uuid: 'a' }]); + + assert.deepStrictEqual(actual.local.added, []); + assert.deepStrictEqual(actual.local.removed, []); + assert.deepStrictEqual(actual.local.updated, [{ identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', preRelease: false, installed: true, state: { 'a': 1 } }]); + assert.deepStrictEqual(actual.remote?.all, [{ identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', preRelease: false, installed: true, state: { 'a': 1 } }]); + }); + + test('merge: base has installed extension, last time synced as builtin extension, local has installed extension, remote has builtin extension with updated state', () => { + const localExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', installed: true }, + ]; + const baseExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', installed: true }, + ]; + const remoteExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', installed: false, state: { 'a': 1 } }, + ]; + + const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], [], [{ id: 'a', uuid: 'a' }]); + + assert.deepStrictEqual(actual.local.added, []); + assert.deepStrictEqual(actual.local.removed, []); + assert.deepStrictEqual(actual.local.updated, [{ identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', preRelease: false, installed: true, state: { 'a': 1 } }]); + assert.deepStrictEqual(actual.remote?.all, [{ identifier: { id: 'a', uuid: 'a' }, version: '1.0.0', preRelease: false, installed: true, state: { 'a': 1 } }]); + }); + + test('merge: base has builtin extension, local does not have extension, remote has builtin extension', () => { + const localExtensions: ISyncExtensionWithVersion[] = []; + const baseExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, version: '1.1.0', installed: false, preRelease: false }, + ]; + const remoteExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, version: '1.1.0', installed: false, preRelease: false }, + ]; + + const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], [], []); + + assert.deepStrictEqual(actual.local.added, []); + assert.deepStrictEqual(actual.local.removed, []); + assert.deepStrictEqual(actual.local.updated, []); + assert.deepStrictEqual(actual.remote, null); + }); + + test('merge: base has installed extension, last synced as builtin, local does not have extension, remote has installed extension', () => { + const localExtensions: ISyncExtensionWithVersion[] = []; + const baseExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, version: '1.1.0', installed: true, preRelease: false }, + ]; + const remoteExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, version: '1.1.0', installed: true, preRelease: false }, + ]; + + const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], [], [{ id: 'a', uuid: 'a' }]); + + assert.deepStrictEqual(actual.local.added, []); + assert.deepStrictEqual(actual.local.removed, []); + assert.deepStrictEqual(actual.local.updated, []); + assert.deepStrictEqual(actual.remote, null); + }); + + test('merge: base has builtin extension, last synced as builtin, local does not have extension, remote has installed extension', () => { + const localExtensions: ISyncExtensionWithVersion[] = []; + const baseExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, version: '1.1.0', installed: false, preRelease: false }, + ]; + const remoteExtensions: ISyncExtensionWithVersion[] = [ + { identifier: { id: 'a', uuid: 'a' }, version: '1.1.0', installed: true, preRelease: false }, + ]; + + const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], [], [{ id: 'a', uuid: 'a' }]); + + assert.deepStrictEqual(actual.local.added, [{ identifier: { id: 'a', uuid: 'a' }, version: '1.1.0', installed: true, preRelease: false }]); + assert.deepStrictEqual(actual.local.removed, []); + assert.deepStrictEqual(actual.local.updated, []); + assert.deepStrictEqual(actual.remote, null); + }); + }); diff --git a/src/vs/platform/userDataSync/test/common/globalStateMerge.test.ts b/src/vs/platform/userDataSync/test/common/globalStateMerge.test.ts index 50ff413ed27..be518dba470 100644 --- a/src/vs/platform/userDataSync/test/common/globalStateMerge.test.ts +++ b/src/vs/platform/userDataSync/test/common/globalStateMerge.test.ts @@ -18,7 +18,7 @@ suite('GlobalStateMerge', () => { assert.deepStrictEqual(actual.local.added, {}); assert.deepStrictEqual(actual.local.updated, {}); assert.deepStrictEqual(actual.local.removed, []); - assert.deepStrictEqual(actual.remote, null); + assert.deepStrictEqual(actual.remote.all, null); }); test('merge when local and remote are same with multiple entries and local is not synced yet', async () => { @@ -30,7 +30,7 @@ suite('GlobalStateMerge', () => { assert.deepStrictEqual(actual.local.added, {}); assert.deepStrictEqual(actual.local.updated, {}); assert.deepStrictEqual(actual.local.removed, []); - assert.deepStrictEqual(actual.remote, null); + assert.deepStrictEqual(actual.remote.all, null); }); test('merge when local and remote are same with multiple entries in different order and local is not synced yet', async () => { @@ -42,7 +42,7 @@ suite('GlobalStateMerge', () => { assert.deepStrictEqual(actual.local.added, {}); assert.deepStrictEqual(actual.local.updated, {}); assert.deepStrictEqual(actual.local.removed, []); - assert.deepStrictEqual(actual.remote, null); + assert.deepStrictEqual(actual.remote.all, null); }); test('merge when local and remote are same with different base content', async () => { @@ -55,7 +55,7 @@ suite('GlobalStateMerge', () => { assert.deepStrictEqual(actual.local.added, {}); assert.deepStrictEqual(actual.local.updated, {}); assert.deepStrictEqual(actual.local.removed, []); - assert.deepStrictEqual(actual.remote, null); + assert.deepStrictEqual(actual.remote.all, null); }); test('merge when a new entry is added to remote and local has not synced yet', async () => { @@ -67,7 +67,7 @@ suite('GlobalStateMerge', () => { assert.deepStrictEqual(actual.local.added, { 'b': { version: 1, value: 'b' } }); assert.deepStrictEqual(actual.local.updated, {}); assert.deepStrictEqual(actual.local.removed, []); - assert.deepStrictEqual(actual.remote, null); + assert.deepStrictEqual(actual.remote.all, null); }); test('merge when multiple new entries are added to remote and local is not synced yet', async () => { @@ -79,7 +79,7 @@ suite('GlobalStateMerge', () => { assert.deepStrictEqual(actual.local.added, { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }); assert.deepStrictEqual(actual.local.updated, {}); assert.deepStrictEqual(actual.local.removed, []); - assert.deepStrictEqual(actual.remote, null); + assert.deepStrictEqual(actual.remote.all, null); }); test('merge when new entry is added to remote from base and local has not changed', async () => { @@ -91,7 +91,7 @@ suite('GlobalStateMerge', () => { assert.deepStrictEqual(actual.local.added, { 'b': { version: 1, value: 'b' } }); assert.deepStrictEqual(actual.local.updated, {}); assert.deepStrictEqual(actual.local.removed, []); - assert.deepStrictEqual(actual.remote, null); + assert.deepStrictEqual(actual.remote.all, null); }); test('merge when an entry is removed from remote from base and local has not changed', async () => { @@ -103,7 +103,7 @@ suite('GlobalStateMerge', () => { assert.deepStrictEqual(actual.local.added, {}); assert.deepStrictEqual(actual.local.updated, {}); assert.deepStrictEqual(actual.local.removed, ['b']); - assert.deepStrictEqual(actual.remote, null); + assert.deepStrictEqual(actual.remote.all, null); }); test('merge when all entries are removed from base and local has not changed', async () => { @@ -115,7 +115,7 @@ suite('GlobalStateMerge', () => { assert.deepStrictEqual(actual.local.added, {}); assert.deepStrictEqual(actual.local.updated, {}); assert.deepStrictEqual(actual.local.removed, ['b', 'a']); - assert.deepStrictEqual(actual.remote, null); + assert.deepStrictEqual(actual.remote.all, null); }); test('merge when an entry is updated in remote from base and local has not changed', async () => { @@ -127,7 +127,7 @@ suite('GlobalStateMerge', () => { assert.deepStrictEqual(actual.local.added, {}); assert.deepStrictEqual(actual.local.updated, { 'a': { version: 1, value: 'b' } }); assert.deepStrictEqual(actual.local.removed, []); - assert.deepStrictEqual(actual.remote, null); + assert.deepStrictEqual(actual.remote.all, null); }); test('merge when remote has moved forwarded with multiple changes and local stays with base', async () => { @@ -139,7 +139,7 @@ suite('GlobalStateMerge', () => { assert.deepStrictEqual(actual.local.added, { 'c': { version: 1, value: 'c' } }); assert.deepStrictEqual(actual.local.updated, { 'a': { version: 1, value: 'd' } }); assert.deepStrictEqual(actual.local.removed, ['b']); - assert.deepStrictEqual(actual.remote, null); + assert.deepStrictEqual(actual.remote.all, null); }); test('merge when new entries are added to local and local is not synced yet', async () => { @@ -151,7 +151,7 @@ suite('GlobalStateMerge', () => { assert.deepStrictEqual(actual.local.added, {}); assert.deepStrictEqual(actual.local.updated, {}); assert.deepStrictEqual(actual.local.removed, []); - assert.deepStrictEqual(actual.remote, local); + assert.deepStrictEqual(actual.remote.all, local); }); test('merge when multiple new entries are added to local from base and remote is not changed', async () => { @@ -163,7 +163,7 @@ suite('GlobalStateMerge', () => { assert.deepStrictEqual(actual.local.added, {}); assert.deepStrictEqual(actual.local.updated, {}); assert.deepStrictEqual(actual.local.removed, []); - assert.deepStrictEqual(actual.remote, local); + assert.deepStrictEqual(actual.remote.all, local); }); test('merge when an entry is removed from local from base and remote has not changed', async () => { @@ -175,7 +175,7 @@ suite('GlobalStateMerge', () => { assert.deepStrictEqual(actual.local.added, {}); assert.deepStrictEqual(actual.local.updated, {}); assert.deepStrictEqual(actual.local.removed, []); - assert.deepStrictEqual(actual.remote, local); + assert.deepStrictEqual(actual.remote.all, local); }); test('merge when an entry is updated in local from base and remote has not changed', async () => { @@ -187,7 +187,7 @@ suite('GlobalStateMerge', () => { assert.deepStrictEqual(actual.local.added, {}); assert.deepStrictEqual(actual.local.updated, {}); assert.deepStrictEqual(actual.local.removed, []); - assert.deepStrictEqual(actual.remote, local); + assert.deepStrictEqual(actual.remote.all, local); }); test('merge when local has moved forwarded with multiple changes and remote stays with base', async () => { @@ -199,7 +199,7 @@ suite('GlobalStateMerge', () => { assert.deepStrictEqual(actual.local.added, {}); assert.deepStrictEqual(actual.local.updated, {}); assert.deepStrictEqual(actual.local.removed, []); - assert.deepStrictEqual(actual.remote, local); + assert.deepStrictEqual(actual.remote.all, local); }); test('merge when local and remote with one entry but different value and local is not synced yet', async () => { @@ -211,7 +211,7 @@ suite('GlobalStateMerge', () => { assert.deepStrictEqual(actual.local.added, {}); assert.deepStrictEqual(actual.local.updated, { 'a': { version: 1, value: 'b' } }); assert.deepStrictEqual(actual.local.removed, []); - assert.deepStrictEqual(actual.remote, null); + assert.deepStrictEqual(actual.remote.all, null); }); test('merge when the entry is removed in remote but updated in local and a new entry is added in remote', async () => { @@ -224,7 +224,7 @@ suite('GlobalStateMerge', () => { assert.deepStrictEqual(actual.local.added, { 'c': { version: 1, value: 'c' } }); assert.deepStrictEqual(actual.local.updated, {}); assert.deepStrictEqual(actual.local.removed, []); - assert.deepStrictEqual(actual.remote, { 'a': { version: 1, value: 'a' }, 'c': { version: 1, value: 'c' }, 'b': { version: 1, value: 'd' } }); + assert.deepStrictEqual(actual.remote.all, { 'a': { version: 1, value: 'a' }, 'c': { version: 1, value: 'c' }, 'b': { version: 1, value: 'd' } }); }); test('merge with single entry and local is empty', async () => { @@ -237,7 +237,7 @@ suite('GlobalStateMerge', () => { assert.deepStrictEqual(actual.local.added, {}); assert.deepStrictEqual(actual.local.updated, {}); assert.deepStrictEqual(actual.local.removed, []); - assert.deepStrictEqual(actual.remote, local); + assert.deepStrictEqual(actual.remote.all, local); }); test('merge when local and remote has moved forward with conflicts', async () => { @@ -250,7 +250,7 @@ suite('GlobalStateMerge', () => { assert.deepStrictEqual(actual.local.added, {}); assert.deepStrictEqual(actual.local.updated, {}); assert.deepStrictEqual(actual.local.removed, []); - assert.deepStrictEqual(actual.remote, local); + assert.deepStrictEqual(actual.remote.all, local); }); test('merge when a new entry is added to remote but scoped to machine locally and local is not synced yet', async () => { @@ -262,7 +262,7 @@ suite('GlobalStateMerge', () => { assert.deepStrictEqual(actual.local.added, {}); assert.deepStrictEqual(actual.local.updated, {}); assert.deepStrictEqual(actual.local.removed, []); - assert.deepStrictEqual(actual.remote, null); + assert.deepStrictEqual(actual.remote.all, null); }); test('merge when an entry is updated to remote but scoped to machine locally', async () => { @@ -274,7 +274,7 @@ suite('GlobalStateMerge', () => { assert.deepStrictEqual(actual.local.added, {}); assert.deepStrictEqual(actual.local.updated, {}); assert.deepStrictEqual(actual.local.removed, []); - assert.deepStrictEqual(actual.remote, null); + assert.deepStrictEqual(actual.remote.all, null); }); test('merge when a local value is removed and scoped to machine locally', async () => { @@ -287,7 +287,7 @@ suite('GlobalStateMerge', () => { assert.deepStrictEqual(actual.local.added, {}); assert.deepStrictEqual(actual.local.updated, {}); assert.deepStrictEqual(actual.local.removed, []); - assert.deepStrictEqual(actual.remote, local); + assert.deepStrictEqual(actual.remote.all, local); }); test('merge when local moved forwared by changing a key to machine scope', async () => { @@ -300,7 +300,7 @@ suite('GlobalStateMerge', () => { assert.deepStrictEqual(actual.local.added, {}); assert.deepStrictEqual(actual.local.updated, {}); assert.deepStrictEqual(actual.local.removed, []); - assert.deepStrictEqual(actual.remote, local); + assert.deepStrictEqual(actual.remote.all, local); }); test('merge should not remove remote keys if not registered', async () => { @@ -313,7 +313,7 @@ suite('GlobalStateMerge', () => { assert.deepStrictEqual(actual.local.added, {}); assert.deepStrictEqual(actual.local.updated, {}); assert.deepStrictEqual(actual.local.removed, []); - assert.deepStrictEqual(actual.remote, { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' }, 'c': { version: 1, value: 'c' } }); + assert.deepStrictEqual(actual.remote.all, { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' }, 'c': { version: 1, value: 'c' } }); }); }); diff --git a/src/vs/platform/userDataSync/test/common/globalStateSync.test.ts b/src/vs/platform/userDataSync/test/common/globalStateSync.test.ts index 20138fb54d7..110991b2d86 100644 --- a/src/vs/platform/userDataSync/test/common/globalStateSync.test.ts +++ b/src/vs/platform/userDataSync/test/common/globalStateSync.test.ts @@ -6,6 +6,7 @@ import * as assert from 'assert'; import { VSBuffer } from 'vs/base/common/buffer'; import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; @@ -37,7 +38,7 @@ suite('GlobalStateSync', () => { teardown(() => disposableStore.clear()); - test('when global state does not exist', async () => { + test('when global state does not exist', () => runWithFakedTimers({ useFakeTimers: true }, async () => { assert.deepStrictEqual(await testObject.getLastSyncUserData(), null); let manifest = await testClient.getResourceManifest(); server.reset(); @@ -62,9 +63,9 @@ suite('GlobalStateSync', () => { server.reset(); await testObject.sync(manifest); assert.deepStrictEqual(server.requests, []); - }); + })); - test('when global state is created after first sync', async () => { + test('when global state is created after first sync', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await testObject.sync(await testClient.getResourceManifest()); updateUserStorage('a', 'value1', testClient); @@ -82,9 +83,9 @@ suite('GlobalStateSync', () => { assert.deepStrictEqual(lastSyncUserData!.ref, remoteUserData.ref); assert.deepStrictEqual(lastSyncUserData!.syncData, remoteUserData.syncData); assert.deepStrictEqual(JSON.parse(lastSyncUserData!.syncData!.content).storage, { 'a': { version: 1, value: 'value1' } }); - }); + })); - test('first time sync - outgoing to server (no state)', async () => { + test('first time sync - outgoing to server (no state)', () => runWithFakedTimers({ useFakeTimers: true }, async () => { updateUserStorage('a', 'value1', testClient); updateMachineStorage('b', 'value1', testClient); await updateLocale(testClient); @@ -97,9 +98,9 @@ suite('GlobalStateSync', () => { assert.ok(content !== null); const actual = parseGlobalState(content!); assert.deepStrictEqual(actual.storage, { 'globalState.argv.locale': { version: 1, value: 'en' }, 'a': { version: 1, value: 'value1' } }); - }); + })); - test('first time sync - incoming from server (no state)', async () => { + test('first time sync - incoming from server (no state)', () => runWithFakedTimers({ useFakeTimers: true }, async () => { updateUserStorage('a', 'value1', client2); await updateLocale(client2); await client2.sync(); @@ -110,9 +111,9 @@ suite('GlobalStateSync', () => { assert.strictEqual(readStorage('a', testClient), 'value1'); assert.strictEqual(await readLocale(testClient), 'en'); - }); + })); - test('first time sync when storage exists', async () => { + test('first time sync when storage exists', () => runWithFakedTimers({ useFakeTimers: true }, async () => { updateUserStorage('a', 'value1', client2); await client2.sync(); @@ -128,9 +129,9 @@ suite('GlobalStateSync', () => { assert.ok(content !== null); const actual = parseGlobalState(content!); assert.deepStrictEqual(actual.storage, { 'a': { version: 1, value: 'value1' }, 'b': { version: 1, value: 'value2' } }); - }); + })); - test('first time sync when storage exists - has conflicts', async () => { + test('first time sync when storage exists - has conflicts', () => runWithFakedTimers({ useFakeTimers: true }, async () => { updateUserStorage('a', 'value1', client2); await client2.sync(); @@ -146,9 +147,9 @@ suite('GlobalStateSync', () => { assert.ok(content !== null); const actual = parseGlobalState(content!); assert.deepStrictEqual(actual.storage, { 'a': { version: 1, value: 'value1' } }); - }); + })); - test('sync adding a storage value', async () => { + test('sync adding a storage value', () => runWithFakedTimers({ useFakeTimers: true }, async () => { updateUserStorage('a', 'value1', testClient); await testObject.sync(await testClient.getResourceManifest()); @@ -164,9 +165,9 @@ suite('GlobalStateSync', () => { assert.ok(content !== null); const actual = parseGlobalState(content!); assert.deepStrictEqual(actual.storage, { 'a': { version: 1, value: 'value1' }, 'b': { version: 1, value: 'value2' } }); - }); + })); - test('sync updating a storage value', async () => { + test('sync updating a storage value', () => runWithFakedTimers({ useFakeTimers: true }, async () => { updateUserStorage('a', 'value1', testClient); await testObject.sync(await testClient.getResourceManifest()); @@ -181,9 +182,9 @@ suite('GlobalStateSync', () => { assert.ok(content !== null); const actual = parseGlobalState(content!); assert.deepStrictEqual(actual.storage, { 'a': { version: 1, value: 'value2' } }); - }); + })); - test('sync removing a storage value', async () => { + test('sync removing a storage value', () => runWithFakedTimers({ useFakeTimers: true }, async () => { updateUserStorage('a', 'value1', testClient); updateUserStorage('b', 'value2', testClient); await testObject.sync(await testClient.getResourceManifest()); @@ -200,9 +201,9 @@ suite('GlobalStateSync', () => { assert.ok(content !== null); const actual = parseGlobalState(content!); assert.deepStrictEqual(actual.storage, { 'a': { version: 1, value: 'value1' } }); - }); + })); - test('sync profile state', async () => { + test('sync profile state', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const client2 = disposableStore.add(new UserDataSyncClient(server)); await client2.setUp(true); const profile = await client2.instantiationService.get(IUserDataProfilesService).createNamedProfile('profile1'); @@ -221,7 +222,7 @@ suite('GlobalStateSync', () => { assert.ok(content !== null); const actual = parseGlobalState(content!); assert.deepStrictEqual(actual.storage, { 'a': { version: 1, value: 'value1' } }); - }); + })); function parseGlobalState(content: string): IGlobalState { const syncData: ISyncData = JSON.parse(content); diff --git a/src/vs/platform/userDataSync/test/common/settingsSync.test.ts b/src/vs/platform/userDataSync/test/common/settingsSync.test.ts index 892014f043c..8c13c2cd43f 100644 --- a/src/vs/platform/userDataSync/test/common/settingsSync.test.ts +++ b/src/vs/platform/userDataSync/test/common/settingsSync.test.ts @@ -7,6 +7,7 @@ import * as assert from 'assert'; import { VSBuffer } from 'vs/base/common/buffer'; import { Event } from 'vs/base/common/event'; import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ConfigurationScope, Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { IFileService } from 'vs/platform/files/common/files'; @@ -47,7 +48,7 @@ suite('SettingsSync - Auto', () => { teardown(() => disposableStore.clear()); - test('when settings file does not exist', async () => { + test('when settings file does not exist', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const fileService = client.instantiationService.get(IFileService); const settingResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.settingsResource; @@ -76,9 +77,9 @@ suite('SettingsSync - Auto', () => { server.reset(); await testObject.sync(manifest); assert.deepStrictEqual(server.requests, []); - }); + })); - test('when settings file is empty and remote has no changes', async () => { + test('when settings file is empty and remote has no changes', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const fileService = client.instantiationService.get(IFileService); const settingsResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.settingsResource; await fileService.writeFile(settingsResource, VSBuffer.fromString('')); @@ -90,9 +91,9 @@ suite('SettingsSync - Auto', () => { assert.strictEqual(parseSettingsSyncContent(lastSyncUserData!.syncData!.content!)?.settings, '{}'); assert.strictEqual(parseSettingsSyncContent(remoteUserData!.syncData!.content!)?.settings, '{}'); assert.strictEqual((await fileService.readFile(settingsResource)).value.toString(), ''); - }); + })); - test('when settings file is empty and remote has changes', async () => { + test('when settings file is empty and remote has changes', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const client2 = disposableStore.add(new UserDataSyncClient(server)); await client2.setUp(true); const content = @@ -131,9 +132,9 @@ suite('SettingsSync - Auto', () => { assert.strictEqual(parseSettingsSyncContent(lastSyncUserData!.syncData!.content!)?.settings, content); assert.strictEqual(parseSettingsSyncContent(remoteUserData!.syncData!.content!)?.settings, content); assert.strictEqual((await fileService.readFile(settingsResource)).value.toString(), content); - }); + })); - test('when settings file is created after first sync', async () => { + test('when settings file is created after first sync', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const fileService = client.instantiationService.get(IFileService); const settingsResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.settingsResource; @@ -154,9 +155,9 @@ suite('SettingsSync - Auto', () => { assert.deepStrictEqual(lastSyncUserData!.ref, remoteUserData.ref); assert.deepStrictEqual(lastSyncUserData!.syncData, remoteUserData.syncData); assert.strictEqual(parseSettingsSyncContent(lastSyncUserData!.syncData!.content!)?.settings, '{}'); - }); + })); - test('sync for first time to the server', async () => { + test('sync for first time to the server', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const expected = `{ // Always @@ -187,9 +188,9 @@ suite('SettingsSync - Auto', () => { assert.ok(content !== null); const actual = parseSettings(content!); assert.deepStrictEqual(actual, expected); - }); + })); - test('do not sync machine settings', async () => { + test('do not sync machine settings', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const settingsContent = `{ // Always @@ -218,9 +219,9 @@ suite('SettingsSync - Auto', () => { // Workbench "workbench.colorTheme": "GitHub Sharp" }`); - }); + })); - test('do not sync machine settings when spread across file', async () => { + test('do not sync machine settings when spread across file', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const settingsContent = `{ // Always @@ -249,9 +250,9 @@ suite('SettingsSync - Auto', () => { // Workbench "workbench.colorTheme": "GitHub Sharp" }`); - }); + })); - test('do not sync machine settings when spread across file - 2', async () => { + test('do not sync machine settings when spread across file - 2', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const settingsContent = `{ // Always @@ -280,9 +281,9 @@ suite('SettingsSync - Auto', () => { "workbench.colorTheme": "GitHub Sharp", "files.simpleDialog.enable": true, }`); - }); + })); - test('sync when all settings are machine settings', async () => { + test('sync when all settings are machine settings', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const settingsContent = `{ // Machine @@ -298,9 +299,9 @@ suite('SettingsSync - Auto', () => { const actual = parseSettings(content!); assert.deepStrictEqual(actual, `{ }`); - }); + })); - test('sync when all settings are machine settings with trailing comma', async () => { + test('sync when all settings are machine settings with trailing comma', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const settingsContent = `{ // Machine @@ -317,9 +318,9 @@ suite('SettingsSync - Auto', () => { assert.deepStrictEqual(actual, `{ , }`); - }); + })); - test('local change event is triggered when settings are changed', async () => { + test('local change event is triggered when settings are changed', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const content = `{ "files.autoSave": "afterDelay", @@ -335,9 +336,9 @@ suite('SettingsSync - Auto', () => { "files.simpleDialog.enable": true, }`, client); await promise; - }); + })); - test('do not sync ignored settings', async () => { + test('do not sync ignored settings', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const settingsContent = `{ // Always @@ -380,9 +381,9 @@ suite('SettingsSync - Auto', () => { "terminal.integrated.shell.osx" ] }`); - }); + })); - test('do not sync ignored and machine settings', async () => { + test('do not sync ignored and machine settings', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const settingsContent = `{ // Always @@ -428,9 +429,9 @@ suite('SettingsSync - Auto', () => { "terminal.integrated.shell.osx" ], }`); - }); + })); - test('sync throws invalid content error', async () => { + test('sync throws invalid content error', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const expected = `{ // Always @@ -463,9 +464,9 @@ suite('SettingsSync - Auto', () => { assert.ok(e instanceof UserDataSyncError); assert.deepStrictEqual((e).code, UserDataSyncErrorCode.LocalInvalidContent); } - }); + })); - test('sync throws invalid content error - content is an array', async () => { + test('sync throws invalid content error - content is an array', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await updateSettings('[]', client); try { await testObject.sync(await client.getResourceManifest()); @@ -474,9 +475,9 @@ suite('SettingsSync - Auto', () => { assert.ok(e instanceof UserDataSyncError); assert.deepStrictEqual((e).code, UserDataSyncErrorCode.LocalInvalidContent); } - }); + })); - test('sync when there are conflicts', async () => { + test('sync when there are conflicts', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const client2 = disposableStore.add(new UserDataSyncClient(server)); await client2.setUp(true); await updateSettings(JSON.stringify({ @@ -499,9 +500,9 @@ suite('SettingsSync - Auto', () => { const fileService = client.instantiationService.get(IFileService); const mergeContent = (await fileService.readFile(testObject.conflicts.conflicts[0].previewResource)).value.toString(); assert.strictEqual(mergeContent, ''); - }); + })); - test('sync profile settings', async () => { + test('sync profile settings', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const client2 = disposableStore.add(new UserDataSyncClient(server)); await client2.setUp(true); const profile = await client2.instantiationService.get(IUserDataProfilesService).createNamedProfile('profile1'); @@ -521,7 +522,7 @@ suite('SettingsSync - Auto', () => { 'a': 1, 'b': 2, }); - }); + })); }); @@ -541,7 +542,7 @@ suite('SettingsSync - Manual', () => { teardown(() => disposableStore.clear()); - test('do not sync ignored settings', async () => { + test('do not sync ignored settings', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const settingsContent = `{ // Always @@ -587,7 +588,7 @@ suite('SettingsSync - Manual', () => { "terminal.integrated.shell.osx" ] }`); - }); + })); }); diff --git a/src/vs/platform/userDataSync/test/common/synchronizer.test.ts b/src/vs/platform/userDataSync/test/common/synchronizer.test.ts index 495551412ee..a034c184115 100644 --- a/src/vs/platform/userDataSync/test/common/synchronizer.test.ts +++ b/src/vs/platform/userDataSync/test/common/synchronizer.test.ts @@ -11,6 +11,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { isEqual, joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; +import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; import { IFileService } from 'vs/platform/files/common/files'; import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; @@ -188,7 +189,7 @@ suite('TestSynchronizer - Auto Sync', () => { teardown(() => disposableStore.clear()); - test('status is syncing', async () => { + test('status is syncing', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); const actual: SyncStatus[] = []; @@ -203,9 +204,9 @@ suite('TestSynchronizer - Auto Sync', () => { assert.deepStrictEqual(testObject.status, SyncStatus.Syncing); testObject.stop(); - }); + })); - test('status is set correctly when sync is finished', async () => { + test('status is set correctly when sync is finished', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncBarrier.open(); @@ -215,9 +216,9 @@ suite('TestSynchronizer - Auto Sync', () => { assert.deepStrictEqual(actual, [SyncStatus.Syncing, SyncStatus.Idle]); assert.deepStrictEqual(testObject.status, SyncStatus.Idle); - }); + })); - test('status is set correctly when sync has errors', async () => { + test('status is set correctly when sync has errors', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasError: true, hasConflicts: false }; testObject.syncBarrier.open(); @@ -232,9 +233,9 @@ suite('TestSynchronizer - Auto Sync', () => { assert.deepStrictEqual(actual, [SyncStatus.Syncing, SyncStatus.Idle]); assert.deepStrictEqual(testObject.status, SyncStatus.Idle); } - }); + })); - test('status is set to hasConflicts when asked to sync if there are conflicts', async () => { + test('status is set to hasConflicts when asked to sync if there are conflicts', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: true, hasError: false }; testObject.syncBarrier.open(); @@ -243,9 +244,9 @@ suite('TestSynchronizer - Auto Sync', () => { assert.deepStrictEqual(testObject.status, SyncStatus.HasConflicts); assertConflicts(testObject.conflicts.conflicts, [testObject.localResource]); - }); + })); - test('sync should not run if syncing already', async () => { + test('sync should not run if syncing already', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); const promise = Event.toPromise(testObject.onDoSyncCall.event); @@ -260,9 +261,9 @@ suite('TestSynchronizer - Auto Sync', () => { assert.deepStrictEqual(testObject.status, SyncStatus.Syncing); await testObject.stop(); - }); + })); - test('sync should not run if there are conflicts', async () => { + test('sync should not run if there are conflicts', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: true, hasError: false }; testObject.syncBarrier.open(); @@ -274,9 +275,9 @@ suite('TestSynchronizer - Auto Sync', () => { assert.deepStrictEqual(actual, []); assert.deepStrictEqual(testObject.status, SyncStatus.HasConflicts); - }); + })); - test('accept preview during conflicts', async () => { + test('accept preview during conflicts', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: true, hasError: false }; testObject.syncBarrier.open(); @@ -292,9 +293,9 @@ suite('TestSynchronizer - Auto Sync', () => { assert.deepStrictEqual(testObject.status, SyncStatus.Idle); const fileService = client.instantiationService.get(IFileService); assert.strictEqual((await testObject.getRemoteUserData(null)).syncData?.content, (await fileService.readFile(testObject.localResource)).value.toString()); - }); + })); - test('accept remote during conflicts', async () => { + test('accept remote during conflicts', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncBarrier.open(); await testObject.sync(await client.getResourceManifest()); @@ -315,9 +316,9 @@ suite('TestSynchronizer - Auto Sync', () => { assert.deepStrictEqual(testObject.status, SyncStatus.Idle); assert.strictEqual((await testObject.getRemoteUserData(null)).syncData?.content, currentRemoteContent); assert.strictEqual((await fileService.readFile(testObject.localResource)).value.toString(), currentRemoteContent); - }); + })); - test('accept local during conflicts', async () => { + test('accept local during conflicts', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncBarrier.open(); await testObject.sync(await client.getResourceManifest()); @@ -337,9 +338,9 @@ suite('TestSynchronizer - Auto Sync', () => { assert.deepStrictEqual(testObject.status, SyncStatus.Idle); assert.strictEqual((await testObject.getRemoteUserData(null)).syncData?.content, newLocalContent); assert.strictEqual((await fileService.readFile(testObject.localResource)).value.toString(), newLocalContent); - }); + })); - test('accept new content during conflicts', async () => { + test('accept new content during conflicts', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncBarrier.open(); await testObject.sync(await client.getResourceManifest()); @@ -360,9 +361,9 @@ suite('TestSynchronizer - Auto Sync', () => { assert.deepStrictEqual(testObject.status, SyncStatus.Idle); assert.strictEqual((await testObject.getRemoteUserData(null)).syncData?.content, mergeContent); assert.strictEqual((await fileService.readFile(testObject.localResource)).value.toString(), mergeContent); - }); + })); - test('accept delete during conflicts', async () => { + test('accept delete during conflicts', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncBarrier.open(); await testObject.sync(await client.getResourceManifest()); @@ -382,9 +383,9 @@ suite('TestSynchronizer - Auto Sync', () => { assert.deepStrictEqual(testObject.status, SyncStatus.Idle); assert.strictEqual((await testObject.getRemoteUserData(null)).syncData?.content, ''); assert.ok(!(await fileService.exists(testObject.localResource))); - }); + })); - test('accept deleted local during conflicts', async () => { + test('accept deleted local during conflicts', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncBarrier.open(); await testObject.sync(await client.getResourceManifest()); @@ -403,9 +404,9 @@ suite('TestSynchronizer - Auto Sync', () => { assert.deepStrictEqual(testObject.status, SyncStatus.Idle); assert.strictEqual((await testObject.getRemoteUserData(null)).syncData?.content, ''); assert.ok(!(await fileService.exists(testObject.localResource))); - }); + })); - test('accept deleted remote during conflicts', async () => { + test('accept deleted remote during conflicts', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncBarrier.open(); const fileService = client.instantiationService.get(IFileService); @@ -423,9 +424,9 @@ suite('TestSynchronizer - Auto Sync', () => { assert.deepStrictEqual(testObject.status, SyncStatus.Idle); assert.strictEqual((await testObject.getRemoteUserData(null)).syncData, null); assert.ok(!(await fileService.exists(testObject.localResource))); - }); + })); - test('request latest data on precondition failure', async () => { + test('request latest data on precondition failure', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); // Sync once testObject.syncBarrier.open(); @@ -450,9 +451,9 @@ suite('TestSynchronizer - Auto Sync', () => { { type: 'GET', url: `${server.url}/v1/resource/${testObject.resource}/latest`, headers: {} }, { type: 'POST', url: `${server.url}/v1/resource/${testObject.resource}`, headers: { 'If-Match': `${parseInt(ref) + 1}` } }, ]); - }); + })); - test('no requests are made to server when local change is triggered', async () => { + test('no requests are made to server when local change is triggered', () => runWithFakedTimers({ useFakeTimers: true }, () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncBarrier.open(); await testObject.sync(await client.getResourceManifest()); @@ -463,9 +464,9 @@ suite('TestSynchronizer - Auto Sync', () => { await promise; assert.deepStrictEqual(server.requests, []); - }); + }))); - test('status is reset when getting latest remote data fails', async () => { + test('status is reset when getting latest remote data fails', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.failWhenGettingLatestRemoteUserData = true; @@ -476,7 +477,7 @@ suite('TestSynchronizer - Auto Sync', () => { } assert.strictEqual(testObject.status, SyncStatus.Idle); - }); + })); }); suite('TestSynchronizer - Manual Sync', () => { @@ -496,7 +497,7 @@ suite('TestSynchronizer - Manual Sync', () => { teardown(() => disposableStore.clear()); - test('preview', async () => { + test('preview', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); @@ -506,9 +507,9 @@ suite('TestSynchronizer - Manual Sync', () => { assert.deepStrictEqual(testObject.status, SyncStatus.Syncing); assertPreviews(preview!.resourcePreviews, [testObject.localResource]); assertConflicts(testObject.conflicts.conflicts, []); - }); + })); - test('preview -> merge', async () => { + test('preview -> merge', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); @@ -520,9 +521,9 @@ suite('TestSynchronizer - Manual Sync', () => { assertPreviews(preview!.resourcePreviews, [testObject.localResource]); assert.strictEqual(preview!.resourcePreviews[0].mergeState, MergeState.Accepted); assertConflicts(testObject.conflicts.conflicts, []); - }); + })); - test('preview -> accept', async () => { + test('preview -> accept', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); @@ -534,9 +535,9 @@ suite('TestSynchronizer - Manual Sync', () => { assertPreviews(preview!.resourcePreviews, [testObject.localResource]); assert.strictEqual(preview!.resourcePreviews[0].mergeState, MergeState.Accepted); assertConflicts(testObject.conflicts.conflicts, []); - }); + })); - test('preview -> merge -> accept', async () => { + test('preview -> merge -> accept', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); @@ -549,9 +550,9 @@ suite('TestSynchronizer - Manual Sync', () => { assertPreviews(preview!.resourcePreviews, [testObject.localResource]); assert.strictEqual(preview!.resourcePreviews[0].mergeState, MergeState.Accepted); assertConflicts(testObject.conflicts.conflicts, []); - }); + })); - test('preview -> merge -> apply', async () => { + test('preview -> merge -> apply', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); @@ -569,9 +570,9 @@ suite('TestSynchronizer - Manual Sync', () => { const expectedContent = manifest![testObject.resource]; assert.strictEqual((await testObject.getRemoteUserData(null)).syncData?.content, expectedContent); assert.strictEqual((await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(), expectedContent); - }); + })); - test('preview -> accept -> apply', async () => { + test('preview -> accept -> apply', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); @@ -589,9 +590,9 @@ suite('TestSynchronizer - Manual Sync', () => { assert.strictEqual((await testObject.getRemoteUserData(null)).syncData?.content, expectedContent); assert.strictEqual((await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(), expectedContent); - }); + })); - test('preview -> merge -> accept -> apply', async () => { + test('preview -> merge -> accept -> apply', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); @@ -609,9 +610,9 @@ suite('TestSynchronizer - Manual Sync', () => { assert.strictEqual((await testObject.getRemoteUserData(null)).syncData?.content, expectedContent); assert.strictEqual((await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(), expectedContent); - }); + })); - test('preview -> accept', async () => { + test('preview -> accept', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); @@ -622,9 +623,9 @@ suite('TestSynchronizer - Manual Sync', () => { assert.deepStrictEqual(testObject.status, SyncStatus.Syncing); assertPreviews(preview!.resourcePreviews, [testObject.localResource]); assertConflicts(testObject.conflicts.conflicts, []); - }); + })); - test('preview -> accept -> apply', async () => { + test('preview -> accept -> apply', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); @@ -642,9 +643,9 @@ suite('TestSynchronizer - Manual Sync', () => { assert.strictEqual((await testObject.getRemoteUserData(null)).syncData?.content, expectedContent); assert.strictEqual((await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(), expectedContent); - }); + })); - test('preivew -> merge -> discard', async () => { + test('preivew -> merge -> discard', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); @@ -657,9 +658,9 @@ suite('TestSynchronizer - Manual Sync', () => { assertPreviews(preview!.resourcePreviews, [testObject.localResource]); assert.strictEqual(preview!.resourcePreviews[0].mergeState, MergeState.Preview); assertConflicts(testObject.conflicts.conflicts, []); - }); + })); - test('preivew -> merge -> discard -> accept', async () => { + test('preivew -> merge -> discard -> accept', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); @@ -673,9 +674,9 @@ suite('TestSynchronizer - Manual Sync', () => { assertPreviews(preview!.resourcePreviews, [testObject.localResource]); assert.strictEqual(preview!.resourcePreviews[0].mergeState, MergeState.Accepted); assertConflicts(testObject.conflicts.conflicts, []); - }); + })); - test('preivew -> accept -> discard', async () => { + test('preivew -> accept -> discard', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); @@ -688,9 +689,9 @@ suite('TestSynchronizer - Manual Sync', () => { assertPreviews(preview!.resourcePreviews, [testObject.localResource]); assert.strictEqual(preview!.resourcePreviews[0].mergeState, MergeState.Preview); assertConflicts(testObject.conflicts.conflicts, []); - }); + })); - test('preivew -> accept -> discard -> accept', async () => { + test('preivew -> accept -> discard -> accept', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); @@ -704,9 +705,9 @@ suite('TestSynchronizer - Manual Sync', () => { assertPreviews(preview!.resourcePreviews, [testObject.localResource]); assert.strictEqual(preview!.resourcePreviews[0].mergeState, MergeState.Accepted); assertConflicts(testObject.conflicts.conflicts, []); - }); + })); - test('preivew -> accept -> discard -> merge', async () => { + test('preivew -> accept -> discard -> merge', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); @@ -720,9 +721,9 @@ suite('TestSynchronizer - Manual Sync', () => { assertPreviews(preview!.resourcePreviews, [testObject.localResource]); assert.strictEqual(preview!.resourcePreviews[0].mergeState, MergeState.Accepted); assertConflicts(testObject.conflicts.conflicts, []); - }); + })); - test('preivew -> merge -> accept -> discard', async () => { + test('preivew -> merge -> accept -> discard', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); @@ -736,9 +737,9 @@ suite('TestSynchronizer - Manual Sync', () => { assertPreviews(preview!.resourcePreviews, [testObject.localResource]); assert.strictEqual(preview!.resourcePreviews[0].mergeState, MergeState.Preview); assertConflicts(testObject.conflicts.conflicts, []); - }); + })); - test('preivew -> merge -> discard -> accept -> apply', async () => { + test('preivew -> merge -> discard -> accept -> apply', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); @@ -756,9 +757,9 @@ suite('TestSynchronizer - Manual Sync', () => { assertConflicts(testObject.conflicts.conflicts, []); assert.strictEqual((await testObject.getRemoteUserData(null)).syncData?.content, expectedContent); assert.strictEqual((await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(), expectedContent); - }); + })); - test('preivew -> accept -> discard -> accept -> apply', async () => { + test('preivew -> accept -> discard -> accept -> apply', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); @@ -777,9 +778,9 @@ suite('TestSynchronizer - Manual Sync', () => { assertConflicts(testObject.conflicts.conflicts, []); assert.strictEqual((await testObject.getRemoteUserData(null)).syncData?.content, expectedContent); assert.strictEqual((await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(), expectedContent); - }); + })); - test('preivew -> accept -> discard -> merge -> apply', async () => { + test('preivew -> accept -> discard -> merge -> apply', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); @@ -800,9 +801,9 @@ suite('TestSynchronizer - Manual Sync', () => { assert.strictEqual((await testObject.getRemoteUserData(null)).syncData?.content, expectedContent); assert.strictEqual((await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(), expectedContent); - }); + })); - test('conflicts: preview', async () => { + test('conflicts: preview', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: true, hasError: false }; testObject.syncBarrier.open(); @@ -812,9 +813,9 @@ suite('TestSynchronizer - Manual Sync', () => { assert.deepStrictEqual(testObject.status, SyncStatus.Syncing); assertPreviews(preview!.resourcePreviews, [testObject.localResource]); assertConflicts(testObject.conflicts.conflicts, []); - }); + })); - test('conflicts: preview -> merge', async () => { + test('conflicts: preview -> merge', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: true, hasError: false }; testObject.syncBarrier.open(); @@ -826,9 +827,9 @@ suite('TestSynchronizer - Manual Sync', () => { assertPreviews(preview!.resourcePreviews, [testObject.localResource]); assert.strictEqual(preview!.resourcePreviews[0].mergeState, MergeState.Conflict); assertConflicts(testObject.conflicts.conflicts, [preview!.resourcePreviews[0].localResource]); - }); + })); - test('conflicts: preview -> merge -> discard', async () => { + test('conflicts: preview -> merge -> discard', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: true, hasError: false }; testObject.syncBarrier.open(); @@ -841,9 +842,9 @@ suite('TestSynchronizer - Manual Sync', () => { assertPreviews(preview!.resourcePreviews, [testObject.localResource]); assert.strictEqual(preview!.resourcePreviews[0].mergeState, MergeState.Preview); assertConflicts(testObject.conflicts.conflicts, []); - }); + })); - test('conflicts: preview -> accept', async () => { + test('conflicts: preview -> accept', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: true, hasError: false }; testObject.syncBarrier.open(); @@ -856,9 +857,9 @@ suite('TestSynchronizer - Manual Sync', () => { assert.deepStrictEqual(testObject.status, SyncStatus.Syncing); assertPreviews(preview!.resourcePreviews, [testObject.localResource]); assert.deepStrictEqual(testObject.conflicts.conflicts, []); - }); + })); - test('conflicts: preview -> merge -> accept -> apply', async () => { + test('conflicts: preview -> merge -> accept -> apply', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); @@ -879,9 +880,9 @@ suite('TestSynchronizer - Manual Sync', () => { assert.strictEqual((await testObject.getRemoteUserData(null)).syncData?.content, expectedContent); assert.strictEqual((await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(), expectedContent); - }); + })); - test('conflicts: preview -> accept', async () => { + test('conflicts: preview -> accept', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: true, hasError: false }; testObject.syncBarrier.open(); @@ -893,9 +894,9 @@ suite('TestSynchronizer - Manual Sync', () => { assert.deepStrictEqual(testObject.status, SyncStatus.Syncing); assertPreviews(preview!.resourcePreviews, [testObject.localResource]); assertConflicts(testObject.conflicts.conflicts, []); - }); + })); - test('conflicts: preview -> accept -> apply', async () => { + test('conflicts: preview -> accept -> apply', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); @@ -915,9 +916,9 @@ suite('TestSynchronizer - Manual Sync', () => { assert.strictEqual((await testObject.getRemoteUserData(null)).syncData?.content, expectedContent); assert.strictEqual((await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(), expectedContent); - }); + })); - test('conflicts: preivew -> merge -> discard', async () => { + test('conflicts: preivew -> merge -> discard', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: true, hasError: false }; testObject.syncBarrier.open(); @@ -930,9 +931,9 @@ suite('TestSynchronizer - Manual Sync', () => { assertPreviews(preview!.resourcePreviews, [testObject.localResource]); assert.strictEqual(preview!.resourcePreviews[0].mergeState, MergeState.Preview); assertConflicts(testObject.conflicts.conflicts, []); - }); + })); - test('conflicts: preivew -> merge -> discard -> accept', async () => { + test('conflicts: preivew -> merge -> discard -> accept', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: true, hasError: false }; testObject.syncBarrier.open(); @@ -946,9 +947,9 @@ suite('TestSynchronizer - Manual Sync', () => { assertPreviews(preview!.resourcePreviews, [testObject.localResource]); assert.strictEqual(preview!.resourcePreviews[0].mergeState, MergeState.Accepted); assertConflicts(testObject.conflicts.conflicts, []); - }); + })); - test('conflicts: preivew -> accept -> discard', async () => { + test('conflicts: preivew -> accept -> discard', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: true, hasError: false }; testObject.syncBarrier.open(); @@ -961,9 +962,9 @@ suite('TestSynchronizer - Manual Sync', () => { assertPreviews(preview!.resourcePreviews, [testObject.localResource]); assert.strictEqual(preview!.resourcePreviews[0].mergeState, MergeState.Preview); assertConflicts(testObject.conflicts.conflicts, []); - }); + })); - test('conflicts: preivew -> accept -> discard -> accept', async () => { + test('conflicts: preivew -> accept -> discard -> accept', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: true, hasError: false }; testObject.syncBarrier.open(); @@ -977,9 +978,9 @@ suite('TestSynchronizer - Manual Sync', () => { assertPreviews(preview!.resourcePreviews, [testObject.localResource]); assert.strictEqual(preview!.resourcePreviews[0].mergeState, MergeState.Accepted); assertConflicts(testObject.conflicts.conflicts, []); - }); + })); - test('conflicts: preivew -> accept -> discard -> merge', async () => { + test('conflicts: preivew -> accept -> discard -> merge', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: true, hasError: false }; testObject.syncBarrier.open(); @@ -993,9 +994,9 @@ suite('TestSynchronizer - Manual Sync', () => { assertPreviews(preview!.resourcePreviews, [testObject.localResource]); assert.strictEqual(preview!.resourcePreviews[0].mergeState, MergeState.Conflict); assertConflicts(testObject.conflicts.conflicts, [preview!.resourcePreviews[0].localResource]); - }); + })); - test('conflicts: preivew -> merge -> discard -> merge', async () => { + test('conflicts: preivew -> merge -> discard -> merge', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: true, hasError: false }; testObject.syncBarrier.open(); @@ -1009,9 +1010,9 @@ suite('TestSynchronizer - Manual Sync', () => { assertPreviews(preview!.resourcePreviews, [testObject.localResource]); assert.strictEqual(preview!.resourcePreviews[0].mergeState, MergeState.Conflict); assertConflicts(testObject.conflicts.conflicts, [preview!.resourcePreviews[0].localResource]); - }); + })); - test('conflicts: preivew -> merge -> accept -> discard', async () => { + test('conflicts: preivew -> merge -> accept -> discard', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); @@ -1025,9 +1026,9 @@ suite('TestSynchronizer - Manual Sync', () => { assertPreviews(preview!.resourcePreviews, [testObject.localResource]); assert.strictEqual(preview!.resourcePreviews[0].mergeState, MergeState.Preview); assertConflicts(testObject.conflicts.conflicts, []); - }); + })); - test('conflicts: preivew -> merge -> discard -> accept -> apply', async () => { + test('conflicts: preivew -> merge -> discard -> accept -> apply', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); @@ -1045,9 +1046,9 @@ suite('TestSynchronizer - Manual Sync', () => { assertConflicts(testObject.conflicts.conflicts, []); assert.strictEqual((await testObject.getRemoteUserData(null)).syncData?.content, expectedContent); assert.strictEqual((await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(), expectedContent); - }); + })); - test('conflicts: preivew -> accept -> discard -> accept -> apply', async () => { + test('conflicts: preivew -> accept -> discard -> accept -> apply', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); @@ -1066,7 +1067,7 @@ suite('TestSynchronizer - Manual Sync', () => { assertConflicts(testObject.conflicts.conflicts, []); assert.strictEqual((await testObject.getRemoteUserData(null)).syncData?.content, expectedContent); assert.strictEqual((await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(), expectedContent); - }); + })); }); diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncProfilesStorageService.test.ts b/src/vs/platform/userDataSync/test/common/userDataSyncProfilesStorageService.test.ts index 66af196c356..a282d1e2d84 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncProfilesStorageService.test.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncProfilesStorageService.test.ts @@ -11,6 +11,7 @@ import { InMemoryStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest, Stor import { AbstractUserDataSyncProfilesStorageService, IUserDataSyncProfilesStorageService } from 'vs/platform/userDataSync/common/userDataSyncProfilesStorageService'; import { InMemoryStorageService, loadKeyTargets, StorageTarget, TARGET_KEY } from 'vs/platform/storage/common/storage'; import { IUserDataProfile, toUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; class TestStorageDatabase extends InMemoryStorageDatabase { @@ -56,13 +57,13 @@ suite('ProfileStorageService', () => { teardown(() => disposables.clear()); - test('read empty storage', async () => { + test('read empty storage', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const actual = await testObject.readStorageData(profile); assert.strictEqual(actual.size, 0); - }); + })); - test('read storage with data', async () => { + test('read storage with data', () => runWithFakedTimers({ useFakeTimers: true }, async () => { storage.set('foo', 'bar'); storage.set(TARGET_KEY, JSON.stringify({ foo: StorageTarget.USER })); await storage.flush(); @@ -71,9 +72,9 @@ suite('ProfileStorageService', () => { assert.strictEqual(actual.size, 1); assert.deepStrictEqual(actual.get('foo'), { 'value': 'bar', 'target': StorageTarget.USER }); - }); + })); - test('write in empty storage', async () => { + test('write in empty storage', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const data = new Map(); data.set('foo', 'bar'); await testObject.updateStorageData(profile, data, StorageTarget.USER); @@ -81,9 +82,9 @@ suite('ProfileStorageService', () => { assert.strictEqual(storage.items.size, 2); assert.deepStrictEqual(loadKeyTargets(storage), { foo: StorageTarget.USER }); assert.strictEqual(storage.get('foo'), 'bar'); - }); + })); - test('write in storage with data', async () => { + test('write in storage with data', () => runWithFakedTimers({ useFakeTimers: true }, async () => { storage.set('foo', 'bar'); storage.set(TARGET_KEY, JSON.stringify({ foo: StorageTarget.USER })); await storage.flush(); @@ -96,9 +97,9 @@ suite('ProfileStorageService', () => { assert.deepStrictEqual(loadKeyTargets(storage), { foo: StorageTarget.USER, abc: StorageTarget.MACHINE }); assert.strictEqual(storage.get('foo'), 'bar'); assert.strictEqual(storage.get('abc'), 'xyz'); - }); + })); - test('write in storage with data (insert, update, remove)', async () => { + test('write in storage with data (insert, update, remove)', () => runWithFakedTimers({ useFakeTimers: true }, async () => { storage.set('foo', 'bar'); storage.set('abc', 'xyz'); storage.set(TARGET_KEY, JSON.stringify({ foo: StorageTarget.USER, abc: StorageTarget.MACHINE })); @@ -114,6 +115,6 @@ suite('ProfileStorageService', () => { assert.deepStrictEqual(loadKeyTargets(storage), { abc: StorageTarget.USER, var: StorageTarget.USER }); assert.strictEqual(storage.get('abc'), 'def'); assert.strictEqual(storage.get('var'), 'const'); - }); + })); }); diff --git a/src/vs/platform/windows/electron-main/windowImpl.ts b/src/vs/platform/windows/electron-main/windowImpl.ts index c7ce131f8b1..73067506f69 100644 --- a/src/vs/platform/windows/electron-main/windowImpl.ts +++ b/src/vs/platform/windows/electron-main/windowImpl.ts @@ -973,6 +973,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { configuration.isInitialStartup = false; // since this is a reload configuration.policiesData = this.policyService.serialize(); // set policies data again + configuration.continueOn = this.environmentMainService.continueOn; configuration.profiles = { all: this.userDataProfilesService.profiles, profile: this.profile || this.userDataProfilesService.defaultProfile diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 4f1f9d43384..b6893d0611f 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -1367,6 +1367,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic accessibilitySupport: app.accessibilitySupportEnabled, colorScheme: this.themeMainService.getColorScheme(), policiesData: this.policyService.serialize(), + continueOn: this.environmentMainService.continueOn, }; let window: ICodeWindow | undefined; diff --git a/src/vs/platform/workspaces/common/workspaces.ts b/src/vs/platform/workspaces/common/workspaces.ts index 7bb8b5e1ec6..71e4a623f65 100644 --- a/src/vs/platform/workspaces/common/workspaces.ts +++ b/src/vs/platform/workspaces/common/workspaces.ts @@ -331,10 +331,10 @@ function isSerializedRecentFile(data: any): data is ISerializedRecentFile { export function restoreRecentlyOpened(data: RecentlyOpenedStorageData | undefined, logService: ILogService): IRecentlyOpened { const result: IRecentlyOpened = { workspaces: [], files: [] }; if (data) { - const restoreGracefully = function (entries: T[], func: (entry: T, index: number) => void) { + const restoreGracefully = function (entries: T[], onEntry: (entry: T, index: number) => void) { for (let i = 0; i < entries.length; i++) { try { - func(entries[i], i); + onEntry(entries[i], i); } catch (e) { logService.warn(`Error restoring recent entry ${JSON.stringify(entries[i])}: ${e.toString()}. Skip entry.`); } @@ -343,7 +343,7 @@ export function restoreRecentlyOpened(data: RecentlyOpenedStorageData | undefine const storedRecents = data as ISerializedRecentlyOpened; if (Array.isArray(storedRecents.entries)) { - restoreGracefully(storedRecents.entries, (entry) => { + restoreGracefully(storedRecents.entries, entry => { const label = entry.label; const remoteAuthority = entry.remoteAuthority; diff --git a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts index 7dc3f039050..2e8ebbcc496 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts @@ -23,6 +23,7 @@ import { IApplicationStorageMainService } from 'vs/platform/storage/electron-mai import { IRecent, IRecentFile, IRecentFolder, IRecentlyOpened, IRecentWorkspace, isRecentFile, isRecentFolder, isRecentWorkspace, restoreRecentlyOpened, toStoreData } from 'vs/platform/workspaces/common/workspaces'; import { IWorkspaceIdentifier, WORKSPACE_EXTENSION } from 'vs/platform/workspace/common/workspace'; import { IWorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService'; +import { ResourceMap } from 'vs/base/common/map'; export const IWorkspacesHistoryMainService = createDecorator('workspacesHistoryMainService'); @@ -73,28 +74,28 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa //#region Workspaces History async addRecentlyOpened(recentToAdd: IRecent[]): Promise { - const workspaces: Array = []; - const files: IRecentFile[] = []; + let workspaces: Array = []; + let files: IRecentFile[] = []; for (const recent of recentToAdd) { // Workspace if (isRecentWorkspace(recent)) { - if (!this.workspacesManagementMainService.isUntitledWorkspace(recent.workspace) && this.indexOfWorkspace(workspaces, recent.workspace) === -1) { + if (!this.workspacesManagementMainService.isUntitledWorkspace(recent.workspace) && !this.containsWorkspace(workspaces, recent.workspace)) { workspaces.push(recent); } } // Folder else if (isRecentFolder(recent)) { - if (this.indexOfFolder(workspaces, recent.folderUri) === -1) { + if (!this.containsFolder(workspaces, recent.folderUri)) { workspaces.push(recent); } } // File else { - const alreadyExistsInHistory = this.indexOfFile(files, recent.fileUri) >= 0; + const alreadyExistsInHistory = this.containsFile(files, recent.fileUri); const shouldBeFiltered = recent.fileUri.scheme === Schemas.file && WorkspacesHistoryMainService.COMMON_FILES_FILTER.indexOf(basename(recent.fileUri)) >= 0; if (!alreadyExistsInHistory && !shouldBeFiltered) { @@ -108,7 +109,9 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa } } - await this.addEntriesFromStorage(workspaces, files); + const mergedEntries = await this.mergeEntriesFromStorage({ workspaces, files }); + workspaces = mergedEntries.workspaces; + files = mergedEntries.files; if (workspaces.length > WorkspacesHistoryMainService.MAX_TOTAL_RECENT_ENTRIES) { workspaces.length = WorkspacesHistoryMainService.MAX_TOTAL_RECENT_ENTRIES; @@ -163,35 +166,53 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa } async getRecentlyOpened(): Promise { - const workspaces: Array = []; - const files: IRecentFile[] = []; - - await this.addEntriesFromStorage(workspaces, files); - - return { workspaces, files }; + return this.mergeEntriesFromStorage(); } - private async addEntriesFromStorage(workspaces: Array, files: IRecentFile[]): Promise { + private async mergeEntriesFromStorage(existingEntries?: IRecentlyOpened): Promise { - // Get from storage - const recents = await this.getRecentlyOpenedFromStorage(); - for (const recent of recents.workspaces) { - const index = isRecentFolder(recent) ? this.indexOfFolder(workspaces, recent.folderUri) : this.indexOfWorkspace(workspaces, recent.workspace); - if (index >= 0) { - workspaces[index].label = workspaces[index].label || recent.label; - } else { - workspaces.push(recent); + // Build maps for more efficient lookup of existing entries that + // are passed in by storing based on workspace/file identifier + + const mapWorkspaceIdToWorkspace = new ResourceMap(uri => extUriBiasedIgnorePathCase.getComparisonKey(uri)); + if (existingEntries?.workspaces) { + for (const workspace of existingEntries.workspaces) { + mapWorkspaceIdToWorkspace.set(this.location(workspace), workspace); } } - for (const recent of recents.files) { - const index = this.indexOfFile(files, recent.fileUri); - if (index >= 0) { - files[index].label = files[index].label || recent.label; - } else { - files.push(recent); + const mapFileIdToFile = new ResourceMap(uri => extUriBiasedIgnorePathCase.getComparisonKey(uri)); + if (existingEntries?.files) { + for (const file of existingEntries.files) { + mapFileIdToFile.set(this.location(file), file); } } + + // Merge in entries from storage, preserving existing known entries + + const recentFromStorage = await this.getRecentlyOpenedFromStorage(); + for (const recentWorkspaceFromStorage of recentFromStorage.workspaces) { + const existingRecentWorkspace = mapWorkspaceIdToWorkspace.get(this.location(recentWorkspaceFromStorage)); + if (existingRecentWorkspace) { + existingRecentWorkspace.label = existingRecentWorkspace.label ?? recentWorkspaceFromStorage.label; + } else { + mapWorkspaceIdToWorkspace.set(this.location(recentWorkspaceFromStorage), recentWorkspaceFromStorage); + } + } + + for (const recentFileFromStorage of recentFromStorage.files) { + const existingRecentFile = mapFileIdToFile.get(this.location(recentFileFromStorage)); + if (existingRecentFile) { + existingRecentFile.label = existingRecentFile.label ?? recentFileFromStorage.label; + } else { + mapFileIdToFile.set(this.location(recentFileFromStorage), recentFileFromStorage); + } + } + + return { + workspaces: [...mapWorkspaceIdToWorkspace.values()], + files: [...mapFileIdToFile.values()] + }; } private async getRecentlyOpenedFromStorage(): Promise { @@ -235,16 +256,16 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa return recent.workspace.configPath; } - private indexOfWorkspace(recents: IRecent[], candidate: IWorkspaceIdentifier): number { - return recents.findIndex(recent => isRecentWorkspace(recent) && recent.workspace.id === candidate.id); + private containsWorkspace(recents: IRecent[], candidate: IWorkspaceIdentifier): boolean { + return !!recents.find(recent => isRecentWorkspace(recent) && recent.workspace.id === candidate.id); } - private indexOfFolder(recents: IRecent[], candidate: URI): number { - return recents.findIndex(recent => isRecentFolder(recent) && extUriBiasedIgnorePathCase.isEqual(recent.folderUri, candidate)); + private containsFolder(recents: IRecent[], candidate: URI): boolean { + return !!recents.find(recent => isRecentFolder(recent) && extUriBiasedIgnorePathCase.isEqual(recent.folderUri, candidate)); } - private indexOfFile(recents: IRecentFile[], candidate: URI): number { - return recents.findIndex(recent => extUriBiasedIgnorePathCase.isEqual(recent.fileUri, candidate)); + private containsFile(recents: IRecentFile[], candidate: URI): boolean { + return !!recents.find(recent => extUriBiasedIgnorePathCase.isEqual(recent.fileUri, candidate)); } //#endregion diff --git a/src/vs/server/node/remoteExtensionHostAgentServer.ts b/src/vs/server/node/remoteExtensionHostAgentServer.ts index 7168172c47a..e503b753a52 100644 --- a/src/vs/server/node/remoteExtensionHostAgentServer.ts +++ b/src/vs/server/node/remoteExtensionHostAgentServer.ts @@ -91,7 +91,7 @@ export class RemoteExtensionHostAgentServer extends Disposable implements IServe this._logService.info(`Extension host agent started.`); } - public async handleRequest(req: http.IncomingMessage, res: http.ServerResponse) { + public async handleRequest(req: http.IncomingMessage, res: http.ServerResponse): Promise { // Only serve GET requests if (req.method !== 'GET') { return serveError(req, res, 405, `Unsupported method ${req.method}`); @@ -116,14 +116,14 @@ export class RemoteExtensionHostAgentServer extends Disposable implements IServe // Version if (pathname === '/version') { res.writeHead(200, { 'Content-Type': 'text/plain' }); - return res.end(this._productService.commit || ''); + return void res.end(this._productService.commit || ''); } // Delay shutdown if (pathname === '/delay-shutdown') { this._delayShutdown(); res.writeHead(200); - return res.end('OK'); + return void res.end('OK'); } if (!httpRequestHasValidConnectionToken(this._connectionToken, req, parsedUrl)) { @@ -171,7 +171,7 @@ export class RemoteExtensionHostAgentServer extends Disposable implements IServe } res.writeHead(404, { 'Content-Type': 'text/plain' }); - return res.end('Not found'); + return void res.end('Not found'); } public handleUpgrade(req: http.IncomingMessage, socket: net.Socket) { diff --git a/src/vs/server/node/remoteTerminalChannel.ts b/src/vs/server/node/remoteTerminalChannel.ts index 457e31a4451..faa7048e384 100644 --- a/src/vs/server/node/remoteTerminalChannel.ts +++ b/src/vs/server/node/remoteTerminalChannel.ts @@ -147,7 +147,7 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel< case '$refreshProperty': return this._ptyService.refreshProperty.apply(this._ptyService, args); case '$requestDetachInstance': return this._ptyService.requestDetachInstance(args[0], args[1]); case '$acceptDetachedInstance': return this._ptyService.acceptDetachInstanceReply(args[0], args[1]); - case '$freePortKillProcess': return this._ptyService.freePortKillProcess?.apply(args[0], args[1]); + case '$freePortKillProcess': return this._ptyService.freePortKillProcess?.apply(this._ptyService, args); } throw new Error(`IPC Command ${command} not found`); diff --git a/src/vs/server/node/server.cli.ts b/src/vs/server/node/server.cli.ts index d7b1c9b307e..138f6dd8130 100644 --- a/src/vs/server/node/server.cli.ts +++ b/src/vs/server/node/server.cli.ts @@ -94,7 +94,7 @@ export async function main(desc: ProductDescription, args: string[]): Promise = { ...OPTIONS }; + const options: OptionDescriptions> = { ...OPTIONS, gitCredential: { type: 'string' }, openExternal: { type: 'boolean' } }; const isSupported = cliCommand ? isSupportedForCmd : isSupportedForPipe; for (const optionId in OPTIONS) { const optId = optionId; diff --git a/src/vs/server/node/serverEnvironmentService.ts b/src/vs/server/node/serverEnvironmentService.ts index 8c5a3c89cb8..432344f146a 100644 --- a/src/vs/server/node/serverEnvironmentService.ts +++ b/src/vs/server/node/serverEnvironmentService.ts @@ -10,7 +10,7 @@ import { OPTIONS, OptionDescriptions } from 'vs/platform/environment/node/argv'; import { refineServiceDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; -export const serverOptions: OptionDescriptions = { +export const serverOptions: OptionDescriptions> = { /* ----- server setup ----- */ @@ -147,7 +147,7 @@ export interface ServerParsedArgs { 'disable-telemetry'?: boolean; 'file-watcher-polling'?: string; - 'log'?: string; + 'log'?: string[]; 'logsPath'?: string; 'force-disable-user-env'?: boolean; diff --git a/src/vs/server/node/serverServices.ts b/src/vs/server/node/serverServices.ts index 0dc354e378b..c2517797d58 100644 --- a/src/vs/server/node/serverServices.ts +++ b/src/vs/server/node/serverServices.ts @@ -37,9 +37,8 @@ import { InstantiationService } from 'vs/platform/instantiation/common/instantia import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks'; import { NativeLanguagePackService } from 'vs/platform/languagePacks/node/languagePacks'; -import { AbstractLogger, DEFAULT_LOG_LEVEL, getLogLevel, ILogService, LogLevel, LogService, MultiplexLogService } from 'vs/platform/log/common/log'; +import { AbstractLogger, DEFAULT_LOG_LEVEL, getLogLevel, ILogService, LogLevel, MultiplexLogService } from 'vs/platform/log/common/log'; import { LogLevelChannel } from 'vs/platform/log/common/logIpc'; -import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog'; import product from 'vs/platform/product/common/product'; import { IProductService } from 'vs/platform/product/common/productService'; import { RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment'; @@ -73,6 +72,9 @@ import { ExtensionsProfileScannerService, IExtensionsProfileScannerService } fro import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { NullPolicyService } from 'vs/platform/policy/common/policy'; import { OneDataSystemAppender } from 'vs/platform/telemetry/node/1dsAppender'; +import { LoggerService } from 'vs/platform/log/node/loggerService'; +import { URI } from 'vs/base/common/uri'; +import { BufferLogService } from 'vs/platform/log/common/bufferLog'; const eventPrefix = 'monacoworkbench'; @@ -87,15 +89,18 @@ export async function setupServerServices(connectionToken: ServerConnectionToken services.set(IEnvironmentService, environmentService); services.set(INativeEnvironmentService, environmentService); - const spdLogService = new LogService(new SpdLogLogger(RemoteExtensionLogFileName, path.join(environmentService.logsPath, `${RemoteExtensionLogFileName}.log`), true, false, getLogLevel(environmentService))); - const logService = new MultiplexLogService([new ServerLogService(getLogLevel(environmentService)), spdLogService]); + const bufferLogService = new BufferLogService(); + const logService = new MultiplexLogService([new ServerLogService(getLogLevel(environmentService)), bufferLogService]); services.set(ILogService, logService); setTimeout(() => cleanupOlderLogs(environmentService.logsPath).then(null, err => logService.error(err)), 10000); + const loggerService = new LoggerService(logService); + bufferLogService.logger = loggerService.createLogger(URI.file(path.join(environmentService.logsPath, `${RemoteExtensionLogFileName}.log`)), { name: RemoteExtensionLogFileName }); + logService.trace(`Remote configuration data at ${REMOTE_DATA_FOLDER}`); logService.trace('process arguments:', environmentService.args); if (Array.isArray(productService.serverGreeting)) { - spdLogService.info(`\n\n${productService.serverGreeting.join('\n')}\n\n`); + logService.info(`\n\n${productService.serverGreeting.join('\n')}\n\n`); } // ExtensionHost Debug broadcast service @@ -103,7 +108,7 @@ export async function setupServerServices(connectionToken: ServerConnectionToken // TODO: @Sandy @Joao need dynamic context based router const router = new StaticRouter(ctx => ctx.clientId === 'renderer'); - socketServer.registerChannel('logger', new LogLevelChannel(logService)); + socketServer.registerChannel('logger', new LogLevelChannel(logService, loggerService)); // Files const fileService = disposables.add(new FileService(logService)); diff --git a/src/vs/server/node/webClientServer.ts b/src/vs/server/node/webClientServer.ts index 433e12a7b15..35cdf1e9590 100644 --- a/src/vs/server/node/webClientServer.ts +++ b/src/vs/server/node/webClientServer.ts @@ -61,7 +61,7 @@ export async function serveFile(filePath: string, cacheControl: CacheControl, lo const etag = `W/"${[stat.ino, stat.size, stat.mtime.getTime()].join('-')}"`; // weak validator (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) if (req.headers['if-none-match'] === etag) { res.writeHead(304); - return res.end(); + return void res.end(); } responseHeaders['Etag'] = etag; @@ -86,7 +86,7 @@ export async function serveFile(filePath: string, cacheControl: CacheControl, lo } res.writeHead(404, { 'Content-Type': 'text/plain' }); - return res.end('Not found'); + return void res.end('Not found'); } } @@ -232,7 +232,7 @@ export class WebClientServer { setResponseHeader('Content-Type'); res.writeHead(200, responseHeaders); const buffer = await streamToBuffer(context.stream); - return res.end(buffer.buffer); + return void res.end(buffer.buffer); } /** @@ -264,7 +264,7 @@ export class WebClientServer { responseHeaders['Location'] = newLocation; res.writeHead(302, responseHeaders); - return res.end(); + return void res.end(); } const getFirstHeader = (headerName: string) => { @@ -302,7 +302,7 @@ export class WebClientServer { const workbenchWebConfiguration = { remoteAuthority, _wrapWebWorkerExtHostInIframe, - developmentOptions: { enableSmokeTestDriver: this._environmentService.args['enable-smoke-test-driver'] ? true : undefined }, + developmentOptions: { enableSmokeTestDriver: this._environmentService.args['enable-smoke-test-driver'] ? true : undefined, logLevel: this._logService.getLevel() }, settingsSyncOptions: !this._environmentService.isBuilt && this._environmentService.args['enable-sync'] ? { enabled: true } : undefined, enableWorkspaceTrust: !this._environmentService.args['disable-workspace-trust'], folderUri: resolveWorkspaceURI(this._environmentService.args['default-folder']), @@ -336,7 +336,7 @@ export class WebClientServer { data = workbenchTemplate.replace(/\{\{([^}]+)\}\}/g, (_, key) => values[key] ?? 'undefined'); } catch (e) { res.writeHead(404, { 'Content-Type': 'text/plain' }); - return res.end('Not found'); + return void res.end('Not found'); } const cspDirectives = [ @@ -372,7 +372,7 @@ export class WebClientServer { } res.writeHead(200, headers); - return res.end(data); + return void res.end(data); } private _getScriptCspHashes(content: string): string[] { @@ -413,6 +413,6 @@ export class WebClientServer { 'Content-Type': 'text/html', 'Content-Security-Policy': cspDirectives }); - return res.end(data); + return void res.end(data); } } diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts index 203f680485c..38a27b2cea4 100644 --- a/src/vs/workbench/api/browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts @@ -90,4 +90,4 @@ export class ExtensionPoints implements IWorkbenchContribution { } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ExtensionPoints, 'ExtensionPoints', LifecyclePhase.Starting); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ExtensionPoints, LifecyclePhase.Starting); diff --git a/src/vs/workbench/api/browser/mainThreadBulkEdits.ts b/src/vs/workbench/api/browser/mainThreadBulkEdits.ts index dabd351f2d8..0c59eb78b5a 100644 --- a/src/vs/workbench/api/browser/mainThreadBulkEdits.ts +++ b/src/vs/workbench/api/browser/mainThreadBulkEdits.ts @@ -27,7 +27,7 @@ export class MainThreadBulkEdits implements MainThreadBulkEditsShape { $tryApplyWorkspaceEdit(dto: IWorkspaceEditDto, undoRedoGroupId?: number, isRefactoring?: boolean): Promise { const edits = reviveWorkspaceEditDto(dto, this._uriIdentService); - return this._bulkEditService.apply(edits, { undoRedoGroupId, respectAutoSaveConfig: isRefactoring }).then(() => true, err => { + return this._bulkEditService.apply(edits, { undoRedoGroupId, respectAutoSaveConfig: isRefactoring }).then((res) => res.isApplied, err => { this._logService.warn(`IGNORING workspace edit: ${err}`); return false; }); diff --git a/src/vs/workbench/api/browser/mainThreadCommands.ts b/src/vs/workbench/api/browser/mainThreadCommands.ts index 0321d2202f5..175c1faa6fa 100644 --- a/src/vs/workbench/api/browser/mainThreadCommands.ts +++ b/src/vs/workbench/api/browser/mainThreadCommands.ts @@ -3,19 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ICommandService, CommandsRegistry, ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { ExtHostContext, MainThreadCommandsShape, ExtHostCommandsShape, MainContext } from '../common/extHost.protocol'; -import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; +import { DisposableMap, IDisposable } from 'vs/base/common/lifecycle'; import { revive } from 'vs/base/common/marshalling'; +import { CommandsRegistry, ICommandHandlerDescription, ICommandService } from 'vs/platform/commands/common/commands'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { SerializableObjectWithBuffers, Dto } from 'vs/workbench/services/extensions/common/proxyIdentifier'; +import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; +import { Dto, SerializableObjectWithBuffers } from 'vs/workbench/services/extensions/common/proxyIdentifier'; +import { ExtHostCommandsShape, ExtHostContext, MainContext, MainThreadCommandsShape } from '../common/extHost.protocol'; @extHostNamedCustomer(MainContext.MainThreadCommands) export class MainThreadCommands implements MainThreadCommandsShape { - private readonly _commandRegistrations = new Map(); + private readonly _commandRegistrations = new DisposableMap(); private readonly _generateCommandsDocumentationRegistration: IDisposable; private readonly _proxy: ExtHostCommandsShape; @@ -30,9 +30,7 @@ export class MainThreadCommands implements MainThreadCommandsShape { } dispose() { - dispose(this._commandRegistrations.values()); - this._commandRegistrations.clear(); - + this._commandRegistrations.dispose(); this._generateCommandsDocumentationRegistration.dispose(); } @@ -67,11 +65,7 @@ export class MainThreadCommands implements MainThreadCommandsShape { } $unregisterCommand(id: string): void { - const command = this._commandRegistrations.get(id); - if (command) { - command.dispose(); - this._commandRegistrations.delete(id); - } + this._commandRegistrations.deleteAndDispose(id); } $fireCommandActivationEvent(id: string): void { diff --git a/src/vs/workbench/api/browser/mainThreadComments.ts b/src/vs/workbench/api/browser/mainThreadComments.ts index fa493d3fbb5..77fc0e85e72 100644 --- a/src/vs/workbench/api/browser/mainThreadComments.ts +++ b/src/vs/workbench/api/browser/mainThreadComments.ts @@ -5,7 +5,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import { IRange, Range } from 'vs/editor/common/core/range'; @@ -465,8 +465,7 @@ const commentsViewIcon = registerIcon('comments-view-icon', Codicon.commentDiscu @extHostNamedCustomer(MainContext.MainThreadComments) export class MainThreadComments extends Disposable implements MainThreadCommentsShape { private readonly _proxy: ExtHostCommentsShape; - private _documentProviders = new Map(); - private _workspaceProviders = new Map(); + private _handlers = new Map(); private _commentControllers = new Map(); @@ -671,11 +670,4 @@ export class MainThreadComments extends Disposable implements MainThreadComments } return this._handlers.get(handle)!; } - override dispose(): void { - super.dispose(); - this._workspaceProviders.forEach(value => dispose(value)); - this._workspaceProviders.clear(); - this._documentProviders.forEach(value => dispose(value)); - this._documentProviders.clear(); - } } diff --git a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts index 3be47943f1a..b549d50c217 100644 --- a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts @@ -9,7 +9,7 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { CancellationToken } from 'vs/base/common/cancellation'; import { isCancellationError, onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, DisposableStore, dispose, IDisposable, IReference } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableMap, DisposableStore, IReference } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { basename } from 'vs/base/common/path'; import { isEqual, isEqualOrParent, toLocalResource } from 'vs/base/common/resources'; @@ -24,7 +24,6 @@ import { MainThreadWebviewPanels } from 'vs/workbench/api/browser/mainThreadWebv import { MainThreadWebviews, reviveWebviewExtension } from 'vs/workbench/api/browser/mainThreadWebviews'; import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; import { IRevertOptions, ISaveOptions } from 'vs/workbench/common/editor'; -import { editorGroupToColumn } from 'vs/workbench/services/editor/common/editorGroupColumn'; import { CustomEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; import { CustomDocumentBackupData } from 'vs/workbench/contrib/customEditor/browser/customEditorInputFactory'; import { ICustomEditorModel, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; @@ -32,15 +31,16 @@ import { CustomTextEditorModel } from 'vs/workbench/contrib/customEditor/common/ import { WebviewExtensionDescription } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewInput } from 'vs/workbench/contrib/webviewPanel/browser/webviewEditorInput'; import { IWebviewWorkbenchService } from 'vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService'; +import { editorGroupToColumn } from 'vs/workbench/services/editor/common/editorGroupColumn'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; +import { ResourceWorkingCopy } from 'vs/workbench/services/workingCopy/common/resourceWorkingCopy'; +import { IWorkingCopy, IWorkingCopyBackup, IWorkingCopySaveEvent, NO_TYPE_ID, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopy'; import { IWorkingCopyFileService, WorkingCopyFileEvent } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; -import { IWorkingCopy, IWorkingCopyBackup, IWorkingCopySaveEvent, NO_TYPE_ID, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopy'; -import { ResourceWorkingCopy } from 'vs/workbench/services/workingCopy/common/resourceWorkingCopy'; -import { IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; const enum CustomEditorModelType { Custom, @@ -51,7 +51,7 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc private readonly _proxyCustomEditors: extHostProtocol.ExtHostCustomEditorsShape; - private readonly _editorProviders = new Map(); + private readonly _editorProviders = this._register(new DisposableMap()); private readonly _editorRenameBackups = new Map(); @@ -99,13 +99,6 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc this._register(workingCopyFileService.onWillRunWorkingCopyFileOperation(async e => this.onWillRunWorkingCopyFileOperation(e))); } - override dispose() { - super.dispose(); - - dispose(this._editorProviders.values()); - this._editorProviders.clear(); - } - public $registerTextEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: extHostProtocol.IWebviewPanelOptions, capabilities: extHostProtocol.CustomTextEditorCapabilities, serializeBuffersForPostMessage: boolean): void { this.registerEditorProvider(CustomEditorModelType.Text, reviveWebviewExtension(extensionData), viewType, options, capabilities, true, serializeBuffersForPostMessage); } @@ -195,8 +188,8 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc try { await this._proxyCustomEditors.$resolveWebviewEditor(resource, handle, viewType, { title: webviewInput.getTitle(), - webviewOptions: webviewInput.webview.contentOptions, - panelOptions: webviewInput.webview.options, + contentOptions: webviewInput.webview.contentOptions, + options: webviewInput.webview.options, }, editorGroupToColumn(this._editorGroupService, webviewInput.group || 0), cancellation); } catch (error) { onUnexpectedError(error); @@ -211,13 +204,11 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc } public $unregisterEditorProvider(viewType: string): void { - const provider = this._editorProviders.get(viewType); - if (!provider) { + if (!this._editorProviders.has(viewType)) { throw new Error(`No provider for ${viewType} registered`); } - provider.dispose(); - this._editorProviders.delete(viewType); + this._editorProviders.deleteAndDispose(viewType); this._customEditorService.models.disposeAllModelsForView(viewType); } diff --git a/src/vs/workbench/api/browser/mainThreadDebugService.ts b/src/vs/workbench/api/browser/mainThreadDebugService.ts index a9ee9c85cfb..a459965d108 100644 --- a/src/vs/workbench/api/browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/browser/mainThreadDebugService.ts @@ -230,9 +230,12 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb lifecycleManagedByParent: options.lifecycleManagedByParent, repl: options.repl, compact: options.compact, - debugUI: options.debugUI, compoundRoot: parentSession?.compoundRoot, - saveBeforeRestart: saveBeforeStart + saveBeforeRestart: saveBeforeStart, + + suppressDebugStatusbar: options.suppressDebugStatusbar, + suppressDebugToolbar: options.suppressDebugToolbar, + suppressDebugView: options.suppressDebugView, }; try { return this.debugService.startDebugging(launch, nameOrConfig, debugOptions, saveBeforeStart); diff --git a/src/vs/workbench/api/browser/mainThreadDocumentContentProviders.ts b/src/vs/workbench/api/browser/mainThreadDocumentContentProviders.ts index 47e1f32ed47..a29f2f0e245 100644 --- a/src/vs/workbench/api/browser/mainThreadDocumentContentProviders.ts +++ b/src/vs/workbench/api/browser/mainThreadDocumentContentProviders.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { onUnexpectedError } from 'vs/base/common/errors'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { dispose, DisposableMap } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Range } from 'vs/editor/common/core/range'; @@ -20,7 +20,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; @extHostNamedCustomer(MainContext.MainThreadDocumentContentProviders) export class MainThreadDocumentContentProviders implements MainThreadDocumentContentProvidersShape { - private readonly _resourceContentProvider = new Map(); + private readonly _resourceContentProvider = new DisposableMap(); private readonly _pendingUpdate = new Map(); private readonly _proxy: ExtHostDocumentContentProvidersShape; @@ -35,7 +35,7 @@ export class MainThreadDocumentContentProviders implements MainThreadDocumentCon } dispose(): void { - dispose(this._resourceContentProvider.values()); + this._resourceContentProvider.dispose(); dispose(this._pendingUpdate.values()); } @@ -56,11 +56,7 @@ export class MainThreadDocumentContentProviders implements MainThreadDocumentCon } $unregisterTextContentProvider(handle: number): void { - const registration = this._resourceContentProvider.get(handle); - if (registration) { - registration.dispose(); - this._resourceContentProvider.delete(handle); - } + this._resourceContentProvider.deleteAndDispose(handle); } $onVirtualDocumentChange(uri: UriComponents, value: string): void { diff --git a/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts b/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts index dbc22090414..dc20f23f976 100644 --- a/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; -import { IDisposable, combinedDisposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { combinedDisposable, DisposableStore, DisposableMap } from 'vs/base/common/lifecycle'; import { ICodeEditor, isCodeEditor, isDiffEditor, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IEditor } from 'vs/editor/common/editorCommon'; @@ -113,7 +113,7 @@ const enum ActiveEditorOrder { class MainThreadDocumentAndEditorStateComputer { private readonly _toDispose = new DisposableStore(); - private _toDisposeOnEditorRemove = new Map(); + private readonly _toDisposeOnEditorRemove = new DisposableMap(); private _currentState?: DocumentAndEditorState; private _activeEditorOrder: ActiveEditorOrder = ActiveEditorOrder.Editor; @@ -141,6 +141,7 @@ class MainThreadDocumentAndEditorStateComputer { dispose(): void { this._toDispose.dispose(); + this._toDisposeOnEditorRemove.dispose(); } private _onDidAddEditor(e: ICodeEditor): void { @@ -153,10 +154,9 @@ class MainThreadDocumentAndEditorStateComputer { } private _onDidRemoveEditor(e: ICodeEditor): void { - const sub = this._toDisposeOnEditorRemove.get(e.getId()); - if (sub) { - this._toDisposeOnEditorRemove.delete(e.getId()); - sub.dispose(); + const id = e.getId(); + if (this._toDisposeOnEditorRemove.has(id)) { + this._toDisposeOnEditorRemove.deleteAndDispose(id); this._updateState(); } } diff --git a/src/vs/workbench/api/browser/mainThreadEditorTabs.ts b/src/vs/workbench/api/browser/mainThreadEditorTabs.ts index bda93934177..09efeab8cde 100644 --- a/src/vs/workbench/api/browser/mainThreadEditorTabs.ts +++ b/src/vs/workbench/api/browser/mainThreadEditorTabs.ts @@ -194,7 +194,7 @@ export class MainThreadEditorTabs implements MainThreadEditorTabsShape { private _generateTabId(editor: EditorInput, groupId: number) { let resourceString: string | undefined; // Properly get the resource and account for side by side editors - const resource = EditorResourceAccessor.getOriginalUri(editor, { supportSideBySide: SideBySideEditor.BOTH }); + const resource = EditorResourceAccessor.getCanonicalUri(editor, { supportSideBySide: SideBySideEditor.BOTH }); if (resource instanceof URI) { resourceString = resource.toString(); } else { diff --git a/src/vs/workbench/api/browser/mainThreadFileSystem.ts b/src/vs/workbench/api/browser/mainThreadFileSystem.ts index 02e75eae05e..93f1ec92983 100644 --- a/src/vs/workbench/api/browser/mainThreadFileSystem.ts +++ b/src/vs/workbench/api/browser/mainThreadFileSystem.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter, Event } from 'vs/base/common/event'; -import { IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { IDisposable, toDisposable, DisposableStore, DisposableMap } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IFileWriteOptions, FileSystemProviderCapabilities, IFileChange, IFileService, IStat, IWatchOptions, FileType, IFileOverwriteOptions, IFileDeleteOptions, IFileOpenOptions, FileOperationError, FileOperationResult, FileSystemProviderErrorCode, IFileSystemProviderWithOpenReadWriteCloseCapability, IFileSystemProviderWithFileReadWriteCapability, IFileSystemProviderWithFileFolderCopyCapability, FilePermission, toFileSystemProviderErrorCode, IFilesConfiguration, IFileStatWithPartialMetadata, IFileStat } from 'vs/platform/files/common/files'; import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; @@ -22,9 +22,9 @@ import { rtrim } from 'vs/base/common/strings'; export class MainThreadFileSystem implements MainThreadFileSystemShape { private readonly _proxy: ExtHostFileSystemShape; - private readonly _fileProvider = new Map(); + private readonly _fileProvider = new DisposableMap(); private readonly _disposables = new DisposableStore(); - private readonly _watches = new Map(); + private readonly _watches = new DisposableMap(); constructor( extHostContext: IExtHostContext, @@ -46,9 +46,8 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape { dispose(): void { this._disposables.dispose(); - dispose(this._fileProvider.values()); - dispose(this._watches.values()); - this._fileProvider.clear(); + this._fileProvider.dispose(); + this._watches.dispose(); } async $registerFileSystemProvider(handle: number, scheme: string, capabilities: FileSystemProviderCapabilities): Promise { @@ -56,8 +55,7 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape { } $unregisterProvider(handle: number): void { - this._fileProvider.get(handle)?.dispose(); - this._fileProvider.delete(handle); + this._fileProvider.deleteAndDispose(handle); } $onFileSystemChange(handle: number, changes: IFileChangeDto[]): void { @@ -254,12 +252,9 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape { } $unwatch(session: number): void { - const subscription = this._watches.get(session); - if (subscription) { + if (this._watches.has(session)) { this._logService.trace(`MainThreadFileSystem#$unwatch(): request to stop watching (session: ${session})`); - - subscription.dispose(); - this._watches.delete(session); + this._watches.deleteAndDispose(session); } } } diff --git a/src/vs/workbench/api/browser/mainThreadLabelService.ts b/src/vs/workbench/api/browser/mainThreadLabelService.ts index b284ef951c4..8ade55d44e5 100644 --- a/src/vs/workbench/api/browser/mainThreadLabelService.ts +++ b/src/vs/workbench/api/browser/mainThreadLabelService.ts @@ -3,20 +3,22 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Disposable, DisposableMap } from 'vs/base/common/lifecycle'; +import { ILabelService, ResourceLabelFormatter } from 'vs/platform/label/common/label'; import { MainContext, MainThreadLabelServiceShape } from 'vs/workbench/api/common/extHost.protocol'; import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; -import { ResourceLabelFormatter, ILabelService } from 'vs/platform/label/common/label'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; @extHostNamedCustomer(MainContext.MainThreadLabelService) -export class MainThreadLabelService implements MainThreadLabelServiceShape { +export class MainThreadLabelService extends Disposable implements MainThreadLabelServiceShape { - private readonly _resourceLabelFormatters = new Map(); + private readonly _resourceLabelFormatters = this._register(new DisposableMap()); constructor( _: IExtHostContext, @ILabelService private readonly _labelService: ILabelService - ) { } + ) { + super(); + } $registerResourceLabelFormatter(handle: number, formatter: ResourceLabelFormatter): void { // Dynamicily registered formatters should have priority over those contributed via package.json @@ -26,11 +28,6 @@ export class MainThreadLabelService implements MainThreadLabelServiceShape { } $unregisterResourceLabelFormatter(handle: number): void { - dispose(this._resourceLabelFormatters.get(handle)); - this._resourceLabelFormatters.delete(handle); - } - - dispose(): void { - // noop + this._resourceLabelFormatters.deleteAndDispose(handle); } } diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 4fc05b82242..e432965c8db 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -8,7 +8,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { createStringDataTransferItem, VSDataTransfer } from 'vs/base/common/dataTransfer'; import { CancellationError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { combinedDisposable, Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { combinedDisposable, Disposable, DisposableMap, toDisposable } from 'vs/base/common/lifecycle'; import { revive } from 'vs/base/common/marshalling'; import { mixin } from 'vs/base/common/objects'; import { URI } from 'vs/base/common/uri'; @@ -38,7 +38,7 @@ import { ExtHostContext, ExtHostLanguageFeaturesShape, ICallHierarchyItemDto, IC export class MainThreadLanguageFeatures extends Disposable implements MainThreadLanguageFeaturesShape { private readonly _proxy: ExtHostLanguageFeaturesShape; - private readonly _registrations = new Map(); + private readonly _registrations = this._register(new DisposableMap()); constructor( extHostContext: IExtHostContext, @@ -80,21 +80,8 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread } } - override dispose(): void { - for (const registration of this._registrations.values()) { - registration.dispose(); - } - this._registrations.clear(); - - super.dispose(); - } - $unregister(handle: number): void { - const registration = this._registrations.get(handle); - if (registration) { - registration.dispose(); - this._registrations.delete(handle); - } + this._registrations.deleteAndDispose(handle); } //#region --- revive functions diff --git a/src/vs/workbench/api/browser/mainThreadLanguages.ts b/src/vs/workbench/api/browser/mainThreadLanguages.ts index fffdbc7f4db..c532913bccd 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguages.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguages.ts @@ -13,7 +13,7 @@ import { IRange, Range } from 'vs/editor/common/core/range'; import { StandardTokenType } from 'vs/editor/common/encodedTokenAttributes'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { ILanguageStatus, ILanguageStatusService } from 'vs/workbench/services/languageStatus/common/languageStatusService'; -import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableMap, DisposableStore } from 'vs/base/common/lifecycle'; @extHostNamedCustomer(MainContext.MainThreadLanguages) export class MainThreadLanguages implements MainThreadLanguagesShape { @@ -21,7 +21,7 @@ export class MainThreadLanguages implements MainThreadLanguagesShape { private readonly _disposables = new DisposableStore(); private readonly _proxy: ExtHostLanguagesShape; - private readonly _status = new Map(); + private readonly _status = new DisposableMap(); constructor( _extHostContext: IExtHostContext, @@ -40,11 +40,7 @@ export class MainThreadLanguages implements MainThreadLanguagesShape { dispose(): void { this._disposables.dispose(); - - for (const status of this._status.values()) { - status.dispose(); - } - this._status.clear(); + this._status.dispose(); } async $changeLanguage(resource: UriComponents, languageId: string): Promise { diff --git a/src/vs/workbench/api/browser/mainThreadLocalization.ts b/src/vs/workbench/api/browser/mainThreadLocalization.ts index 988fb8ba6c3..cc532e404ea 100644 --- a/src/vs/workbench/api/browser/mainThreadLocalization.ts +++ b/src/vs/workbench/api/browser/mainThreadLocalization.ts @@ -8,6 +8,7 @@ import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/ext import { URI, UriComponents } from 'vs/base/common/uri'; import { IFileService } from 'vs/platform/files/common/files'; import { Disposable } from 'vs/base/common/lifecycle'; +import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks'; @extHostNamedCustomer(MainContext.MainThreadLocalization) export class MainThreadLocalization extends Disposable implements MainThreadLocalizationShape { @@ -15,10 +16,20 @@ export class MainThreadLocalization extends Disposable implements MainThreadLoca constructor( extHostContext: IExtHostContext, @IFileService private readonly fileService: IFileService, + @ILanguagePackService private readonly languagePackService: ILanguagePackService ) { super(); } + async $fetchBuiltInBundleUri(id: string): Promise { + try { + const uri = await this.languagePackService.getBuiltInExtensionTranslationsUri(id); + return uri; + } catch (e) { + return undefined; + } + } + async $fetchBundleContents(uriComponents: UriComponents): Promise { const contents = await this.fileService.readFile(URI.revive(uriComponents)); return contents.value.toString(); diff --git a/src/vs/workbench/api/browser/mainThreadLogService.ts b/src/vs/workbench/api/browser/mainThreadLogService.ts index d48722ba5cf..3d350071c5f 100644 --- a/src/vs/workbench/api/browser/mainThreadLogService.ts +++ b/src/vs/workbench/api/browser/mainThreadLogService.ts @@ -5,29 +5,41 @@ import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; import { ILoggerOptions, ILoggerService, ILogService, log, LogLevel } from 'vs/platform/log/common/log'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { ExtHostContext, MainThreadLoggerShape, MainContext } from 'vs/workbench/api/common/extHost.protocol'; import { UriComponents, URI } from 'vs/base/common/uri'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ILogLevelService } from 'vs/workbench/contrib/logs/common/logLevelService'; +import { IOutputService } from 'vs/workbench/services/output/common/output'; +import { localExtHostLog, remoteExtHostLog, webWorkerExtHostLog } from 'vs/workbench/services/extensions/common/extensions'; @extHostNamedCustomer(MainContext.MainThreadLogger) export class MainThreadLoggerService implements MainThreadLoggerShape { - private readonly _logListener: IDisposable; + private readonly disposables = new DisposableStore(); constructor( extHostContext: IExtHostContext, @ILogService logService: ILogService, - @ILoggerService private readonly _loggerService: ILoggerService, + @ILoggerService private readonly loggerService: ILoggerService, + @ILogLevelService extensionLoggerService: ILogLevelService, + @IOutputService outputService: IOutputService, ) { const proxy = extHostContext.getProxy(ExtHostContext.ExtHostLogLevelServiceShape); - this._logListener = logService.onDidChangeLogLevel(level => proxy.$setLevel(level)); + this.disposables.add(logService.onDidChangeLogLevel(level => proxy.$setLevel(level))); + this.disposables.add(extensionLoggerService.onDidChangeLogLevel(({ id, logLevel }) => { + const channel = outputService.getChannelDescriptor(id); + const resource = channel?.log ? channel.file : undefined; + if (resource && (channel?.extensionId || id === localExtHostLog || id === remoteExtHostLog || id === webWorkerExtHostLog)) { + proxy.$setLevel(logLevel, resource); + } + })); } $log(file: UriComponents, messages: [LogLevel, string][]): void { - const logger = this._loggerService.getLogger(URI.revive(file)); + const logger = this.loggerService.getLogger(URI.revive(file)); if (!logger) { throw new Error('Create the logger before logging'); } @@ -37,11 +49,11 @@ export class MainThreadLoggerService implements MainThreadLoggerShape { } async $createLogger(file: UriComponents, options?: ILoggerOptions): Promise { - this._loggerService.createLogger(URI.revive(file), options); + this.loggerService.createLogger(URI.revive(file), options); } dispose(): void { - this._logListener.dispose(); + this.disposables.dispose(); } } diff --git a/src/vs/workbench/api/browser/mainThreadNotebookDocumentsAndEditors.ts b/src/vs/workbench/api/browser/mainThreadNotebookDocumentsAndEditors.ts index 1c649d6f669..4207532513f 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookDocumentsAndEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookDocumentsAndEditors.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { diffMaps, diffSets } from 'vs/base/common/collections'; -import { combinedDisposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { combinedDisposable, DisposableStore, DisposableMap } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { MainThreadNotebookDocuments } from 'vs/workbench/api/browser/mainThreadNotebookDocuments'; @@ -85,7 +85,7 @@ export class MainThreadNotebooksAndEditors { private readonly _proxy: Pick; private readonly _disposables = new DisposableStore(); - private readonly _editorListeners = new Map(); + private readonly _editorListeners = new DisposableMap(); private _currentState?: NotebookAndEditorState; @@ -121,6 +121,7 @@ export class MainThreadNotebooksAndEditors { this._mainThreadNotebooks.dispose(); this._mainThreadEditors.dispose(); this._disposables.dispose(); + this._editorListeners.dispose(); } private _handleEditorAdd(editor: INotebookEditor): void { @@ -132,8 +133,7 @@ export class MainThreadNotebooksAndEditors { } private _handleEditorRemove(editor: INotebookEditor): void { - this._editorListeners.get(editor.getId())?.dispose(); - this._editorListeners.delete(editor.getId()); + this._editorListeners.deleteAndDispose(editor.getId()); this._updateState(); } diff --git a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts index 30702b46cd8..e975e931400 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts @@ -6,7 +6,7 @@ import { isNonEmptyArray } from 'vs/base/common/arrays'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { combinedDisposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { combinedDisposable, DisposableMap, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; @@ -104,7 +104,7 @@ abstract class MainThreadKernel implements INotebookKernel { @extHostNamedCustomer(MainContext.MainThreadNotebookKernels) export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape { - private readonly _editors = new Map(); + private readonly _editors = new DisposableMap(); private readonly _disposables = new DisposableStore(); private readonly _kernels = new Map(); @@ -143,6 +143,7 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape for (const [, registration] of this._kernels.values()) { registration.dispose(); } + this._editors.dispose(); } // --- kernel ipc @@ -168,8 +169,7 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape } private _onEditorRemove(editor: INotebookEditor) { - this._editors.get(editor)?.dispose(); - this._editors.delete(editor); + this._editors.deleteAndDispose(editor); } async $postMessage(handle: number, editorId: string | undefined, message: any): Promise { diff --git a/src/vs/workbench/api/browser/mainThreadTask.ts b/src/vs/workbench/api/browser/mainThreadTask.ts index be081d8e61f..18163ed9261 100644 --- a/src/vs/workbench/api/browser/mainThreadTask.ts +++ b/src/vs/workbench/api/browser/mainThreadTask.ts @@ -33,6 +33,7 @@ import { } from 'vs/workbench/api/common/shared/tasks'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { ErrorNoTelemetry } from 'vs/base/common/errors'; namespace TaskExecutionDTO { export function from(value: ITaskExecution): ITaskExecutionDTO { @@ -649,7 +650,7 @@ export class MainThreadTask implements MainThreadTaskShape { return; } } - reject(new Error('Task to terminate not found')); + reject(new ErrorNoTelemetry('Task to terminate not found')); }); }); } diff --git a/src/vs/workbench/api/browser/mainThreadTelemetry.ts b/src/vs/workbench/api/browser/mainThreadTelemetry.ts index acb2067b0c8..1d5ba159d83 100644 --- a/src/vs/workbench/api/browser/mainThreadTelemetry.ts +++ b/src/vs/workbench/api/browser/mainThreadTelemetry.ts @@ -5,7 +5,6 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { ILogger, ILoggerService } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; import { ClassifiedEvent, IGDPRProperty, OmitMetadata, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings'; import { ITelemetryService, TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; @@ -19,26 +18,14 @@ export class MainThreadTelemetry extends Disposable implements MainThreadTelemet private static readonly _name = 'pluginHostTelemetry'; - private readonly _extensionTelemetryLog: ILogger; - constructor( extHostContext: IExtHostContext, @ITelemetryService private readonly _telemetryService: ITelemetryService, @IEnvironmentService private readonly _environmentService: IEnvironmentService, @IProductService private readonly _productService: IProductService, - @ILoggerService loggerService: ILoggerService, ) { super(); - const logger = loggerService.getLogger(this._environmentService.extensionTelemetryLogResource); - if (logger) { - this._extensionTelemetryLog = this._register(logger); - } else { - this._extensionTelemetryLog = this._register(loggerService.createLogger(this._environmentService.extensionTelemetryLogResource)); - this._extensionTelemetryLog.info('Below are logs for extension telemetry events sent to the telemetry output channel API once the log level is set to trace.'); - this._extensionTelemetryLog.info('==========================================================='); - } - this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTelemetry); if (supportsTelemetry(this._productService, this._environmentService)) { @@ -67,11 +54,6 @@ export class MainThreadTelemetry extends Disposable implements MainThreadTelemet $publicLog2> = never, T extends IGDPRProperty = never>(eventName: string, data?: StrictPropertyCheck): void { this.$publicLog(eventName, data as any); } - - $logTelemetryToOutputChannel(eventName: string, data: Record) { - this._extensionTelemetryLog.trace(eventName, data); - this._extensionTelemetryLog.flush(); - } } diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index aa6848ff88a..d529c525786 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -170,9 +170,9 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape if (terminalInstance) { this._terminalService.setActiveInstance(terminalInstance); if (terminalInstance.target === TerminalLocation.Editor) { - this._terminalEditorService.revealActiveEditor(preserveFocus); + await this._terminalEditorService.revealActiveEditor(preserveFocus); } else { - this._terminalGroupService.showPanel(!preserveFocus); + await this._terminalGroupService.showPanel(!preserveFocus); } } } diff --git a/src/vs/workbench/api/browser/mainThreadTesting.ts b/src/vs/workbench/api/browser/mainThreadTesting.ts index e21387242da..db044029567 100644 --- a/src/vs/workbench/api/browser/mainThreadTesting.ts +++ b/src/vs/workbench/api/browser/mainThreadTesting.ts @@ -171,6 +171,7 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh id: controllerId, label, canRefresh, + syncTests: () => this.proxy.$syncTests(), refreshTests: token => this.proxy.$refreshTests(controllerId, token), configureRunProfile: id => this.proxy.$configureRunProfile(controllerId, id), runTests: (reqs, token) => this.proxy.$runControllerTests(reqs, token), diff --git a/src/vs/workbench/api/browser/mainThreadTunnelService.ts b/src/vs/workbench/api/browser/mainThreadTunnelService.ts index c43a9e82756..ea4f8198698 100644 --- a/src/vs/workbench/api/browser/mainThreadTunnelService.ts +++ b/src/vs/workbench/api/browser/mainThreadTunnelService.ts @@ -8,7 +8,7 @@ import { MainThreadTunnelServiceShape, MainContext, ExtHostContext, ExtHostTunne import { TunnelDtoConverter } from 'vs/workbench/api/common/extHostTunnelService'; import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; import { CandidatePort, IRemoteExplorerService, makeAddress, PORT_AUTO_FORWARD_SETTING, PORT_AUTO_SOURCE_SETTING, PORT_AUTO_SOURCE_SETTING_OUTPUT, PORT_AUTO_SOURCE_SETTING_PROCESS, TunnelSource } from 'vs/workbench/services/remote/common/remoteExplorerService'; -import { ITunnelProvider, ITunnelService, TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions, RemoteTunnel, isPortPrivileged, ProvidedPortAttributes, PortAttributesProvider, TunnelProtocol } from 'vs/platform/tunnel/common/tunnel'; +import { ITunnelProvider, ITunnelService, TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions, RemoteTunnel, ProvidedPortAttributes, PortAttributesProvider, TunnelProtocol } from 'vs/platform/tunnel/common/tunnel'; import { Disposable } from 'vs/base/common/lifecycle'; import type { TunnelDescription } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; @@ -111,7 +111,7 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun if (!this.elevateionRetry && (tunnelOptions.localAddressPort !== undefined) && (tunnel.tunnelLocalPort !== undefined) - && isPortPrivileged(tunnelOptions.localAddressPort) + && this.tunnelService.isPortPrivileged(tunnelOptions.localAddressPort) && (tunnel.tunnelLocalPort !== tunnelOptions.localAddressPort) && this.tunnelService.canElevate) { diff --git a/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts index 79a0765ef3e..9f9af56b2d9 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts @@ -4,18 +4,19 @@ *--------------------------------------------------------------------------------------------*/ import { onUnexpectedError } from 'vs/base/common/errors'; -import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableMap } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { MainThreadWebviews, reviveWebviewContentOptions, reviveWebviewExtension } from 'vs/workbench/api/browser/mainThreadWebviews'; import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; -import { WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; +import { WebviewOptions, WebviewOriginStore } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewInput } from 'vs/workbench/contrib/webviewPanel/browser/webviewEditorInput'; import { WebviewIcons } from 'vs/workbench/contrib/webviewPanel/browser/webviewIconManager'; -import { ICreateWebViewShowOptions, IWebviewWorkbenchService } from 'vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService'; +import { IWebViewShowOptions, IWebviewWorkbenchService } from 'vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService'; import { editorGroupToColumn } from 'vs/workbench/services/editor/common/editorGroupColumn'; import { GroupLocation, GroupsOrder, IEditorGroup, IEditorGroupsService, preferredSideBySideGroupDirection } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ACTIVE_GROUP, IEditorService, PreferredGroup, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; @@ -83,9 +84,9 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc private readonly _webviewInputs = new WebviewInputStore(); - private readonly _editorProviders = new Map(); + private readonly _revivers = this._register(new DisposableMap()); - private readonly _revivers = new Map(); + private readonly webviewOriginStore: WebviewOriginStore; constructor( context: IExtHostContext, @@ -94,11 +95,14 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, @IEditorService private readonly _editorService: IEditorService, @IExtensionService extensionService: IExtensionService, + @IStorageService storageService: IStorageService, @ITelemetryService private readonly _telemetryService: ITelemetryService, @IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService, ) { super(); + this.webviewOriginStore = new WebviewOriginStore('mainThreadWebviewPanel.origins', storageService); + this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewPanels); this._register(_editorService.onDidActiveEditorChange(() => { @@ -127,16 +131,6 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc })); } - override dispose() { - super.dispose(); - - dispose(this._editorProviders.values()); - this._editorProviders.clear(); - - dispose(this._revivers.values()); - this._revivers.clear(); - } - public get webviewInputs(): Iterable { return this._webviewInputs; } public addWebviewInput(handle: extHostProtocol.WebviewHandle, input: WebviewInput, options: { serializeBuffersForPostMessage: boolean }): void { @@ -158,15 +152,17 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc showOptions: extHostProtocol.WebviewPanelShowOptions, ): void { const targetGroup = this.getTargetGroupFromShowOptions(showOptions); - const mainThreadShowOptions: ICreateWebViewShowOptions = showOptions ? { + const mainThreadShowOptions: IWebViewShowOptions = showOptions ? { preserveFocus: !!showOptions.preserveFocus, group: targetGroup } : {}; const extension = reviveWebviewExtension(extensionData); + const origin = this.webviewOriginStore.getOrigin(viewType, extension.id); - const webview = this._webviewWorkbenchService.createWebview({ + const webview = this._webviewWorkbenchService.openWebview({ id: handle, + origin, providedViewType: viewType, options: reviveWebviewOptions(initData.panelOptions), contentOptions: reviveWebviewContentOptions(initData.webviewOptions), @@ -295,13 +291,11 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc } public $unregisterSerializer(viewType: string): void { - const reviver = this._revivers.get(viewType); - if (!reviver) { + if (!this._revivers.has(viewType)) { throw new Error(`No reviver for ${viewType} registered`); } - reviver.dispose(); - this._revivers.delete(viewType); + this._revivers.deleteAndDispose(viewType); } private updateWebviewViewStates(activeEditorInput: EditorInput | undefined) { diff --git a/src/vs/workbench/api/browser/mainThreadWindow.ts b/src/vs/workbench/api/browser/mainThreadWindow.ts index a0ad6dbd0e1..79cf2c48a96 100644 --- a/src/vs/workbench/api/browser/mainThreadWindow.ts +++ b/src/vs/workbench/api/browser/mainThreadWindow.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; -import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; @@ -16,7 +16,6 @@ export class MainThreadWindow implements MainThreadWindowShape { private readonly proxy: ExtHostWindowShape; private readonly disposables = new DisposableStore(); - private readonly resolved = new Map(); constructor( extHostContext: IExtHostContext, @@ -31,11 +30,6 @@ export class MainThreadWindow implements MainThreadWindowShape { dispose(): void { this.disposables.dispose(); - - for (const value of this.resolved.values()) { - value.dispose(); - } - this.resolved.clear(); } $getWindowVisibility(): Promise { diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 62e6a51a57c..87e69afc783 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -162,9 +162,9 @@ const viewDescriptor: IJSONSchema = { localize('vscode.extension.contributes.view.initialState.collapsed', "The view will show in the view container, but will be collapsed.") ] }, - size: { + initialSize: { type: 'number', - description: localize('vscode.extension.contributs.view.size', "The size of the view. Using a number will behave like the css 'flex' property, and the size will set the initial size when the view is first shown. In the side bar, this is the height of the view."), + description: localize('vscode.extension.contributs.view.size', "The initial size of the view. The size will behave like the css 'flex' property, and will set the initial size when the view is first shown. In the side bar, this is the height of the view. This value is only respected when the same extension owns both the view and the view container."), } } }; @@ -642,4 +642,4 @@ class ViewsExtensionHandler implements IWorkbenchContribution { } const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(ViewsExtensionHandler, 'ViewsExtensionHandler', LifecyclePhase.Starting); +workbenchRegistry.registerWorkbenchContribution(ViewsExtensionHandler, LifecyclePhase.Starting); diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 413b1ff5505..f781761bb04 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -59,7 +59,7 @@ import { IExtHostDecorations } from 'vs/workbench/api/common/extHostDecorations' import { IExtHostTask } from 'vs/workbench/api/common/extHostTask'; import { IExtHostDebugService } from 'vs/workbench/api/common/extHostDebugService'; import { IExtHostSearch } from 'vs/workbench/api/common/extHostSearch'; -import { ILoggerService, ILogService } from 'vs/platform/log/common/log'; +import { ILoggerService, ILogService, LogLevel } from 'vs/platform/log/common/log'; import { IURITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; @@ -80,7 +80,7 @@ import { ExtHostTesting } from 'vs/workbench/api/common/extHostTesting'; import { ExtHostUriOpeners } from 'vs/workbench/api/common/extHostUriOpener'; import { IExtHostSecretState } from 'vs/workbench/api/common/extHostSecretState'; import { IExtHostEditorTabs } from 'vs/workbench/api/common/extHostEditorTabs'; -import { IExtHostTelemetry } from 'vs/workbench/api/common/extHostTelemetry'; +import { IExtHostTelemetry, isNewAppInstall } from 'vs/workbench/api/common/extHostTelemetry'; import { ExtHostNotebookKernels } from 'vs/workbench/api/common/extHostNotebookKernels'; import { TextSearchCompleteMessageType } from 'vs/workbench/services/search/common/searchExtTypes'; import { ExtHostNotebookRenderers } from 'vs/workbench/api/common/extHostNotebookRenderers'; @@ -92,7 +92,6 @@ import { ExtHostInteractive } from 'vs/workbench/api/common/extHostInteractive'; import { combinedDisposable } from 'vs/base/common/lifecycle'; import { checkProposedApiEnabled, ExtensionIdentifierSet, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/contrib/debug/common/debug'; -import { IExtHostTelemetryLogService } from 'vs/workbench/api/common/extHostTelemetryLogService'; import { IExtHostLocalizationService } from 'vs/workbench/api/common/extHostLocalizationService'; export interface IExtensionRegistries { @@ -124,7 +123,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostLoggerService = accessor.get(ILoggerService); const extHostLogService = accessor.get(ILogService); const extHostTunnelService = accessor.get(IExtHostTunnelService); - const extHostTelemetryLogService = accessor.get(IExtHostTelemetryLogService); const extHostApiDeprecation = accessor.get(IExtHostApiDeprecationService); const extHostWindow = accessor.get(IExtHostWindow); const extHostSecretState = accessor.get(IExtHostSecretState); @@ -169,7 +167,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostEditorInsets = rpcProtocol.set(ExtHostContext.ExtHostEditorInsets, new ExtHostEditorInsets(rpcProtocol.getProxy(MainContext.MainThreadEditorInsets), extHostEditors, initData.remote)); const extHostDiagnostics = rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(rpcProtocol, extHostLogService, extHostFileSystemInfo)); const extHostLanguages = rpcProtocol.set(ExtHostContext.ExtHostLanguages, new ExtHostLanguages(rpcProtocol, extHostDocuments, extHostCommands.converter, uriTransformer)); - const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, uriTransformer, extHostDocuments, extHostCommands, extHostDiagnostics, extHostLogService, extHostApiDeprecation)); + const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, uriTransformer, extHostDocuments, extHostCommands, extHostDiagnostics, extHostLogService, extHostApiDeprecation, extHostTelemetry)); const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures)); const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpcProtocol, extHostLogService, extHostDocumentsAndEditors)); const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, createExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands)); @@ -330,8 +328,11 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostTelemetry.onDidChangeTelemetryConfiguration; }, get isNewAppInstall() { - const installAge = Date.now() - new Date(initData.telemetryInfo.firstSessionDate).getTime(); - return isNaN(installAge) ? false : installAge < 1000 * 60 * 60 * 24; // install age is less than a day + return isNewAppInstall(initData.telemetryInfo.firstSessionDate); + }, + createTelemetryLogger(appender: vscode.TelemetryAppender): vscode.TelemetryLogger { + checkProposedApiEnabled(extension, 'telemetry'); + return extHostTelemetry.instantiateLogger(extension, appender); }, openExternal(uri: URI, options?: { allowContributedOpeners?: boolean | string }) { return extHostWindow.openUri(uri, { @@ -363,6 +364,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }, get uiKind() { return initData.uiKind; + }, + get logLevel() { + checkProposedApiEnabled(extension, 'extensionLog'); + return extHostLogService.getLevel(); + }, + get onDidChangeLogLevel() { + checkProposedApiEnabled(extension, 'extensionLog'); + return extHostLogService.onDidChangeLogLevel; } }; if (!initData.environment.extensionTestsLocationURI) { @@ -796,10 +805,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }, get tabGroups(): vscode.TabGroups { return extHostEditorTabs.tabGroups; - }, - logTelemetryToOutputChannel(eventName: string, data: Record): void { - checkProposedApiEnabled(extension, 'telemetryLog'); - extHostTelemetryLogService.logToTelemetryOutputChannel(extension, eventName, data); } }; @@ -1010,7 +1015,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostFileSystemEvent.getOnWillRenameFileEvent(extension)(listener, thisArg, disposables); }, openTunnel: (forward: vscode.TunnelOptions) => { - checkProposedApiEnabled(extension, 'resolvers'); + checkProposedApiEnabled(extension, 'tunnels'); return extHostTunnelService.openTunnel(extension, forward).then(value => { if (!value) { throw new Error('cannot open tunnel'); @@ -1019,11 +1024,11 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }); }, get tunnels() { - checkProposedApiEnabled(extension, 'resolvers'); + checkProposedApiEnabled(extension, 'tunnels'); return extHostTunnelService.getTunnels(); }, onDidChangeTunnels: (listener, thisArg?, disposables?) => { - checkProposedApiEnabled(extension, 'resolvers'); + checkProposedApiEnabled(extension, 'tunnels'); return extHostTunnelService.onDidChangeTunnels(listener, thisArg, disposables); }, registerPortAttributesProvider: (portSelector: { pid?: number; portRange?: [number, number]; commandMatcher?: RegExp }, provider: vscode.PortAttributesProvider) => { @@ -1171,22 +1176,22 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I // namespace: l10n const l10n: typeof vscode.l10n = { - t(...params: [message: string, ...args: any[]] | [{ message: string; args?: any[]; comment: string[] }]): string { - checkProposedApiEnabled(extension, 'localization'); - + t(...params: [message: string, ...args: Array] | [message: string, args: Record] | [{ message: string; args?: Array | Record; comment: string | string[] }]): string { if (typeof params[0] === 'string') { const key = params.shift() as string; - return extHostLocalization.getMessage(extension.identifier.value, { message: key, args: params as any[] | undefined }); + + // We have either rest args which are Array or an array with a single Record. + // This ensures we get a Record which will be formatted correctly. + const argsFormatted = !params || typeof params[0] !== 'object' ? params : params[0]; + return extHostLocalization.getMessage(extension.identifier.value, { message: key, args: argsFormatted as Record | undefined }); } return extHostLocalization.getMessage(extension.identifier.value, params[0]); }, get bundle() { - checkProposedApiEnabled(extension, 'localization'); return extHostLocalization.getBundle(extension.identifier.value); }, get uri() { - checkProposedApiEnabled(extension, 'localization'); return extHostLocalization.getBundleUri(extension.identifier.value); } }; @@ -1386,7 +1391,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I TabInputWebview: extHostTypes.WebviewEditorTabInput, TabInputTerminal: extHostTypes.TerminalEditorTabInput, TabInputInteractiveWindow: extHostTypes.InteractiveWindowInput, - TerminalExitReason: extHostTypes.TerminalExitReason + TerminalExitReason: extHostTypes.TerminalExitReason, + LogLevel: LogLevel, }; }; } diff --git a/src/vs/workbench/api/common/extHost.common.services.ts b/src/vs/workbench/api/common/extHost.common.services.ts index 4658a622298..f5e48cd8854 100644 --- a/src/vs/workbench/api/common/extHost.common.services.ts +++ b/src/vs/workbench/api/common/extHost.common.services.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IExtHostOutputService, ExtHostOutputService } from 'vs/workbench/api/common/extHostOutput'; import { IExtHostWorkspace, ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import { IExtHostDecorations, ExtHostDecorations } from 'vs/workbench/api/common/extHostDecorations'; @@ -27,12 +27,11 @@ import { ExtHostLoggerService } from 'vs/workbench/api/common/extHostLoggerServi import { ILoggerService, ILogService } from 'vs/platform/log/common/log'; import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService'; import { ExtHostVariableResolverProviderService, IExtHostVariableResolverProvider } from 'vs/workbench/api/common/extHostVariableResolverService'; -import { ExtHostTelemetryLogService, IExtHostTelemetryLogService } from 'vs/workbench/api/common/extHostTelemetryLogService'; import { ExtHostLocalizationService, IExtHostLocalizationService } from 'vs/workbench/api/common/extHostLocalizationService'; -registerSingleton(IExtHostLocalizationService, ExtHostLocalizationService, true); -registerSingleton(ILoggerService, ExtHostLoggerService, true); -registerSingleton(ILogService, ExtHostLogService, true); +registerSingleton(IExtHostLocalizationService, ExtHostLocalizationService, InstantiationType.Delayed); +registerSingleton(ILoggerService, ExtHostLoggerService, InstantiationType.Delayed); +registerSingleton(ILogService, ExtHostLogService, InstantiationType.Delayed); registerSingleton(IExtHostApiDeprecationService, ExtHostApiDeprecationService, false); registerSingleton(IExtHostCommands, ExtHostCommands, false); registerSingleton(IExtHostConfiguration, ExtHostConfiguration, false); @@ -41,7 +40,7 @@ registerSingleton(IExtHostDebugService, WorkerExtHostDebugService, false); registerSingleton(IExtHostDecorations, ExtHostDecorations, false); registerSingleton(IExtHostDocumentsAndEditors, ExtHostDocumentsAndEditors, false); registerSingleton(IExtHostFileSystemInfo, ExtHostFileSystemInfo, false); -registerSingleton(IExtHostOutputService, ExtHostOutputService, true); +registerSingleton(IExtHostOutputService, ExtHostOutputService, InstantiationType.Delayed); registerSingleton(IExtHostSearch, ExtHostSearch, false); registerSingleton(IExtHostStorage, ExtHostStorage, false); registerSingleton(IExtHostTask, WorkerExtHostTask, false); @@ -51,6 +50,5 @@ registerSingleton(IExtHostWindow, ExtHostWindow, false); registerSingleton(IExtHostWorkspace, ExtHostWorkspace, false); registerSingleton(IExtHostSecretState, ExtHostSecretState, false); registerSingleton(IExtHostTelemetry, ExtHostTelemetry, false); -registerSingleton(IExtHostTelemetryLogService, ExtHostTelemetryLogService, false); registerSingleton(IExtHostEditorTabs, ExtHostEditorTabs, false); registerSingleton(IExtHostVariableResolverProvider, ExtHostVariableResolverProviderService, false); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 5e21c84cd15..adb51a10a0f 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -598,7 +598,6 @@ export interface MainThreadStorageShape extends IDisposable { export interface MainThreadTelemetryShape extends IDisposable { $publicLog(eventName: string, data?: any): void; $publicLog2> = never, T extends IGDPRProperty = never>(eventName: string, data?: StrictPropertyCheck): void; - $logTelemetryToOutputChannel(eventName: string, data: Record): void; } export interface MainThreadEditorInsetsShape extends IDisposable { @@ -903,8 +902,8 @@ export interface ExtHostCustomEditorsShape { viewType: string, initData: { title: string; - webviewOptions: IWebviewContentOptions; - panelOptions: IWebviewPanelOptions; + contentOptions: IWebviewContentOptions; + options: IWebviewPanelOptions; }, position: EditorGroupColumn, cancellation: CancellationToken @@ -1227,9 +1226,9 @@ export interface IStartDebuggingOptions { repl?: IDebugSessionReplMode; noDebug?: boolean; compact?: boolean; - debugUI?: { - simple?: boolean; - }; + suppressDebugToolbar?: boolean; + suppressDebugStatusbar?: boolean; + suppressDebugView?: boolean; suppressSaveBeforeStart?: boolean; } @@ -1945,7 +1944,7 @@ export interface ExtHostWindowShape { } export interface ExtHostLogLevelServiceShape { - $setLevel(level: LogLevel): void; + $setLevel(level: LogLevel, resource?: UriComponents): void; } export interface MainThreadLoggerShape { @@ -2145,6 +2144,7 @@ export interface MainThreadThemingShape extends IDisposable { } export interface MainThreadLocalizationShape extends IDisposable { + $fetchBuiltInBundleUri(id: string): Promise; $fetchBundleContents(uriComponents: UriComponents): Promise; } @@ -2195,19 +2195,21 @@ export interface ExtHostTestingShape { $configureRunProfile(controllerId: string, configId: number): void; /** Asks the controller to refresh its tests */ $refreshTests(controllerId: string, token: CancellationToken): Promise; + /** Ensures any pending test diffs are flushed */ + $syncTests(): Promise; } export interface ExtHostLocalizationShape { getMessage(extensionId: string, details: IStringDetails): string; - getBundle(extensionId: string): { [key: string]: string }; + getBundle(extensionId: string): { [key: string]: string } | undefined; getBundleUri(extensionId: string): URI | undefined; initializeLocalizedMessages(extension: IExtensionDescription): Promise; } export interface IStringDetails { message: string; - args?: any[]; - comment?: string[]; + args?: Record; + comment?: string | string[]; } export interface ITestControllerPatch { diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index 8f3c18a769f..db6bdefe028 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -439,7 +439,7 @@ const newCommands: ApiCommand[] = [ ), // --- continue edit session new ApiCommand( - 'vscode.experimental.editSession.continue', '_workbench.experimental.editSessions.actions.continueEditSession', 'Continue the current edit session in a different workspace', + 'vscode.experimental.editSession.continue', '_workbench.editSessions.actions.continueEditSession', 'Continue the current edit session in a different workspace', [ApiCommandArgument.Uri.with('workspaceUri', 'The target workspace to continue the current edit session in')], ApiCommandResult.Void ) diff --git a/src/vs/workbench/api/common/extHostBulkEdits.ts b/src/vs/workbench/api/common/extHostBulkEdits.ts index 59e0bbc2426..bdbfe2e5ba8 100644 --- a/src/vs/workbench/api/common/extHostBulkEdits.ts +++ b/src/vs/workbench/api/common/extHostBulkEdits.ts @@ -8,7 +8,6 @@ import { MainContext, MainThreadBulkEditsShape } from 'vs/workbench/api/common/e import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { WorkspaceEdit } from 'vs/workbench/api/common/extHostTypeConverters'; -import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import type * as vscode from 'vscode'; export class ExtHostBulkEdits { @@ -29,11 +28,6 @@ export class ExtHostBulkEdits { } applyWorkspaceEdit(edit: vscode.WorkspaceEdit, extension: IExtensionDescription, metadata: vscode.WorkspaceEditMetadata | undefined): Promise { - const allowIsRefactoring = isProposedApiEnabled(extension, 'workspaceEditIsRefactoring'); - if (metadata && !allowIsRefactoring) { - console.warn(`Extension '${extension.identifier.value}' uses a proposed API 'workspaceEditIsRefactoring' which is NOT enabled for it`); - metadata = undefined; - } const dto = WorkspaceEdit.from(edit, this._versionInformationProvider); return this._proxy.$tryApplyWorkspaceEdit(dto, undefined, metadata?.isRefactoring ?? false); } diff --git a/src/vs/workbench/api/common/extHostConfiguration.ts b/src/vs/workbench/api/common/extHostConfiguration.ts index 2e9c5b7ada8..68f363f19f0 100644 --- a/src/vs/workbench/api/common/extHostConfiguration.ts +++ b/src/vs/workbench/api/common/extHostConfiguration.ts @@ -251,22 +251,22 @@ export class ExtHostConfigProvider { }, inspect: (key: string): ConfigurationInspect | undefined => { key = section ? `${section}.${key}` : key; - const config = deepClone(this._configuration.inspect(key, overrides, this._extHostWorkspace.workspace)); + const config = this._configuration.inspect(key, overrides, this._extHostWorkspace.workspace); if (config) { return { key, - defaultValue: config.policy?.value ?? config.default?.value, - globalValue: config.user?.value ?? config.application?.value, - workspaceValue: config.workspace?.value, - workspaceFolderValue: config.workspaceFolder?.value, + defaultValue: deepClone(config.policy?.value ?? config.default?.value), + globalValue: deepClone(config.user?.value ?? config.application?.value), + workspaceValue: deepClone(config.workspace?.value), + workspaceFolderValue: deepClone(config.workspaceFolder?.value), - defaultLanguageValue: config.default?.override, - globalLanguageValue: config.user?.override ?? config.application?.override, - workspaceLanguageValue: config.workspace?.override, - workspaceFolderLanguageValue: config.workspaceFolder?.override, + defaultLanguageValue: deepClone(config.default?.override), + globalLanguageValue: deepClone(config.user?.override ?? config.application?.override), + workspaceLanguageValue: deepClone(config.workspace?.override), + workspaceFolderLanguageValue: deepClone(config.workspaceFolder?.override), - languageIds: config.overrideIdentifiers + languageIds: deepClone(config.overrideIdentifiers) }; } return undefined; diff --git a/src/vs/workbench/api/common/extHostCustomEditors.ts b/src/vs/workbench/api/common/extHostCustomEditors.ts index 24ee34186c0..04aa98ad3a8 100644 --- a/src/vs/workbench/api/common/extHostCustomEditors.ts +++ b/src/vs/workbench/api/common/extHostCustomEditors.ts @@ -112,7 +112,6 @@ class CustomDocumentStore { private key(viewType: string, resource: vscode.Uri): string { return `${viewType}@@@${resource}`; } - } const enum WebviewEditorType { @@ -187,7 +186,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor } else { disposables.add(this._editorProviders.addCustomProvider(viewType, extension, provider)); - if (this.supportEditing(provider)) { + if (isCustomEditorProviderWithEditingCapability(provider)) { disposables.add(provider.onDidChangeCustomDocument(e => { const entry = this.getCustomDocumentEntry(viewType, e.document.uri); if (isEditEvent(e)) { @@ -223,12 +222,12 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor const document = await entry.provider.openCustomDocument(revivedResource, { backupId, untitledDocumentData: untitledDocumentData?.buffer }, cancellation); let storageRoot: URI | undefined; - if (this.supportEditing(entry.provider) && this._extensionStoragePaths) { + if (isCustomEditorProviderWithEditingCapability(entry.provider) && this._extensionStoragePaths) { storageRoot = this._extensionStoragePaths.workspaceValue(entry.extension) ?? this._extensionStoragePaths.globalValue(entry.extension); } this._documents.add(viewType, document, storageRoot); - return { editable: this.supportEditing(entry.provider) }; + return { editable: isCustomEditorProviderWithEditingCapability(entry.provider) }; } async $disposeCustomDocument(resource: UriComponents, viewType: string): Promise { @@ -253,8 +252,8 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor viewType: string, initData: { title: string; - webviewOptions: extHostProtocol.IWebviewContentOptions; - panelOptions: extHostProtocol.IWebviewPanelOptions; + contentOptions: extHostProtocol.IWebviewContentOptions; + options: extHostProtocol.IWebviewPanelOptions; }, position: EditorGroupColumn, cancellation: CancellationToken, @@ -266,26 +265,23 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor const viewColumn = typeConverters.ViewColumn.to(position); - const webview = this._extHostWebview.createNewWebview(handle, initData.webviewOptions, entry.extension); - const panel = this._extHostWebviewPanels.createNewWebviewPanel(handle, viewType, initData.title, viewColumn, initData.panelOptions, webview, true); + const webview = this._extHostWebview.createNewWebview(handle, initData.contentOptions, entry.extension); + const panel = this._extHostWebviewPanels.createNewWebviewPanel(handle, viewType, initData.title, viewColumn, initData.options, webview, true); const revivedResource = URI.revive(resource); switch (entry.type) { - case WebviewEditorType.Custom: - { - const { document } = this.getCustomDocumentEntry(viewType, revivedResource); - return entry.provider.resolveCustomEditor(document, panel, cancellation); - } - case WebviewEditorType.Text: - { - const document = this._extHostDocuments.getDocument(revivedResource); - return entry.provider.resolveCustomTextEditor(document, panel, cancellation); - } - default: - { - throw new Error('Unknown webview provider type'); - } + case WebviewEditorType.Custom: { + const { document } = this.getCustomDocumentEntry(viewType, revivedResource); + return entry.provider.resolveCustomEditor(document, panel, cancellation); + } + case WebviewEditorType.Text: { + const document = this._extHostDocuments.getDocument(revivedResource); + return entry.provider.resolveCustomTextEditor(document, panel, cancellation); + } + default: { + throw new Error('Unknown webview provider type'); + } } } @@ -366,17 +362,15 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor private getCustomEditorProvider(viewType: string): vscode.CustomEditorProvider { const entry = this._editorProviders.get(viewType); const provider = entry?.provider; - if (!provider || !this.supportEditing(provider)) { + if (!provider || !isCustomEditorProviderWithEditingCapability(provider)) { throw new Error('Custom document is not editable'); } return provider; } +} - private supportEditing( - provider: vscode.CustomTextEditorProvider | vscode.CustomEditorProvider | vscode.CustomReadonlyEditorProvider - ): provider is vscode.CustomEditorProvider { - return !!(provider as vscode.CustomEditorProvider).onDidChangeCustomDocument; - } +function isCustomEditorProviderWithEditingCapability(provider: vscode.CustomTextEditorProvider | vscode.CustomEditorProvider | vscode.CustomReadonlyEditorProvider): provider is vscode.CustomEditorProvider { + return !!(provider as vscode.CustomEditorProvider).onDidChangeCustomDocument; } function isCustomTextEditorProvider(provider: vscode.CustomReadonlyEditorProvider | vscode.CustomTextEditorProvider): provider is vscode.CustomTextEditorProvider { @@ -392,4 +386,3 @@ function hashPath(resource: URI): string { const str = resource.scheme === Schemas.file || resource.scheme === Schemas.untitled ? resource.fsPath : resource.toString(); return hash(str) + ''; } - diff --git a/src/vs/workbench/api/common/extHostDebugService.ts b/src/vs/workbench/api/common/extHostDebugService.ts index 9e0e03f1379..ba7e411aee1 100644 --- a/src/vs/workbench/api/common/extHostDebugService.ts +++ b/src/vs/workbench/api/common/extHostDebugService.ts @@ -285,8 +285,12 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E repl: options.consoleMode === DebugConsoleMode.MergeWithParent ? 'mergeWithParent' : 'separate', noDebug: options.noDebug, compact: options.compact, - debugUI: options.debugUI, - suppressSaveBeforeStart: options.suppressSaveBeforeStart + suppressSaveBeforeStart: options.suppressSaveBeforeStart, + + // Check debugUI for back-compat, #147264 + suppressDebugStatusbar: options.suppressDebugStatusbar ?? (options as any).debugUI?.simple, + suppressDebugToolbar: options.suppressDebugToolbar ?? (options as any).debugUI?.simple, + suppressDebugView: options.suppressDebugView ?? (options as any).debugUI?.simple, }); } diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 49384cee4af..dad1a4ff5d1 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -34,6 +34,7 @@ import { StopWatch } from 'vs/base/common/stopwatch'; import { isCancellationError, NotImplementedError } from 'vs/base/common/errors'; import { raceCancellationError } from 'vs/base/common/async'; import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtHostTelemetry } from 'vs/workbench/api/common/extHostTelemetry'; // --- adapter @@ -1831,31 +1832,20 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF private static _handlePool: number = 0; - private readonly _uriTransformer: IURITransformer; private readonly _proxy: extHostProtocol.MainThreadLanguageFeaturesShape; - private readonly _documents: ExtHostDocuments; - private readonly _commands: ExtHostCommands; - private readonly _diagnostics: ExtHostDiagnostics; private readonly _adapter = new Map(); - private readonly _logService: ILogService; - private readonly _apiDeprecation: IExtHostApiDeprecationService; constructor( mainContext: extHostProtocol.IMainContext, - uriTransformer: IURITransformer, - documents: ExtHostDocuments, - commands: ExtHostCommands, - diagnostics: ExtHostDiagnostics, - logService: ILogService, - apiDeprecationService: IExtHostApiDeprecationService, + private readonly _uriTransformer: IURITransformer, + private readonly _documents: ExtHostDocuments, + private readonly _commands: ExtHostCommands, + private readonly _diagnostics: ExtHostDiagnostics, + private readonly _logService: ILogService, + private readonly _apiDeprecation: IExtHostApiDeprecationService, + private readonly _extensionTelemetry: IExtHostTelemetry ) { - this._uriTransformer = uriTransformer; this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadLanguageFeatures); - this._documents = documents; - this._commands = commands; - this._diagnostics = diagnostics; - this._logService = logService; - this._apiDeprecation = apiDeprecationService; } private _transformDocumentSelector(selector: vscode.DocumentSelector): Array { @@ -1898,6 +1888,8 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF if (!isCancellationError(err)) { this._logService.error(`[${data.extension.identifier.value}] provider FAILED`); this._logService.error(err); + + this._extensionTelemetry.onExtensionError(data.extension.identifier, err); } }).finally(() => { if (!doNotLog) { diff --git a/src/vs/workbench/api/common/extHostLocalizationService.ts b/src/vs/workbench/api/common/extHostLocalizationService.ts index 51006adaaaa..611add6ffce 100644 --- a/src/vs/workbench/api/common/extHostLocalizationService.ts +++ b/src/vs/workbench/api/common/extHostLocalizationService.ts @@ -3,48 +3,54 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Language } from 'vs/base/common/platform'; -import { format } from 'vs/base/common/strings'; +import { LANGUAGE_DEFAULT } from 'vs/base/common/platform'; +import { format2 } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { ExtHostLocalizationShape, IStringDetails, MainContext, MainThreadLocalizationShape } from 'vs/workbench/api/common/extHost.protocol'; +import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; export class ExtHostLocalizationService implements ExtHostLocalizationShape { readonly _serviceBrand: undefined; private readonly _proxy: MainThreadLocalizationShape; + private readonly currentLanguage: string; + private readonly isDefaultLanguage: boolean; private readonly bundleCache: Map = new Map(); constructor( + @IExtHostInitDataService initData: IExtHostInitDataService, @IExtHostRpcService rpc: IExtHostRpcService, @ILogService private readonly logService: ILogService ) { this._proxy = rpc.getProxy(MainContext.MainThreadLocalization); + this.currentLanguage = initData.environment.appLanguage; + this.isDefaultLanguage = this.currentLanguage === LANGUAGE_DEFAULT; } getMessage(extensionId: string, details: IStringDetails): string { const { message, args, comment } = details; - if (Language.isDefault()) { - return format(message, args); + if (this.isDefaultLanguage) { + return format2(message, (args ?? {})); } let key = message; if (comment && comment.length > 0) { - key += `/${comment.join()}`; + key += `/${Array.isArray(comment) ? comment.join() : comment}`; } const str = this.bundleCache.get(extensionId)?.contents[key]; if (!str) { this.logService.warn(`Using default string since no string found in i18n bundle that has the key: ${key}`); } - return format(str ?? key, args); + return format2(str ?? key, (args ?? {})); } - getBundle(extensionId: string): { [key: string]: string } { - return this.bundleCache.get(extensionId)?.contents ?? {}; + getBundle(extensionId: string): { [key: string]: string } | undefined { + return this.bundleCache.get(extensionId)?.contents; } getBundleUri(extensionId: string): URI | undefined { @@ -52,9 +58,9 @@ export class ExtHostLocalizationService implements ExtHostLocalizationShape { } async initializeLocalizedMessages(extension: IExtensionDescription): Promise { - if (Language.isDefault() + if (this.isDefaultLanguage // TODO: support builtin extensions - || !extension.l10n + || (!extension.l10n && !extension.isBuiltin) ) { return; } @@ -64,16 +70,17 @@ export class ExtHostLocalizationService implements ExtHostLocalizationShape { } let contents: { [key: string]: string } | undefined; - const bundleLocation = this.getBundleLocation(extension); - if (!bundleLocation) { + const bundleUri = await this.getBundleLocation(extension); + if (!bundleUri) { this.logService.error(`No bundle location found for extension ${extension.identifier.value}`); return; } - const bundleUri = URI.joinPath(bundleLocation, `bundle.l10n.${Language.value()}.json`); try { const response = await this._proxy.$fetchBundleContents(bundleUri); - contents = JSON.parse(response); + const result = JSON.parse(response); + // 'contents.bundle' is a well-known key in the language pack json file that contains the _code_ translations for the extension + contents = extension.isBuiltin ? result.contents?.bundle : result; } catch (e) { this.logService.error(`Failed to load translations for ${extension.identifier.value} from ${bundleUri}: ${e.message}`); return; @@ -87,14 +94,14 @@ export class ExtHostLocalizationService implements ExtHostLocalizationShape { } } - private getBundleLocation(extension: IExtensionDescription): URI | undefined { - // TODO: support builtin extensions using IExtHostInitDataService - // if (extension.isBuiltin && this.initData.nlsBaseUrl) { - // return URI.joinPath(this.initData.nlsBaseUrl, extension.identifier.value, 'main'); - // } + private async getBundleLocation(extension: IExtensionDescription): Promise { + if (extension.isBuiltin) { + const uri = await this._proxy.$fetchBuiltInBundleUri(extension.identifier.value); + return URI.revive(uri); + } return extension.l10n - ? URI.joinPath(extension.extensionLocation, extension.l10n) + ? URI.joinPath(extension.extensionLocation, extension.l10n, `bundle.l10n.${this.currentLanguage}.json`) : undefined; } } diff --git a/src/vs/workbench/api/common/extHostLoggerService.ts b/src/vs/workbench/api/common/extHostLoggerService.ts index 6501df99bf0..47ec1627609 100644 --- a/src/vs/workbench/api/common/extHostLoggerService.ts +++ b/src/vs/workbench/api/common/extHostLoggerService.ts @@ -7,27 +7,29 @@ import { ILogger, ILoggerOptions, AbstractMessageLogger, LogLevel, AbstractLogge import { MainThreadLoggerShape, MainContext, ExtHostLogLevelServiceShape as ExtHostLogLevelServiceShape } from 'vs/workbench/api/common/extHost.protocol'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; -import { URI } from 'vs/base/common/uri'; -import { Emitter } from 'vs/base/common/event'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { Event } from 'vs/base/common/event'; +import { isUndefined } from 'vs/base/common/types'; export class ExtHostLoggerService extends AbstractLoggerService implements ExtHostLogLevelServiceShape { declare readonly _serviceBrand: undefined; - private readonly _onDidChangeLogLevel: Emitter; private readonly _proxy: MainThreadLoggerShape; constructor( @IExtHostRpcService rpc: IExtHostRpcService, @IExtHostInitDataService initData: IExtHostInitDataService, ) { - const emitter = new Emitter(); - super(initData.logLevel, emitter.event); + super(initData.logLevel, Event.None); this._proxy = rpc.getProxy(MainContext.MainThreadLogger); - this._onDidChangeLogLevel = this._register(emitter); } - $setLevel(level: LogLevel): void { - this._onDidChangeLogLevel.fire(level); + $setLevel(level: LogLevel, resource?: UriComponents): void { + if (resource) { + this.setLevel(URI.revive(resource), level); + } else if (!isUndefined(level)) { + this.setLevel(level); + } } protected doCreateLogger(resource: URI, logLevel: LogLevel, options?: ILoggerOptions): ILogger { diff --git a/src/vs/workbench/api/common/extHostNotebookDocument.ts b/src/vs/workbench/api/common/extHostNotebookDocument.ts index c3ebc53b3d6..38437605cec 100644 --- a/src/vs/workbench/api/common/extHostNotebookDocument.ts +++ b/src/vs/workbench/api/common/extHostNotebookDocument.ts @@ -110,6 +110,32 @@ export class ExtHostCell { output.items.length = 0; } output.items.push(...newItems); + + if (output.items.length > 1 && output.items.every(item => notebookCommon.isTextStreamMime(item.mime))) { + // Look for the mimes in the items, and keep track of their order. + // Merge the streams into one output item, per mime type. + const mimeOutputs = new Map(); + const mimeTypes: string[] = []; + output.items.forEach(item => { + let items: Uint8Array[]; + if (mimeOutputs.has(item.mime)) { + items = mimeOutputs.get(item.mime)!; + } else { + items = []; + mimeOutputs.set(item.mime, items); + mimeTypes.push(item.mime); + } + items.push(item.data); + }); + output.items.length = 0; + mimeTypes.forEach(mime => { + const compressed = notebookCommon.compressOutputItemStreams(mimeOutputs.get(mime)!); + output.items.push({ + mime, + data: compressed.buffer + }); + }); + } } } diff --git a/src/vs/workbench/api/common/extHostOutput.ts b/src/vs/workbench/api/common/extHostOutput.ts index abe63cff8da..3f1de6629f7 100644 --- a/src/vs/workbench/api/common/extHostOutput.ts +++ b/src/vs/workbench/api/common/extHostOutput.ts @@ -8,8 +8,8 @@ import type * as vscode from 'vscode'; import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; -import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { AbstractMessageLogger, ILogger, ILoggerService, log, LogLevel } from 'vs/platform/log/common/log'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { AbstractMessageLogger, ILogger, ILoggerService, ILogService, log, LogLevel, parseLogLevel } from 'vs/platform/log/common/log'; import { OutputChannelUpdateMode } from 'vs/workbench/services/output/common/output'; import { IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; @@ -18,6 +18,9 @@ import { toLocalISOString } from 'vs/base/common/date'; import { VSBuffer } from 'vs/base/common/buffer'; import { isString } from 'vs/base/common/types'; import { FileSystemProviderErrorCode, toFileSystemProviderErrorCode } from 'vs/platform/files/common/files'; +import { Emitter } from 'vs/base/common/event'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; class ExtHostOutputChannel extends AbstractMessageLogger implements vscode.LogOutputChannel { @@ -35,6 +38,11 @@ class ExtHostOutputChannel extends AbstractMessageLogger implements vscode.LogOu readonly extension: IExtensionDescription, ) { super(); + this._register(logger.onDidChangeLogLevel(level => this.setLevel(level))); + } + + get logLevel(): LogLevel { + return this.logger.getLevel(); } appendLine(value: string): void { @@ -117,6 +125,7 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape { @IExtHostConsumerFileSystem private readonly extHostFileSystem: IExtHostConsumerFileSystem, @IExtHostFileSystemInfo private readonly extHostFileSystemInfo: IExtHostFileSystemInfo, @ILoggerService private readonly loggerService: ILoggerService, + @ILogService private readonly logService: ILogService, ) { this.proxy = extHostRpc.getProxy(MainContext.MainThreadOutputService); this.outputsLocation = this.extHostFileSystemInfo.extUri.joinPath(initData.logsLocation, `output_logging_${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}`); @@ -135,16 +144,20 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape { throw new Error('illegal argument `name`. must not be falsy'); } const log = typeof options === 'object' && options.log; + if (log) { + checkProposedApiEnabled(extension, 'extensionLog'); + } const languageId = isString(options) ? options : undefined; if (isString(languageId) && !languageId.trim()) { throw new Error('illegal argument `languageId`. must not be empty'); } - const extHostOutputChannel = log ? this.doCreateLogOutputChannel(name, extension) : this.doCreateOutputChannel(name, languageId, extension); + const logLevel = this.getDefaultLogLevel(extension); + const extHostOutputChannel = log ? this.doCreateLogOutputChannel(name, logLevel, extension) : this.doCreateOutputChannel(name, languageId, extension); extHostOutputChannel.then(channel => { this.channels.set(channel.id, channel); channel.visible = channel.id === this.visibleChannelId; }); - return log ? this.createExtHostLogOutputChannel(name, >extHostOutputChannel) : this.createExtHostOutputChannel(name, >extHostOutputChannel); + return log ? this.createExtHostLogOutputChannel(name, logLevel, >extHostOutputChannel) : this.createExtHostOutputChannel(name, >extHostOutputChannel); } private async doCreateOutputChannel(name: string, languageId: string | undefined, extension: IExtensionDescription): Promise { @@ -158,14 +171,23 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape { return new ExtHostOutputChannel(id, name, logger, this.proxy, extension); } - private async doCreateLogOutputChannel(name: string, extension: IExtensionDescription): Promise { + private async doCreateLogOutputChannel(name: string, logLevel: LogLevel, extension: IExtensionDescription): Promise { const extensionLogDir = await this.createExtensionLogDirectory(extension); const file = this.extHostFileSystemInfo.extUri.joinPath(extensionLogDir, `${name.replace(/[\\/:\*\?"<>\|]/g, '')}.log`); - const logger = this.loggerService.createLogger(file, { name }); + const logger = this.loggerService.createLogger(file, { name }, logLevel); const id = await this.proxy.$register(name, file, true, undefined, extension.identifier.value); return new ExtHostLogOutputChannel(id, name, logger, this.proxy, extension); } + private getDefaultLogLevel(extension: IExtensionDescription): LogLevel { + let logLevel: LogLevel | undefined; + const logLevelValue = this.initData.environment.extensionLogLevel?.find(([identifier]) => ExtensionIdentifier.equals(extension.identifier, identifier))?.[1]; + if (logLevelValue) { + logLevel = parseLogLevel(logLevelValue); + } + return logLevel ?? this.logService.getLevel(); + } + private createExtensionLogDirectory(extension: IExtensionDescription): Thenable { let extensionLogDirectoryPromise = this.extensionLogDirectoryPromise.get(extension.identifier.value); if (!extensionLogDirectoryPromise) { @@ -224,18 +246,28 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape { }; } - private createExtHostLogOutputChannel(name: string, channelPromise: Promise): vscode.LogOutputChannel { - let disposed = false; + private createExtHostLogOutputChannel(name: string, logLevel: LogLevel, channelPromise: Promise): vscode.LogOutputChannel { + const disposables = new DisposableStore(); const validate = () => { - if (disposed) { + if (disposables.isDisposed) { throw new Error('Channel has been closed'); } }; + const onDidChangeLogLevel = disposables.add(new Emitter()); + channelPromise.then(channel => { + disposables.add(channel); + disposables.add(channel.onDidChangeLogLevel(e => { + logLevel = e; + onDidChangeLogLevel.fire(e); + })); + }); return { ...this.createExtHostOutputChannel(name, channelPromise), + get logLevel() { return logLevel; }, + onDidChangeLogLevel: onDidChangeLogLevel.event, trace(value: string, ...args: any[]): void { validate(); - channelPromise.then(channel => channel.info(value, ...args)); + channelPromise.then(channel => channel.trace(value, ...args)); }, debug(value: string, ...args: any[]): void { validate(); @@ -254,8 +286,7 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape { channelPromise.then(channel => channel.error(value, ...args)); }, dispose(): void { - disposed = true; - channelPromise.then(channel => channel.dispose()); + disposables.dispose(); } }; } diff --git a/src/vs/workbench/api/common/extHostTask.ts b/src/vs/workbench/api/common/extHostTask.ts index bf122d3ab2c..033b98d338f 100644 --- a/src/vs/workbench/api/common/extHostTask.ts +++ b/src/vs/workbench/api/common/extHostTask.ts @@ -25,7 +25,7 @@ import * as Platform from 'vs/base/common/platform'; import { ILogService } from 'vs/platform/log/common/log'; import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; import { USER_TASKS_GROUP_KEY } from 'vs/workbench/contrib/tasks/common/tasks'; -import { NotSupportedError } from 'vs/base/common/errors'; +import { ErrorNoTelemetry, NotSupportedError } from 'vs/base/common/errors'; export interface IExtHostTask extends ExtHostTaskShape { @@ -631,7 +631,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape, IExtHostTask if (typeof execution === 'string') { const taskExecution = this._taskExecutionPromises.get(execution); if (!taskExecution) { - throw new Error('Unexpected: The specified task is missing an execution'); + throw new ErrorNoTelemetry('Unexpected: The specified task is missing an execution'); } return taskExecution; } diff --git a/src/vs/workbench/api/common/extHostTelemetry.ts b/src/vs/workbench/api/common/extHostTelemetry.ts index d27d0a9047d..809d8e2d5a8 100644 --- a/src/vs/workbench/api/common/extHostTelemetry.ts +++ b/src/vs/workbench/api/common/extHostTelemetry.ts @@ -3,28 +3,47 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import type * as vscode from 'vscode'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Event, Emitter } from 'vs/base/common/event'; import { ExtHostTelemetryShape } from 'vs/workbench/api/common/extHost.protocol'; import { TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; -import type { TelemetryConfiguration } from 'vscode'; +import { ILogger, ILoggerService } from 'vs/platform/log/common/log'; +import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { UIKind } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; +import { getRemoteName } from 'vs/platform/remote/common/remoteHosts'; +import { cleanData, cleanRemoteAuthority } from 'vs/platform/telemetry/common/telemetryUtils'; +import { mixin } from 'vs/base/common/objects'; +import { URI } from 'vs/base/common/uri'; export class ExtHostTelemetry implements ExtHostTelemetryShape { private readonly _onDidChangeTelemetryEnabled = new Emitter(); readonly onDidChangeTelemetryEnabled: Event = this._onDidChangeTelemetryEnabled.event; - private readonly _onDidChangeTelemetryConfiguration = new Emitter(); - readonly onDidChangeTelemetryConfiguration: Event = this._onDidChangeTelemetryConfiguration.event; + private readonly _onDidChangeTelemetryConfiguration = new Emitter(); + readonly onDidChangeTelemetryConfiguration: Event = this._onDidChangeTelemetryConfiguration.event; private _productConfig: { usage: boolean; error: boolean } = { usage: true, error: true }; private _level: TelemetryLevel = TelemetryLevel.NONE; private _oldTelemetryEnablement: boolean | undefined; + private readonly _outputLogger: ILogger; + private readonly _telemetryLoggers = new Map(); + + constructor( + @IExtHostInitDataService private readonly initData: IExtHostInitDataService, + @ILoggerService loggerService: ILoggerService, + ) { + this._outputLogger = loggerService.createLogger(URI.revive(this.initData.environment.extensionTelemetryLogResource)); + this._outputLogger.info('Below are logs for extension telemetry events sent to the telemetry output channel API once the log level is set to trace.'); + this._outputLogger.info('==========================================================='); + } getTelemetryConfiguration(): boolean { return this._level === TelemetryLevel.USAGE; } - getTelemetryDetails(): TelemetryConfiguration { + getTelemetryDetails(): vscode.TelemetryConfiguration { return { isCrashEnabled: this._level >= TelemetryLevel.CRASH, isErrorsEnabled: this._productConfig.error ? this._level >= TelemetryLevel.ERROR : false, @@ -32,19 +51,163 @@ export class ExtHostTelemetry implements ExtHostTelemetryShape { }; } + instantiateLogger(extension: IExtensionDescription, appender: vscode.TelemetryAppender) { + const telemetryDetails = this.getTelemetryDetails(); + const logger = new ExtHostTelemetryLogger(appender, extension, this._outputLogger, this.getBuiltInCommonProperties(extension), { isUsageEnabled: telemetryDetails.isUsageEnabled, isErrorsEnabled: telemetryDetails.isErrorsEnabled }); + this._telemetryLoggers.set(extension.identifier.value, logger); + return logger.apiTelemetryLogger; + } + $initializeTelemetryLevel(level: TelemetryLevel, productConfig?: { usage: boolean; error: boolean }): void { this._level = level; this._productConfig = productConfig || { usage: true, error: true }; } + getBuiltInCommonProperties(extension: IExtensionDescription): Record { + const commonProperties: Record = {}; + // TODO @lramos15, does os info like node arch, platform version, etc exist here. + // Or will first party extensions just mix this in + commonProperties['common.extname'] = extension.name; + commonProperties['common.extversion'] = extension.version; + commonProperties['common.vscodemachineid'] = this.initData.telemetryInfo.machineId; + commonProperties['common.vscodesessionid'] = this.initData.telemetryInfo.sessionId; + commonProperties['common.vscodeversion'] = this.initData.version; + commonProperties['common.isnewappinstall'] = isNewAppInstall(this.initData.telemetryInfo.firstSessionDate); + commonProperties['common.product'] = this.initData.environment.appHost; + + switch (this.initData.uiKind) { + case UIKind.Web: + commonProperties['common.uikind'] = 'web'; + break; + case UIKind.Desktop: + commonProperties['common.uikind'] = 'desktop'; + break; + default: + commonProperties['common.uikind'] = 'unknown'; + } + + commonProperties['common.remotename'] = getRemoteName(cleanRemoteAuthority(this.initData.remote.authority)); + + return commonProperties; + } + $onDidChangeTelemetryLevel(level: TelemetryLevel): void { this._oldTelemetryEnablement = this.getTelemetryConfiguration(); this._level = level; + const telemetryDetails = this.getTelemetryDetails(); + + // Loop through all loggers and update their level + this._telemetryLoggers.forEach(logger => { + logger.updateTelemetryEnablements(telemetryDetails.isUsageEnabled, telemetryDetails.isErrorsEnabled); + }); + if (this._oldTelemetryEnablement !== this.getTelemetryConfiguration()) { this._onDidChangeTelemetryEnabled.fire(this.getTelemetryConfiguration()); } this._onDidChangeTelemetryConfiguration.fire(this.getTelemetryDetails()); } + + onExtensionError(extension: ExtensionIdentifier, error: Error): boolean { + const logger = this._telemetryLoggers.get(extension.value); + if (!logger) { + return false; + } + logger.logError(error); + return true; + } +} + +export class ExtHostTelemetryLogger { + private _appender: vscode.TelemetryAppender; + private readonly _onDidChangeEnableStates = new Emitter(); + private _telemetryEnablements: { isUsageEnabled: boolean; isErrorsEnabled: boolean }; + private _apiObject: vscode.TelemetryLogger | undefined; + constructor( + appender: vscode.TelemetryAppender, + private readonly _extension: IExtensionDescription, + private readonly _logger: ILogger, + private readonly _commonProperties: Record, + telemetryEnablements: { isUsageEnabled: boolean; isErrorsEnabled: boolean }) { + this._appender = appender; + this._telemetryEnablements = { isUsageEnabled: telemetryEnablements.isUsageEnabled, isErrorsEnabled: telemetryEnablements.isErrorsEnabled }; + } + + updateTelemetryEnablements(isUsageEnabled: boolean, isErrorsEnabled: boolean): void { + if (this._apiObject) { + this._telemetryEnablements = { isUsageEnabled, isErrorsEnabled }; + this._onDidChangeEnableStates.fire(this._apiObject); + } + } + + mixInCommonPropsAndCleanData(data: Record): Record { + if (!this._appender.ignoreBuiltInCommonProperties) { + data = mixin(data, this._commonProperties); + } + if (this._appender.additionalCommonProperties) { + data = mixin(data, this._appender.additionalCommonProperties); + } + + data = cleanData(data, []); + + return data; + } + + private logEvent(eventName: string, data?: Record): void { + eventName = this._extension.identifier.value + '/' + eventName; + data = this.mixInCommonPropsAndCleanData(data || {}); + this._appender.logEvent(eventName, data); + this._logger.trace(eventName, data); + } + + logUsage(eventName: string, data?: Record): void { + if (!this._telemetryEnablements.isUsageEnabled) { + return; + } + this.logEvent(eventName, data); + } + + logError(eventNameOrException: Error | string, data?: Record): void { + if (!this._telemetryEnablements.isErrorsEnabled) { + return; + } + if (typeof eventNameOrException === 'string') { + this.logEvent(eventNameOrException, data); + } else { + // TODO @lramos15, implement cleaning for and logging for this case + this._appender.logException(eventNameOrException, data); + } + } + + get apiTelemetryLogger(): vscode.TelemetryLogger { + if (!this._apiObject) { + const that = this; + const obj: vscode.TelemetryLogger = { + logUsage: that.logUsage.bind(that), + get isUsageEnabled() { + return that._telemetryEnablements.isUsageEnabled; + }, + get isErrorsEnabled() { + return that._telemetryEnablements.isErrorsEnabled; + }, + logError: that.logError.bind(that), + dispose: that.dispose.bind(that), + onDidChangeEnableStates: that._onDidChangeEnableStates.event.bind(that) + }; + this._apiObject = Object.freeze(obj); + } + return this._apiObject; + } + + dispose(): void { + if (this._appender?.flush) { + this._appender.flush(); + } + } +} + +export function isNewAppInstall(firstSessionDate: string): boolean { + const installAge = Date.now() - new Date(firstSessionDate).getTime(); + return isNaN(installAge) ? false : installAge < 1000 * 60 * 60 * 24; // install age is less than a day } export const IExtHostTelemetry = createDecorator('IExtHostTelemetry'); diff --git a/src/vs/workbench/api/common/extHostTelemetryLogService.ts b/src/vs/workbench/api/common/extHostTelemetryLogService.ts deleted file mode 100644 index e55fcb27b4c..00000000000 --- a/src/vs/workbench/api/common/extHostTelemetryLogService.ts +++ /dev/null @@ -1,34 +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 { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; -import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; - -export interface IExtHostTelemetryLogService { - readonly _serviceBrand: undefined; - - logToTelemetryOutputChannel(extension: IExtensionDescription, eventName: string, data: Record): void; -} - -export const IExtHostTelemetryLogService = createDecorator('IExtHostTelemetryLogService'); - -export class ExtHostTelemetryLogService implements IExtHostTelemetryLogService { - - declare readonly _serviceBrand: undefined; - - private readonly _telemetryShape: extHostProtocol.MainThreadTelemetryShape; - - constructor( - @IExtHostRpcService rpc: IExtHostRpcService, - ) { - this._telemetryShape = rpc.getProxy(extHostProtocol.MainContext.MainThreadTelemetry); - } - - public logToTelemetryOutputChannel(extension: IExtensionDescription, eventName: string, data: Record): void { - this._telemetryShape.$logTelemetryToOutputChannel(`${extension.identifier.value}/${eventName}`, data); - } -} diff --git a/src/vs/workbench/api/common/extHostTestItem.ts b/src/vs/workbench/api/common/extHostTestItem.ts index 25fa2780a45..496cc12a035 100644 --- a/src/vs/workbench/api/common/extHostTestItem.ts +++ b/src/vs/workbench/api/common/extHostTestItem.ts @@ -175,6 +175,8 @@ export class TestItemImpl implements vscode.TestItem { } export class TestItemRootImpl extends TestItemImpl { + public readonly _isRoot = true; + constructor(controllerId: string, label: string) { super(controllerId, controllerId, label, undefined); } diff --git a/src/vs/workbench/api/common/extHostTesting.ts b/src/vs/workbench/api/common/extHostTesting.ts index c436a936cf6..9df368705cd 100644 --- a/src/vs/workbench/api/common/extHostTesting.ts +++ b/src/vs/workbench/api/common/extHostTesting.ts @@ -23,7 +23,7 @@ import * as Convert from 'vs/workbench/api/common/extHostTypeConverters'; import { TestRunProfileKind, TestRunRequest } from 'vs/workbench/api/common/extHostTypes'; import { TestId, TestIdPathParts, TestPosition } from 'vs/workbench/contrib/testing/common/testId'; import { InvalidTestItemError } from 'vs/workbench/contrib/testing/common/testItemCollection'; -import { AbstractIncrementalTestCollection, CoverageDetails, IFileCoverage, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, ISerializedTestResults, ITestItem, RunTestForControllerRequest, RunTestForControllerResult, TestResultState, TestRunProfileBitset, TestsDiff, TestsDiffOp } from 'vs/workbench/contrib/testing/common/testTypes'; +import { AbstractIncrementalTestCollection, CoverageDetails, IFileCoverage, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, ISerializedTestResults, ITestItem, ITestItemContext, RunTestForControllerRequest, RunTestForControllerResult, TestResultState, TestRunProfileBitset, TestsDiff, TestsDiffOp } from 'vs/workbench/contrib/testing/common/testTypes'; import type * as vscode from 'vscode'; interface ControllerInfo { @@ -52,8 +52,16 @@ export class ExtHostTesting implements ExtHostTestingShape { this.runTracker = new TestRunCoordinator(this.proxy); commands.registerArgumentProcessor({ - processArgument: arg => - arg?.$mid === MarshalledId.TestItemContext ? toItemFromContext(arg) : arg, + processArgument: arg => { + if (arg?.$mid !== MarshalledId.TestItemContext) { + return arg; + } + + const cast = arg as ITestItemContext; + const targetTest = cast.tests[cast.tests.length - 1].item.extId; + const controller = this.controllers.get(TestId.root(targetTest)); + return controller?.collection.tree.get(targetTest)?.actual ?? toItemFromContext(arg); + } }); } @@ -165,6 +173,17 @@ export class ExtHostTesting implements ExtHostTestingShape { }, token); } + /** + * @inheritdoc + */ + $syncTests(): Promise { + for (const { collection } of this.controllers.values()) { + collection.flushDiff(); + } + + return Promise.resolve(); + } + /** * @inheritdoc */ @@ -751,7 +770,8 @@ class MirroredChangeCollector extends IncrementalChangeCollector => { return treeView.reveal(element, options); diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 2be8385ad85..8721bb81444 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1846,7 +1846,8 @@ export namespace TestResults { const byInternalId = new Map(); for (const item of serialized.items) { byInternalId.set(item.item.extId, item); - if (serialized.request.targets.some(t => t.controllerId === item.controllerId && t.testIds.includes(item.item.extId))) { + const controllerId = TestId.root(item.item.extId); + if (serialized.request.targets.some(t => t.controllerId === controllerId && t.testIds.includes(item.item.extId))) { roots.push(item); } } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 57c2608bdfc..f7b89d987a9 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -28,12 +28,21 @@ import type * as vscode from 'vscode'; * but new ones must not be added * */ function es5ClassCompat(target: Function): any { - ///@ts-expect-error - function _() { return Reflect.construct(target, arguments, this.constructor); } - Object.defineProperty(_, 'name', Object.getOwnPropertyDescriptor(target, 'name')!); - Object.setPrototypeOf(_, target); - Object.setPrototypeOf(_.prototype, target.prototype); - return _; + const interceptFunctions = { + apply: function () { + const args = arguments.length === 1 ? [] : arguments[1]; + return Reflect.construct(target, args, arguments[0].constructor); + }, + call: function () { + if (arguments.length === 0) { + return Reflect.construct(target, []); + } else { + const [thisArg, ...restArgs] = arguments; + return Reflect.construct(target, restArgs, thisArg.constructor); + } + } + }; + return Object.assign(target, interceptFunctions); } @es5ClassCompat @@ -687,11 +696,11 @@ export class SnippetTextEdit implements vscode.SnippetTextEdit { } export interface IFileOperationOptions { - overwrite?: boolean; - ignoreIfExists?: boolean; - ignoreIfNotExists?: boolean; - recursive?: boolean; - contents?: Uint8Array; + readonly overwrite?: boolean; + readonly ignoreIfExists?: boolean; + readonly ignoreIfNotExists?: boolean; + readonly recursive?: boolean; + readonly contents?: Uint8Array; } export const enum FileEditType { @@ -703,43 +712,43 @@ export const enum FileEditType { } export interface IFileOperation { - _type: FileEditType.File; - from?: URI; - to?: URI; - options?: IFileOperationOptions; - metadata?: vscode.WorkspaceEditEntryMetadata; + readonly _type: FileEditType.File; + readonly from?: URI; + readonly to?: URI; + readonly options?: IFileOperationOptions; + readonly metadata?: vscode.WorkspaceEditEntryMetadata; } export interface IFileTextEdit { - _type: FileEditType.Text; - uri: URI; - edit: TextEdit; - metadata?: vscode.WorkspaceEditEntryMetadata; + readonly _type: FileEditType.Text; + readonly uri: URI; + readonly edit: TextEdit; + readonly metadata?: vscode.WorkspaceEditEntryMetadata; } export interface IFileSnippetTextEdit { - _type: FileEditType.Snippet; - uri: URI; - range: vscode.Range; - edit: vscode.SnippetString; - metadata?: vscode.WorkspaceEditEntryMetadata; + readonly _type: FileEditType.Snippet; + readonly uri: URI; + readonly range: vscode.Range; + readonly edit: vscode.SnippetString; + readonly metadata?: vscode.WorkspaceEditEntryMetadata; } export interface IFileCellEdit { - _type: FileEditType.Cell; - uri: URI; - edit?: ICellPartialMetadataEdit | IDocumentMetadataEdit; - notebookMetadata?: Record; - metadata?: vscode.WorkspaceEditEntryMetadata; + readonly _type: FileEditType.Cell; + readonly uri: URI; + readonly edit?: ICellPartialMetadataEdit | IDocumentMetadataEdit; + readonly notebookMetadata?: Record; + readonly metadata?: vscode.WorkspaceEditEntryMetadata; } export interface ICellEdit { - _type: FileEditType.CellReplace; - metadata?: vscode.WorkspaceEditEntryMetadata; - uri: URI; - index: number; - count: number; - cells: vscode.NotebookCellData[]; + readonly _type: FileEditType.CellReplace; + readonly metadata?: vscode.WorkspaceEditEntryMetadata; + readonly uri: URI; + readonly index: number; + readonly count: number; + readonly cells: vscode.NotebookCellData[]; } @@ -757,15 +766,15 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { // --- file - renameFile(from: vscode.Uri, to: vscode.Uri, options?: { overwrite?: boolean; ignoreIfExists?: boolean }, metadata?: vscode.WorkspaceEditEntryMetadata): void { + renameFile(from: vscode.Uri, to: vscode.Uri, options?: { readonly overwrite?: boolean; readonly ignoreIfExists?: boolean }, metadata?: vscode.WorkspaceEditEntryMetadata): void { this._edits.push({ _type: FileEditType.File, from, to, options, metadata }); } - createFile(uri: vscode.Uri, options?: { overwrite?: boolean; ignoreIfExists?: boolean; contents?: Uint8Array }, metadata?: vscode.WorkspaceEditEntryMetadata): void { + createFile(uri: vscode.Uri, options?: { readonly overwrite?: boolean; readonly ignoreIfExists?: boolean; readonly contents?: Uint8Array }, metadata?: vscode.WorkspaceEditEntryMetadata): void { this._edits.push({ _type: FileEditType.File, from: undefined, to: uri, options, metadata }); } - deleteFile(uri: vscode.Uri, options?: { recursive?: boolean; ignoreIfNotExists?: boolean }, metadata?: vscode.WorkspaceEditEntryMetadata): void { + deleteFile(uri: vscode.Uri, options?: { readonly recursive?: boolean; readonly ignoreIfNotExists?: boolean }, metadata?: vscode.WorkspaceEditEntryMetadata): void { this._edits.push({ _type: FileEditType.File, from: uri, to: undefined, options, metadata }); } @@ -808,12 +817,12 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { return this._edits.some(edit => edit._type === FileEditType.Text && edit.uri.toString() === uri.toString()); } - set(uri: URI, edits: (TextEdit | SnippetTextEdit)[]): void; - set(uri: URI, edits: [TextEdit | SnippetTextEdit, vscode.WorkspaceEditEntryMetadata][]): void; - set(uri: URI, edits: NotebookEdit[]): void; - set(uri: URI, edits: [NotebookEdit, vscode.WorkspaceEditEntryMetadata][]): void; + set(uri: URI, edits: ReadonlyArray): void; + set(uri: URI, edits: ReadonlyArray<[TextEdit | SnippetTextEdit, vscode.WorkspaceEditEntryMetadata]>): void; + set(uri: URI, edits: readonly NotebookEdit[]): void; + set(uri: URI, edits: ReadonlyArray<[NotebookEdit, vscode.WorkspaceEditEntryMetadata]>): void; - set(uri: URI, edits: null | undefined | (TextEdit | SnippetTextEdit | NotebookEdit | [NotebookEdit, vscode.WorkspaceEditEntryMetadata] | [TextEdit | SnippetTextEdit, vscode.WorkspaceEditEntryMetadata])[]): void { + set(uri: URI, edits: null | undefined | ReadonlyArray): void { if (!edits) { // remove all text, snippet, or notebook edits for `uri` for (let i = 0; i < this._edits.length; i++) { @@ -2460,6 +2469,22 @@ export enum ProgressLocation { Notification = 15 } +export namespace ViewBadge { + export function isViewBadge(thing: any): thing is vscode.ViewBadge { + const viewBadgeThing = thing as vscode.ViewBadge; + + if (!isNumber(viewBadgeThing.value)) { + console.log('INVALID view badge, invalid value', viewBadgeThing.value); + return false; + } + if (viewBadgeThing.tooltip && !isString(viewBadgeThing.tooltip)) { + console.log('INVALID view badge, invalid tooltip', viewBadgeThing.tooltip); + return false; + } + return true; + } +} + @es5ClassCompat export class TreeItem { diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index db22cb7fc13..11359a68969 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -33,7 +33,8 @@ export class ExtHostWebview implements vscode.Webview { #isDisposed: boolean = false; #hasCalledAsWebviewUri = false; - #serializeBuffersForPostMessage = false; + #serializeBuffersForPostMessage: boolean; + #shouldRewriteOldResourceUris: boolean; constructor( handle: extHostProtocol.WebviewHandle, @@ -51,6 +52,7 @@ export class ExtHostWebview implements vscode.Webview { this.#workspace = workspace; this.#extension = extension; this.#serializeBuffersForPostMessage = shouldSerializeBuffersForPostMessage(extension); + this.#shouldRewriteOldResourceUris = shouldTryRewritingOldResourceUris(extension); this.#deprecationService = deprecationService; } @@ -98,12 +100,12 @@ export class ExtHostWebview implements vscode.Webview { this.assertNotDisposed(); if (this.#html !== value) { this.#html = value; - if (!this.#hasCalledAsWebviewUri && /(["'])vscode-resource:([^\s'"]+?)(["'])/i.test(value)) { + if (this.#shouldRewriteOldResourceUris && !this.#hasCalledAsWebviewUri && /(["'])vscode-resource:([^\s'"]+?)(["'])/i.test(value)) { this.#hasCalledAsWebviewUri = true; this.#deprecationService.report('Webview vscode-resource: uris', this.#extension, `Please migrate to use the 'webview.asWebviewUri' api instead: https://aka.ms/vscode-webview-use-aswebviewuri`); } - this.#proxy.$setHtml(this.#handle, value); + this.#proxy.$setHtml(this.#handle, this.rewriteOldResourceUrlsIfNeeded(value)); } } @@ -131,6 +133,32 @@ export class ExtHostWebview implements vscode.Webview { throw new Error('Webview is disposed'); } } + + private rewriteOldResourceUrlsIfNeeded(value: string): string { + if (!this.#shouldRewriteOldResourceUris) { + return value; + } + + const isRemote = this.#extension.extensionLocation?.scheme === Schemas.vscodeRemote; + const remoteAuthority = this.#extension.extensionLocation.scheme === Schemas.vscodeRemote ? this.#extension.extensionLocation.authority : undefined; + return value + .replace(/(["'])(?:vscode-resource):(\/\/([^\s\/'"]+?)(?=\/))?([^\s'"]+?)(["'])/gi, (_match, startQuote, _1, scheme, path, endQuote) => { + const uri = URI.from({ + scheme: scheme || 'file', + path: decodeURIComponent(path), + }); + const webviewUri = asWebviewUri(uri, { isRemote, authority: remoteAuthority }).toString(); + return `${startQuote}${webviewUri}${endQuote}`; + }) + .replace(/(["'])(?:vscode-webview-resource):(\/\/[^\s\/'"]+\/([^\s\/'"]+?)(?=\/))?([^\s'"]+?)(["'])/gi, (_match, startQuote, _1, scheme, path, endQuote) => { + const uri = URI.from({ + scheme: scheme || 'file', + path: decodeURIComponent(path), + }); + const webviewUri = asWebviewUri(uri, { isRemote, authority: remoteAuthority }).toString(); + return `${startQuote}${webviewUri}${endQuote}`; + }); + } } export function shouldSerializeBuffersForPostMessage(extension: IExtensionDescription): boolean { @@ -142,6 +170,19 @@ export function shouldSerializeBuffersForPostMessage(extension: IExtensionDescri } } +function shouldTryRewritingOldResourceUris(extension: IExtensionDescription): boolean { + try { + const version = normalizeVersion(parseVersion(extension.engines.vscode)); + if (!version) { + return false; + } + + return version.majorBase < 1 || (version.majorBase === 1 && version.minorBase < 60); + } catch { + return false; + } +} + export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { private readonly _webviewProxy: extHostProtocol.MainThreadWebviewsShape; diff --git a/src/vs/workbench/api/common/extensionHostMain.ts b/src/vs/workbench/api/common/extensionHostMain.ts index a204a569f0e..f621e0af82a 100644 --- a/src/vs/workbench/api/common/extensionHostMain.ts +++ b/src/vs/workbench/api/common/extensionHostMain.ts @@ -11,16 +11,17 @@ import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { MainContext, MainThreadConsoleShape } from 'vs/workbench/api/common/extHost.protocol'; import { IExtensionHostInitData } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { RPCProtocol } from 'vs/workbench/services/extensions/common/rpcProtocol'; -import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { getSingletonServiceDescriptors } from 'vs/platform/instantiation/common/extensions'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IExtHostRpcService, ExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { IURITransformerService, URITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService'; import { IExtHostExtensionService, IHostUtils } from 'vs/workbench/api/common/extHostExtensionService'; +import { IExtHostTelemetry } from 'vs/workbench/api/common/extHostTelemetry'; export interface IExitFn { (code?: number): any; @@ -30,6 +31,75 @@ export interface IConsolePatchFn { (mainThreadConsole: MainThreadConsoleShape): any; } +abstract class ErrorHandler { + + static { + // increase number of stack frames (from 10, https://github.com/v8/v8/wiki/Stack-Trace-API) + Error.stackTraceLimit = 100; + } + + static async installEarlyHandler(accessor: ServicesAccessor): Promise { + // does NOT dependent of extension information, can be installed immediately, and simply forwards + // to the log service and main thread errors + const logService = accessor.get(ILogService); + const rpcService = accessor.get(IExtHostRpcService); + const mainThreadErrors = rpcService.getProxy(MainContext.MainThreadErrors); + + errors.setUnexpectedErrorHandler(err => { + logService.error(err); + const data = errors.transformErrorForSerialization(err); + mainThreadErrors.$onUnexpectedError(data); + }); + } + + static async installFullHandler(accessor: ServicesAccessor): Promise { + // uses extension knowledges to correlate errors with extensions + + const logService = accessor.get(ILogService); + const rpcService = accessor.get(IExtHostRpcService); + const extensionService = accessor.get(IExtHostExtensionService); + const extensionTelemetry = accessor.get(IExtHostTelemetry); + + const mainThreadExtensions = rpcService.getProxy(MainContext.MainThreadExtensionService); + const mainThreadErrors = rpcService.getProxy(MainContext.MainThreadErrors); + + const map = await extensionService.getExtensionPathIndex(); + const extensionErrors = new WeakMap(); + + // set the prepareStackTrace-handle and use it as a side-effect to associate errors + // with extensions - this works by looking up callsites in the extension path index + (Error).prepareStackTrace = (error: Error, stackTrace: errors.V8CallSite[]) => { + let stackTraceMessage = ''; + let extension: IExtensionDescription | undefined; + let fileName: string | null; + for (const call of stackTrace) { + stackTraceMessage += `\n\tat ${call.toString()}`; + fileName = call.getFileName(); + if (!extension && fileName) { + extension = map.findSubstr(URI.file(fileName)); + } + } + extensionErrors.set(error, extension?.identifier); + return `${error.name || 'Error'}: ${error.message || ''}${stackTraceMessage}`; + }; + + errors.setUnexpectedErrorHandler(err => { + logService.error(err); + + const data = errors.transformErrorForSerialization(err); + const extension = extensionErrors.get(err); + if (!extension) { + mainThreadErrors.$onUnexpectedError(data); + return; + } + + mainThreadExtensions.$onExtensionRuntimeError(extension, data); + const reported = extensionTelemetry.onExtensionError(extension, err); + logService.trace('forwarded error to extension?', reported, extension); + }); + } +} + export class ExtensionHostMain { private readonly _hostUtils: IHostUtils; @@ -59,6 +129,8 @@ export class ExtensionHostMain { const instaService: IInstantiationService = new InstantiationService(services, true); + instaService.invokeFunction(ErrorHandler.installEarlyHandler); + // ugly self - inject this._logService = instaService.invokeFunction(accessor => accessor.get(ILogService)); @@ -76,38 +148,8 @@ export class ExtensionHostMain { this._extensionService = instaService.invokeFunction(accessor => accessor.get(IExtHostExtensionService)); this._extensionService.initialize(); - // error forwarding and stack trace scanning - Error.stackTraceLimit = 100; // increase number of stack frames (from 10, https://github.com/v8/v8/wiki/Stack-Trace-API) - const extensionErrors = new WeakMap(); - this._extensionService.getExtensionPathIndex().then(map => { - (Error).prepareStackTrace = (error: Error, stackTrace: errors.V8CallSite[]) => { - let stackTraceMessage = ''; - let extension: IExtensionDescription | undefined; - let fileName: string; - for (const call of stackTrace) { - stackTraceMessage += `\n\tat ${call.toString()}`; - fileName = call.getFileName(); - if (!extension && fileName) { - extension = map.findSubstr(URI.file(fileName)); - } - - } - extensionErrors.set(error, extension); - return `${error.name || 'Error'}: ${error.message || ''}${stackTraceMessage}`; - }; - }); - - const mainThreadExtensions = this._rpcProtocol.getProxy(MainContext.MainThreadExtensionService); - const mainThreadErrors = this._rpcProtocol.getProxy(MainContext.MainThreadErrors); - errors.setUnexpectedErrorHandler(err => { - const data = errors.transformErrorForSerialization(err); - const extension = extensionErrors.get(err); - if (extension) { - mainThreadExtensions.$onExtensionRuntimeError(extension.identifier, data); - } else { - mainThreadErrors.$onUnexpectedError(data); - } - }); + // install error handler that is extension-aware + instaService.invokeFunction(ErrorHandler.installFullHandler); } async asBrowserUri(uri: URI): Promise { diff --git a/src/vs/workbench/api/node/extHost.node.services.ts b/src/vs/workbench/api/node/extHost.node.services.ts index 27562ce32ca..98d300f7d8c 100644 --- a/src/vs/workbench/api/node/extHost.node.services.ts +++ b/src/vs/workbench/api/node/extHost.node.services.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ExtHostTerminalService } from 'vs/workbench/api/node/extHostTerminalService'; import { ExtHostTask } from 'vs/workbench/api/node/extHostTask'; import { ExtHostDebugService } from 'vs/workbench/api/node/extHostDebugService'; @@ -30,7 +30,7 @@ import { IExtHostVariableResolverProvider } from 'vs/workbench/api/common/extHos // ######################################################################### registerSingleton(IExtHostExtensionService, ExtHostExtensionService, false); -registerSingleton(ILoggerService, ExtHostLoggerService, true); +registerSingleton(ILoggerService, ExtHostLoggerService, InstantiationType.Delayed); registerSingleton(IExtensionStoragePaths, ExtensionStoragePaths, false); registerSingleton(IExtHostDebugService, ExtHostDebugService, false); diff --git a/src/vs/workbench/api/test/browser/extHostApiCommands.test.ts b/src/vs/workbench/api/test/browser/extHostApiCommands.test.ts index 8264a9439a2..a8b3cb85a52 100644 --- a/src/vs/workbench/api/test/browser/extHostApiCommands.test.ts +++ b/src/vs/workbench/api/test/browser/extHostApiCommands.test.ts @@ -58,6 +58,7 @@ import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeat import { LanguageFeaturesService } from 'vs/editor/common/services/languageFeaturesService'; import { assertType } from 'vs/base/common/types'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { IExtHostTelemetry } from 'vs/workbench/api/common/extHostTelemetry'; function assertRejects(fn: () => Promise, message: string = 'Expected rejection') { return fn().then(() => assert.ok(false, message), _err => assert.ok(true)); @@ -167,7 +168,7 @@ suite('ExtHostLanguageFeatureCommands', function () { const diagnostics = new ExtHostDiagnostics(rpcProtocol, new NullLogService(), new class extends mock() { }); rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, diagnostics); - extHost = new ExtHostLanguageFeatures(rpcProtocol, new URITransformerService(null), extHostDocuments, commands, diagnostics, new NullLogService(), NullApiDeprecationService); + extHost = new ExtHostLanguageFeatures(rpcProtocol, new URITransformerService(null), extHostDocuments, commands, diagnostics, new NullLogService(), NullApiDeprecationService, new class extends mock() { }); rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, extHost); mainThread = rpcProtocol.set(MainContext.MainThreadLanguageFeatures, insta.createInstance(MainThreadLanguageFeatures, rpcProtocol)); diff --git a/src/vs/workbench/api/test/browser/extHostDiagnostics.test.ts b/src/vs/workbench/api/test/browser/extHostDiagnostics.test.ts index 021725f35f0..f7d92af7f58 100644 --- a/src/vs/workbench/api/test/browser/extHostDiagnostics.test.ts +++ b/src/vs/workbench/api/test/browser/extHostDiagnostics.test.ts @@ -16,6 +16,7 @@ import type * as vscode from 'vscode'; import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { ExtUri, extUri } from 'vs/base/common/resources'; import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo'; +import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; suite('ExtHostDiagnostics', () => { @@ -438,43 +439,46 @@ suite('ExtHostDiagnostics', () => { }); test('Diagnostics created by tasks aren\'t accessible to extensions #47292', async function () { - const diags = new ExtHostDiagnostics(new class implements IMainContext { - getProxy(id: any): any { - return {}; - } - set(): any { - return null; - } - dispose() { } - assertRegistered(): void { + return runWithFakedTimers({}, async function () { - } - drain() { - return undefined!; - } - }, new NullLogService(), fileSystemInfoService); + const diags = new ExtHostDiagnostics(new class implements IMainContext { + getProxy(id: any): any { + return {}; + } + set(): any { + return null; + } + dispose() { } + assertRegistered(): void { + + } + drain() { + return undefined!; + } + }, new NullLogService(), fileSystemInfoService); - // - const uri = URI.parse('foo:bar'); - const data: IMarkerData[] = [{ - message: 'message', - startLineNumber: 1, - startColumn: 1, - endLineNumber: 1, - endColumn: 1, - severity: 3 - }]; + // + const uri = URI.parse('foo:bar'); + const data: IMarkerData[] = [{ + message: 'message', + startLineNumber: 1, + startColumn: 1, + endLineNumber: 1, + endColumn: 1, + severity: 3 + }]; - const p1 = Event.toPromise(diags.onDidChangeDiagnostics); - diags.$acceptMarkersChange([[uri, data]]); - await p1; - assert.strictEqual(diags.getDiagnostics(uri).length, 1); + const p1 = Event.toPromise(diags.onDidChangeDiagnostics); + diags.$acceptMarkersChange([[uri, data]]); + await p1; + assert.strictEqual(diags.getDiagnostics(uri).length, 1); - const p2 = Event.toPromise(diags.onDidChangeDiagnostics); - diags.$acceptMarkersChange([[uri, []]]); - await p2; - assert.strictEqual(diags.getDiagnostics(uri).length, 0); + const p2 = Event.toPromise(diags.onDidChangeDiagnostics); + diags.$acceptMarkersChange([[uri, []]]); + await p2; + assert.strictEqual(diags.getDiagnostics(uri).length, 0); + }); }); test('languages.getDiagnostics doesn\'t handle case insensitivity correctly #128198', function () { diff --git a/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts b/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts index 17a22143276..69b6f50acc6 100644 --- a/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts @@ -55,6 +55,7 @@ import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeat import { LanguageFeaturesService } from 'vs/editor/common/services/languageFeaturesService'; import { CodeActionTriggerSource } from 'vs/editor/contrib/codeAction/browser/types'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { IExtHostTelemetry } from 'vs/workbench/api/common/extHostTelemetry'; suite('ExtHostLanguageFeatures', function () { @@ -121,7 +122,7 @@ suite('ExtHostLanguageFeatures', function () { const diagnostics = new ExtHostDiagnostics(rpcProtocol, new NullLogService(), new class extends mock() { }); rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, diagnostics); - extHost = new ExtHostLanguageFeatures(rpcProtocol, new URITransformerService(null), extHostDocuments, commands, diagnostics, new NullLogService(), NullApiDeprecationService); + extHost = new ExtHostLanguageFeatures(rpcProtocol, new URITransformerService(null), extHostDocuments, commands, diagnostics, new NullLogService(), NullApiDeprecationService, new class extends mock() { }); rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, extHost); mainThread = rpcProtocol.set(MainContext.MainThreadLanguageFeatures, inst.createInstance(MainThreadLanguageFeatures, rpcProtocol)); diff --git a/src/vs/workbench/api/test/browser/extHostNotebook.test.ts b/src/vs/workbench/api/test/browser/extHostNotebook.test.ts index 09e785c307b..0a9c0def137 100644 --- a/src/vs/workbench/api/test/browser/extHostNotebook.test.ts +++ b/src/vs/workbench/api/test/browser/extHostNotebook.test.ts @@ -10,7 +10,7 @@ import { TestRPCProtocol } from 'vs/workbench/api/test/common/testRPCProtocol'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { NullLogService } from 'vs/platform/log/common/log'; import { mock } from 'vs/base/test/common/mock'; -import { IModelAddedData, MainContext, MainThreadCommandsShape, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol'; +import { IModelAddedData, MainContext, MainThreadCommandsShape, MainThreadNotebookShape, NotebookCellsChangedEventDto, NotebookOutputItemDto } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; import { ExtHostNotebookDocument } from 'vs/workbench/api/common/extHostNotebookDocument'; import { CellKind, CellUri, NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; @@ -542,4 +542,95 @@ suite('NotebookCell#Document', function () { assert.ok(cellChange.metadata === undefined); assert.ok(cellChange.outputs === undefined); }); + + async function replaceOutputs(cellIndex: number, outputId: string, outputItems: NotebookOutputItemDto[]) { + const changeEvent = Event.toPromise(extHostNotebookDocuments.onDidChangeNotebookDocument); + extHostNotebookDocuments.$acceptModelChanged(notebook.uri, new SerializableObjectWithBuffers({ + versionId: notebook.apiNotebook.version + 1, + rawEvents: [{ + kind: NotebookCellsChangeType.Output, + index: cellIndex, + outputs: [{ outputId, items: outputItems }] + }] + }), false); + await changeEvent; + } + async function appendOutputItem(cellIndex: number, outputId: string, outputItems: NotebookOutputItemDto[]) { + const changeEvent = Event.toPromise(extHostNotebookDocuments.onDidChangeNotebookDocument); + extHostNotebookDocuments.$acceptModelChanged(notebook.uri, new SerializableObjectWithBuffers({ + versionId: notebook.apiNotebook.version + 1, + rawEvents: [{ + kind: NotebookCellsChangeType.OutputItem, + index: cellIndex, + append: true, + outputId, + outputItems + }] + }), false); + await changeEvent; + } + test('Append multiple text/plain output items', async function () { + await replaceOutputs(1, '1', [{ mime: 'text/plain', valueBytes: VSBuffer.fromString('foo') }]); + await appendOutputItem(1, '1', [{ mime: 'text/plain', valueBytes: VSBuffer.fromString('bar') }]); + await appendOutputItem(1, '1', [{ mime: 'text/plain', valueBytes: VSBuffer.fromString('baz') }]); + + + assert.strictEqual(notebook.apiNotebook.cellAt(1).outputs.length, 1); + assert.strictEqual(notebook.apiNotebook.cellAt(1).outputs[0].items.length, 3); + assert.strictEqual(notebook.apiNotebook.cellAt(1).outputs[0].items[0].mime, 'text/plain'); + assert.strictEqual(VSBuffer.wrap(notebook.apiNotebook.cellAt(1).outputs[0].items[0].data).toString(), 'foo'); + assert.strictEqual(notebook.apiNotebook.cellAt(1).outputs[0].items[1].mime, 'text/plain'); + assert.strictEqual(VSBuffer.wrap(notebook.apiNotebook.cellAt(1).outputs[0].items[1].data).toString(), 'bar'); + assert.strictEqual(notebook.apiNotebook.cellAt(1).outputs[0].items[2].mime, 'text/plain'); + assert.strictEqual(VSBuffer.wrap(notebook.apiNotebook.cellAt(1).outputs[0].items[2].data).toString(), 'baz'); + }); + test('Append multiple stdout stream output items to an output with another mime', async function () { + await replaceOutputs(1, '1', [{ mime: 'text/plain', valueBytes: VSBuffer.fromString('foo') }]); + await appendOutputItem(1, '1', [{ mime: 'application/vnd.code.notebook.stdout', valueBytes: VSBuffer.fromString('bar') }]); + await appendOutputItem(1, '1', [{ mime: 'application/vnd.code.notebook.stdout', valueBytes: VSBuffer.fromString('baz') }]); + + assert.strictEqual(notebook.apiNotebook.cellAt(1).outputs.length, 1); + assert.strictEqual(notebook.apiNotebook.cellAt(1).outputs[0].items.length, 3); + assert.strictEqual(notebook.apiNotebook.cellAt(1).outputs[0].items[0].mime, 'text/plain'); + assert.strictEqual(notebook.apiNotebook.cellAt(1).outputs[0].items[1].mime, 'application/vnd.code.notebook.stdout'); + assert.strictEqual(notebook.apiNotebook.cellAt(1).outputs[0].items[2].mime, 'application/vnd.code.notebook.stdout'); + }); + test('Compress multiple stdout stream output items', async function () { + await replaceOutputs(1, '1', [{ mime: 'application/vnd.code.notebook.stdout', valueBytes: VSBuffer.fromString('foo') }]); + await appendOutputItem(1, '1', [{ mime: 'application/vnd.code.notebook.stdout', valueBytes: VSBuffer.fromString('bar') }]); + await appendOutputItem(1, '1', [{ mime: 'application/vnd.code.notebook.stdout', valueBytes: VSBuffer.fromString('baz') }]); + + assert.strictEqual(notebook.apiNotebook.cellAt(1).outputs.length, 1); + assert.strictEqual(notebook.apiNotebook.cellAt(1).outputs[0].items.length, 1); + assert.strictEqual(notebook.apiNotebook.cellAt(1).outputs[0].items[0].mime, 'application/vnd.code.notebook.stdout'); + assert.strictEqual(VSBuffer.wrap(notebook.apiNotebook.cellAt(1).outputs[0].items[0].data).toString(), 'foobarbaz'); + }); + test('Compress multiple stdout stream output items (with support for terminal escape code -> \u001b[A)', async function () { + await replaceOutputs(1, '1', [{ mime: 'application/vnd.code.notebook.stdout', valueBytes: VSBuffer.fromString('\nfoo') }]); + await appendOutputItem(1, '1', [{ mime: 'application/vnd.code.notebook.stdout', valueBytes: VSBuffer.fromString(`${String.fromCharCode(27)}[Abar`) }]); + + assert.strictEqual(notebook.apiNotebook.cellAt(1).outputs.length, 1); + assert.strictEqual(notebook.apiNotebook.cellAt(1).outputs[0].items.length, 1); + assert.strictEqual(notebook.apiNotebook.cellAt(1).outputs[0].items[0].mime, 'application/vnd.code.notebook.stdout'); + assert.strictEqual(VSBuffer.wrap(notebook.apiNotebook.cellAt(1).outputs[0].items[0].data).toString(), 'bar'); + }); + test('Compress multiple stdout stream output items (with support for terminal escape code -> \r character)', async function () { + await replaceOutputs(1, '1', [{ mime: 'application/vnd.code.notebook.stdout', valueBytes: VSBuffer.fromString('foo') }]); + await appendOutputItem(1, '1', [{ mime: 'application/vnd.code.notebook.stdout', valueBytes: VSBuffer.fromString(`\rbar`) }]); + + assert.strictEqual(notebook.apiNotebook.cellAt(1).outputs.length, 1); + assert.strictEqual(notebook.apiNotebook.cellAt(1).outputs[0].items.length, 1); + assert.strictEqual(notebook.apiNotebook.cellAt(1).outputs[0].items[0].mime, 'application/vnd.code.notebook.stdout'); + assert.strictEqual(VSBuffer.wrap(notebook.apiNotebook.cellAt(1).outputs[0].items[0].data).toString(), 'bar'); + }); + test('Compress multiple stderr stream output items', async function () { + await replaceOutputs(1, '1', [{ mime: 'application/vnd.code.notebook.stderr', valueBytes: VSBuffer.fromString('foo') }]); + await appendOutputItem(1, '1', [{ mime: 'application/vnd.code.notebook.stderr', valueBytes: VSBuffer.fromString('bar') }]); + await appendOutputItem(1, '1', [{ mime: 'application/vnd.code.notebook.stderr', valueBytes: VSBuffer.fromString('baz') }]); + + assert.strictEqual(notebook.apiNotebook.cellAt(1).outputs.length, 1); + assert.strictEqual(notebook.apiNotebook.cellAt(1).outputs[0].items.length, 1); + assert.strictEqual(notebook.apiNotebook.cellAt(1).outputs[0].items[0].mime, 'application/vnd.code.notebook.stderr'); + assert.strictEqual(VSBuffer.wrap(notebook.apiNotebook.cellAt(1).outputs[0].items[0].data).toString(), 'foobarbaz'); + }); }); diff --git a/src/vs/workbench/api/test/browser/extHostTelemetry.test.ts b/src/vs/workbench/api/test/browser/extHostTelemetry.test.ts new file mode 100644 index 00000000000..2153a61f480 --- /dev/null +++ b/src/vs/workbench/api/test/browser/extHostTelemetry.test.ts @@ -0,0 +1,145 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { URI } from 'vs/base/common/uri'; +import { ExtensionIdentifier, IExtensionDescription, TargetPlatform } from 'vs/platform/extensions/common/extensions'; +import { DEFAULT_LOG_LEVEL } from 'vs/platform/log/common/log'; +import { ITelemetryInfo, TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; +import { TestTelemetryLoggerService } from 'vs/platform/telemetry/test/common/telemetryLogAppender.test'; +import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; +import { ExtHostTelemetry } from 'vs/workbench/api/common/extHostTelemetry'; +import { IEnvironment } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; +import { mock } from 'vs/workbench/test/common/workbenchTestServices'; +import type { TelemetryAppender } from 'vscode'; + +suite('ExtHostTelemetry', function () { + + const mockEnvironment: IEnvironment = { + isExtensionDevelopmentDebug: false, + extensionDevelopmentLocationURI: undefined, + extensionTestsLocationURI: undefined, + appRoot: undefined, + appName: 'test', + extensionTelemetryLogResource: URI.parse('fake'), + appHost: 'test', + appLanguage: 'en', + globalStorageHome: URI.parse('fake'), + workspaceStorageHome: URI.parse('fake'), + appUriScheme: 'test', + }; + + const mockTelemetryInfo: ITelemetryInfo = { + firstSessionDate: '2020-01-01T00:00:00.000Z', + sessionId: 'test', + machineId: 'test', + }; + + const mockRemote = { + authority: 'test', + isRemote: false, + connectionData: null + }; + + const mockExtensionIdentifier: IExtensionDescription = { + identifier: new ExtensionIdentifier('test-extension'), + targetPlatform: TargetPlatform.UNIVERSAL, + isBuiltin: true, + isUserBuiltin: true, + isUnderDevelopment: true, + name: 'test-extension', + publisher: 'vscode', + version: '1.0.0', + engines: { vscode: '*' }, + extensionLocation: URI.parse('fake') + }; + + test('Ensure logger gets proper telemetry level during initialization', function () { + const extensionTelemetry = new ExtHostTelemetry(new class extends mock() { + override environment: IEnvironment = mockEnvironment; + override telemetryInfo: ITelemetryInfo = mockTelemetryInfo; + override remote = mockRemote; + }, new TestTelemetryLoggerService(DEFAULT_LOG_LEVEL)); + extensionTelemetry.$initializeTelemetryLevel(TelemetryLevel.USAGE, { usage: true, error: true }); + let config = extensionTelemetry.getTelemetryDetails(); + assert.strictEqual(config.isCrashEnabled, true); + assert.strictEqual(config.isUsageEnabled, true); + assert.strictEqual(config.isErrorsEnabled, true); + + // Initialize would never be called twice, but this is just for testing + extensionTelemetry.$initializeTelemetryLevel(TelemetryLevel.ERROR, { usage: true, error: true }); + config = extensionTelemetry.getTelemetryDetails(); + assert.strictEqual(config.isCrashEnabled, true); + assert.strictEqual(config.isUsageEnabled, false); + assert.strictEqual(config.isErrorsEnabled, true); + + extensionTelemetry.$initializeTelemetryLevel(TelemetryLevel.CRASH, { usage: true, error: true }); + config = extensionTelemetry.getTelemetryDetails(); + assert.strictEqual(config.isCrashEnabled, true); + assert.strictEqual(config.isUsageEnabled, false); + assert.strictEqual(config.isErrorsEnabled, false); + + extensionTelemetry.$initializeTelemetryLevel(TelemetryLevel.USAGE, { usage: false, error: true }); + config = extensionTelemetry.getTelemetryDetails(); + assert.strictEqual(config.isCrashEnabled, true); + assert.strictEqual(config.isUsageEnabled, false); + assert.strictEqual(config.isErrorsEnabled, true); + }); + + test('Simple log event to TelemetryLogger', function () { + const sentData: any[] = []; + const sentExceptions: any[] = []; + let flushCalled = false; + + // This is the appender which the extension would contribute + const appender: TelemetryAppender = { + logEvent: (eventName: string, data) => { + sentData.push({ eventName, data }); + }, + logException: (exception, data) => { + sentExceptions.push({ exception, data }); + }, + ignoreBuiltInCommonProperties: false, + flush: () => { + flushCalled = true; + } + }; + + const extensionTelemetry = new ExtHostTelemetry(new class extends mock() { + override environment: IEnvironment = mockEnvironment; + override telemetryInfo: ITelemetryInfo = mockTelemetryInfo; + override remote = mockRemote; + }, new TestTelemetryLoggerService(DEFAULT_LOG_LEVEL)); + + extensionTelemetry.$initializeTelemetryLevel(TelemetryLevel.USAGE, { usage: true, error: true }); + + // Create the logger usting the appender + const logger = extensionTelemetry.instantiateLogger(mockExtensionIdentifier, appender); + + logger.logUsage('test-event', { 'test-data': 'test-data' }); + assert.strictEqual(sentData.length, 1); + assert.strictEqual(sentData[0].eventName, `${mockExtensionIdentifier.name}/test-event`); + assert.strictEqual(sentData[0].data['test-data'], 'test-data'); + + logger.logUsage('test-event', { 'test-data': 'test-data' }); + assert.strictEqual(sentData.length, 2); + + logger.logError('test-event', { 'test-data': 'test-data' }); + assert.strictEqual(sentData.length, 3); + + logger.logError(new Error('test-error'), { 'test-data': 'test-data' }); + assert.strictEqual(sentData.length, 3); + assert.strictEqual(sentExceptions.length, 1); + + + // Assert not flushed + assert.strictEqual(flushCalled, false); + + // Call flush and assert that flush occurs + logger.dispose(); + assert.strictEqual(flushCalled, true); + + }); +}); diff --git a/src/vs/workbench/api/test/browser/extHostTesting.test.ts b/src/vs/workbench/api/test/browser/extHostTesting.test.ts index dad5ae26397..80645717880 100644 --- a/src/vs/workbench/api/test/browser/extHostTesting.test.ts +++ b/src/vs/workbench/api/test/browser/extHostTesting.test.ts @@ -102,19 +102,19 @@ suite('ExtHost Testing', () => { assert.deepStrictEqual(single.collectDiff(), [ { op: TestDiffOpType.Add, - item: { controllerId: 'ctrlId', parent: null, expand: TestItemExpandState.BusyExpanding, item: { ...convert.TestItem.from(single.root) } } + item: { controllerId: 'ctrlId', expand: TestItemExpandState.BusyExpanding, item: { ...convert.TestItem.from(single.root) } } }, { op: TestDiffOpType.Add, - item: { controllerId: 'ctrlId', parent: single.root.id, expand: TestItemExpandState.BusyExpanding, item: { ...convert.TestItem.from(a) } } + item: { controllerId: 'ctrlId', expand: TestItemExpandState.BusyExpanding, item: { ...convert.TestItem.from(a) } } }, { op: TestDiffOpType.Add, - item: { controllerId: 'ctrlId', parent: new TestId(['ctrlId', 'id-a']).toString(), expand: TestItemExpandState.NotExpandable, item: convert.TestItem.from(a.children.get('id-aa') as TestItemImpl) } + item: { controllerId: 'ctrlId', expand: TestItemExpandState.NotExpandable, item: convert.TestItem.from(a.children.get('id-aa') as TestItemImpl) } }, { op: TestDiffOpType.Add, - item: { controllerId: 'ctrlId', parent: new TestId(['ctrlId', 'id-a']).toString(), expand: TestItemExpandState.NotExpandable, item: convert.TestItem.from(a.children.get('id-ab') as TestItemImpl) } + item: { controllerId: 'ctrlId', expand: TestItemExpandState.NotExpandable, item: convert.TestItem.from(a.children.get('id-ab') as TestItemImpl) } }, { op: TestDiffOpType.Update, @@ -122,7 +122,7 @@ suite('ExtHost Testing', () => { }, { op: TestDiffOpType.Add, - item: { controllerId: 'ctrlId', parent: single.root.id, expand: TestItemExpandState.NotExpandable, item: convert.TestItem.from(b) } + item: { controllerId: 'ctrlId', expand: TestItemExpandState.NotExpandable, item: convert.TestItem.from(b) } }, { op: TestDiffOpType.Update, @@ -141,6 +141,19 @@ suite('ExtHost Testing', () => { assert.strictEqual(ab.parent, a); }); + test('can add an item with same ID as root', () => { + single.collectDiff(); + + const child = new TestItemImpl('ctrlId', 'ctrlId', 'c', undefined); + single.root.children.add(child); + assert.deepStrictEqual(single.collectDiff(), [ + { + op: TestDiffOpType.Add, + item: { controllerId: 'ctrlId', expand: TestItemExpandState.NotExpandable, item: convert.TestItem.from(child) }, + } + ]); + }); + test('no-ops if items not changed', () => { single.collectDiff(); assert.deepStrictEqual(single.collectDiff(), []); @@ -184,7 +197,6 @@ suite('ExtHost Testing', () => { { op: TestDiffOpType.Add, item: { controllerId: 'ctrlId', - parent: new TestId(['ctrlId', 'id-a']).toString(), expand: TestItemExpandState.NotExpandable, item: convert.TestItem.from(child), } @@ -213,7 +225,6 @@ suite('ExtHost Testing', () => { { op: TestDiffOpType.Add, item: { controllerId: 'ctrlId', - parent: new TestId(['ctrlId', 'id-a']).toString(), expand: TestItemExpandState.NotExpandable, item: convert.TestItem.from(child), } @@ -326,7 +337,7 @@ suite('ExtHost Testing', () => { }, { op: TestDiffOpType.Add, - item: { controllerId: 'ctrlId', parent: new TestId(['ctrlId', 'id-a']).toString(), expand: TestItemExpandState.NotExpandable, item: convert.TestItem.from(b) } + item: { controllerId: 'ctrlId', expand: TestItemExpandState.NotExpandable, item: convert.TestItem.from(b) } }, ]); diff --git a/src/vs/workbench/browser/actions/developerActions.ts b/src/vs/workbench/browser/actions/developerActions.ts index 5a0d08a8623..9de0788c23f 100644 --- a/src/vs/workbench/browser/actions/developerActions.ts +++ b/src/vs/workbench/browser/actions/developerActions.ts @@ -27,7 +27,7 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'v import { ILogService } from 'vs/platform/log/common/log'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { IWorkingCopyBackupService } from 'vs/workbench/services/workingCopy/common/workingCopyBackup'; class InspectContextKeysAction extends Action2 { @@ -36,7 +36,7 @@ class InspectContextKeysAction extends Action2 { super({ id: 'workbench.action.inspectContextKeys', title: { value: localize('inspect context keys', "Inspect Context Keys"), original: 'Inspect Context Keys' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true }); } @@ -96,7 +96,7 @@ class ToggleScreencastModeAction extends Action2 { super({ id: 'workbench.action.toggleScreencastMode', title: { value: localize('toggle screencast mode', "Toggle Screencast Mode"), original: 'Toggle Screencast Mode' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true }); } @@ -267,7 +267,7 @@ class LogStorageAction extends Action2 { super({ id: 'workbench.action.logStorage', title: { value: localize({ key: 'logStorage', comment: ['A developer only action to log the contents of the storage for the current window.'] }, "Log Storage Database Contents"), original: 'Log Storage Database Contents' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true }); } @@ -283,7 +283,7 @@ class LogWorkingCopiesAction extends Action2 { super({ id: 'workbench.action.logWorkingCopies', title: { value: localize({ key: 'logWorkingCopies', comment: ['A developer only action to log the working copies that exist.'] }, "Log Working Copies"), original: 'Log Working Copies' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true }); } diff --git a/src/vs/workbench/browser/actions/helpActions.ts b/src/vs/workbench/browser/actions/helpActions.ts index 227492a8fce..d05f5a9e391 100644 --- a/src/vs/workbench/browser/actions/helpActions.ts +++ b/src/vs/workbench/browser/actions/helpActions.ts @@ -14,7 +14,7 @@ import { KeyChord, KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { IProductService } from 'vs/platform/product/common/productService'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; class KeybindingsReferenceAction extends Action2 { @@ -29,7 +29,7 @@ class KeybindingsReferenceAction extends Action2 { mnemonicTitle: localize({ key: 'miKeyboardShortcuts', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts Reference"), original: 'Keyboard Shortcuts Reference' }, - category: CATEGORIES.Help, + category: Categories.Help, f1: true, keybinding: { weight: KeybindingWeight.WorkbenchContrib, @@ -68,7 +68,7 @@ class OpenIntroductoryVideosUrlAction extends Action2 { mnemonicTitle: localize({ key: 'miVideoTutorials', comment: ['&& denotes a mnemonic'] }, "&&Video Tutorials"), original: 'Video Tutorials' }, - category: CATEGORIES.Help, + category: Categories.Help, f1: true, menu: { id: MenuId.MenubarHelpMenu, @@ -101,7 +101,7 @@ class OpenTipsAndTricksUrlAction extends Action2 { mnemonicTitle: localize({ key: 'miTipsAndTricks', comment: ['&& denotes a mnemonic'] }, "Tips and Tri&&cks"), original: 'Tips and Tricks' }, - category: CATEGORIES.Help, + category: Categories.Help, f1: true, menu: { id: MenuId.MenubarHelpMenu, @@ -134,7 +134,7 @@ class OpenDocumentationUrlAction extends Action2 { mnemonicTitle: localize({ key: 'miDocumentation', comment: ['&& denotes a mnemonic'] }, "&&Documentation"), original: 'Documentation' }, - category: CATEGORIES.Help, + category: Categories.Help, f1: true, menu: { id: MenuId.MenubarHelpMenu, @@ -163,7 +163,7 @@ class OpenNewsletterSignupUrlAction extends Action2 { super({ id: OpenNewsletterSignupUrlAction.ID, title: { value: localize('newsletterSignup', "Signup for the VS Code Newsletter"), original: 'Signup for the VS Code Newsletter' }, - category: CATEGORIES.Help, + category: Categories.Help, f1: true }); } @@ -192,7 +192,7 @@ class OpenTwitterUrlAction extends Action2 { mnemonicTitle: localize({ key: 'miTwitter', comment: ['&& denotes a mnemonic'] }, "&&Join Us on Twitter"), original: 'Join Us on Twitter' }, - category: CATEGORIES.Help, + category: Categories.Help, f1: true, menu: { id: MenuId.MenubarHelpMenu, @@ -225,7 +225,7 @@ class OpenRequestFeatureUrlAction extends Action2 { mnemonicTitle: localize({ key: 'miUserVoice', comment: ['&& denotes a mnemonic'] }, "&&Search Feature Requests"), original: 'Search Feature Requests' }, - category: CATEGORIES.Help, + category: Categories.Help, f1: true, menu: { id: MenuId.MenubarHelpMenu, @@ -258,7 +258,7 @@ class OpenLicenseUrlAction extends Action2 { mnemonicTitle: localize({ key: 'miLicense', comment: ['&& denotes a mnemonic'] }, "View &&License"), original: 'View License' }, - category: CATEGORIES.Help, + category: Categories.Help, f1: true, menu: { id: MenuId.MenubarHelpMenu, @@ -296,7 +296,7 @@ class OpenPrivacyStatementUrlAction extends Action2 { mnemonicTitle: localize({ key: 'miPrivacyStatement', comment: ['&& denotes a mnemonic'] }, "Privac&&y Statement"), original: 'Privacy Statement' }, - category: CATEGORIES.Help, + category: Categories.Help, f1: true, menu: { id: MenuId.MenubarHelpMenu, diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index 89651caa991..61f8d64c69f 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -6,7 +6,7 @@ import { localize } from 'vs/nls'; import Severity from 'vs/base/common/severity'; import { MenuId, MenuRegistry, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchLayoutService, Parts, Position, positionToString } from 'vs/workbench/services/layout/browser/layoutService'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -58,7 +58,7 @@ registerAction2(class extends Action2 { super({ id: 'workbench.action.closeSidebar', title: { value: localize('closeSidebar', "Close Primary Side Bar"), original: 'Close Primary Side Bar' }, - category: CATEGORIES.View, + category: Categories.View, f1: true }); } @@ -84,7 +84,7 @@ export class ToggleActivityBarVisibilityAction extends Action2 { mnemonicTitle: localize({ key: 'miActivityBar', comment: ['&& denotes a mnemonic'] }, "&&Activity Bar"), original: 'Toggle Activity Bar Visibility' }, - category: CATEGORIES.View, + category: Categories.View, f1: true, toggled: ContextKeyExpr.equals('config.workbench.activityBar.visible', true), menu: [{ @@ -120,7 +120,7 @@ registerAction2(class extends Action2 { mnemonicTitle: localize({ key: 'miToggleCenteredLayout', comment: ['&& denotes a mnemonic'] }, "&&Centered Layout"), original: 'Toggle Centered Layout' }, - category: CATEGORIES.View, + category: Categories.View, f1: true, toggled: IsCenteredLayoutContext, menu: [{ @@ -201,7 +201,7 @@ export class ToggleSidebarPositionAction extends Action2 { super({ id: ToggleSidebarPositionAction.ID, title: { value: localize('toggleSidebarPosition', "Toggle Primary Side Bar Position"), original: 'Toggle Primary Side Bar Position' }, - category: CATEGORIES.View, + category: Categories.View, f1: true }); } @@ -329,7 +329,7 @@ registerAction2(class extends Action2 { mnemonicTitle: localize({ key: 'miShowEditorArea', comment: ['&& denotes a mnemonic'] }, "Show &&Editor Area"), original: 'Toggle Editor Area Visibility' }, - category: CATEGORIES.View, + category: Categories.View, f1: true, toggled: EditorAreaVisibleContext, // the workbench grid currently prevents us from supporting panel maximization with non-center panel alignment @@ -359,7 +359,7 @@ class ToggleSidebarVisibilityAction extends Action2 { super({ id: ToggleSidebarVisibilityAction.ID, title: { value: localize('toggleSidebar', "Toggle Primary Side Bar Visibility"), original: 'Toggle Primary Side Bar Visibility' }, - category: CATEGORIES.View, + category: Categories.View, f1: true, keybinding: { weight: KeybindingWeight.WorkbenchContrib, @@ -467,7 +467,7 @@ export class ToggleStatusbarVisibilityAction extends Action2 { mnemonicTitle: localize({ key: 'miStatusbar', comment: ['&& denotes a mnemonic'] }, "S&&tatus Bar"), original: 'Toggle Status Bar Visibility' }, - category: CATEGORIES.View, + category: Categories.View, f1: true, toggled: ContextKeyExpr.equals('config.workbench.statusBar.visible', true), menu: [{ @@ -502,7 +502,7 @@ registerAction2(class extends Action2 { value: localize('toggleTabs', "Toggle Tab Visibility"), original: 'Toggle Tab Visibility' }, - category: CATEGORIES.View, + category: Categories.View, f1: true }); } @@ -529,7 +529,7 @@ registerAction2(class extends Action2 { mnemonicTitle: localize('miToggleZenMode', "Zen Mode"), original: 'Toggle Zen Mode' }, - category: CATEGORIES.View, + category: Categories.View, f1: true, keybinding: { weight: KeybindingWeight.WorkbenchContrib, @@ -576,7 +576,7 @@ if (isWindows || isLinux || isWeb) { mnemonicTitle: localize({ key: 'miMenuBar', comment: ['&& denotes a mnemonic'] }, "Menu &&Bar"), original: 'Toggle Menu Bar' }, - category: CATEGORIES.View, + category: Categories.View, f1: true, toggled: ContextKeyExpr.and(IsMacNativeContext.toNegated(), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'hidden'), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'toggle'), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'compact')), menu: [{ @@ -614,7 +614,7 @@ registerAction2(class extends Action2 { value: localize('resetViewLocations', "Reset View Locations"), original: 'Reset View Locations' }, - category: CATEGORIES.View, + category: Categories.View, f1: true }); } @@ -635,7 +635,7 @@ registerAction2(class extends Action2 { value: localize('moveView', "Move View"), original: 'Move View' }, - category: CATEGORIES.View, + category: Categories.View, f1: true }); } @@ -780,7 +780,7 @@ class MoveFocusedViewAction extends Action2 { value: localize('moveFocusedView', "Move Focused View"), original: 'Move Focused View' }, - category: CATEGORIES.View, + category: Categories.View, precondition: FocusedViewContext.notEqualsTo(''), f1: true }); @@ -939,7 +939,7 @@ registerAction2(class extends Action2 { value: localize('resetFocusedViewLocation', "Reset Focused View Location"), original: 'Reset Focused View Location' }, - category: CATEGORIES.View, + category: Categories.View, f1: true, precondition: FocusedViewContext.notEqualsTo('') }); diff --git a/src/vs/workbench/browser/actions/navigationActions.ts b/src/vs/workbench/browser/actions/navigationActions.ts index 86fca131e3f..9c3c8e0b1c1 100644 --- a/src/vs/workbench/browser/actions/navigationActions.ts +++ b/src/vs/workbench/browser/actions/navigationActions.ts @@ -9,7 +9,8 @@ import { Action } from 'vs/base/common/actions'; import { IEditorGroupsService, GroupDirection, GroupLocation, IFindGroupScope } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { IWorkbenchActionRegistry, Extensions, CATEGORIES } from 'vs/workbench/common/actions'; +import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { Direction } from 'vs/base/browser/ui/grid/grid'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -312,9 +313,9 @@ export class FocusPreviousPart extends Action { const actionsRegistry = Registry.as(Extensions.WorkbenchActions); -actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateUpAction, undefined), 'View: Navigate to the View Above', CATEGORIES.View.value); -actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateDownAction, undefined), 'View: Navigate to the View Below', CATEGORIES.View.value); -actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateLeftAction, undefined), 'View: Navigate to the View on the Left', CATEGORIES.View.value); -actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateRightAction, undefined), 'View: Navigate to the View on the Right', CATEGORIES.View.value); -actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(FocusNextPart, { primary: KeyCode.F6 }), 'View: Focus Next Part', CATEGORIES.View.value); -actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(FocusPreviousPart, { primary: KeyMod.Shift | KeyCode.F6 }), 'View: Focus Previous Part', CATEGORIES.View.value); +actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateUpAction, undefined), 'View: Navigate to the View Above', Categories.View.value); +actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateDownAction, undefined), 'View: Navigate to the View Below', Categories.View.value); +actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateLeftAction, undefined), 'View: Navigate to the View on the Left', Categories.View.value); +actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateRightAction, undefined), 'View: Navigate to the View on the Right', Categories.View.value); +actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(FocusNextPart, { primary: KeyCode.F6 }), 'View: Focus Next Part', Categories.View.value); +actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(FocusPreviousPart, { primary: KeyMod.Shift | KeyCode.F6 }), 'View: Focus Previous Part', Categories.View.value); diff --git a/src/vs/workbench/browser/actions/quickAccessActions.ts b/src/vs/workbench/browser/actions/quickAccessActions.ts index 6cda33c142c..ba895c00fd6 100644 --- a/src/vs/workbench/browser/actions/quickAccessActions.ts +++ b/src/vs/workbench/browser/actions/quickAccessActions.ts @@ -13,6 +13,7 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { inQuickPickContext, defaultQuickAccessContext, getQuickNavigateHandler } from 'vs/workbench/browser/quickaccess'; import { ILocalizedString } from 'vs/platform/action/common/action'; +import { AnythingQuickAccessProviderRunOptions } from 'vs/platform/quickinput/common/quickAccess'; //#region Quick access management commands and keys @@ -139,6 +140,24 @@ registerAction2(class QuickAccessAction extends Action2 { secondary: globalQuickAccessKeybinding.secondary, mac: globalQuickAccessKeybinding.mac }, + f1: true + }); + } + + run(accessor: ServicesAccessor, prefix: undefined): void { + const quickInputService = accessor.get(IQuickInputService); + quickInputService.quickAccess.show(typeof prefix === 'string' ? prefix : undefined, { preserveValue: typeof prefix === 'string' /* preserve as is if provided */ }); + } +}); + +registerAction2(class QuickAccessAction extends Action2 { + constructor() { + super({ + id: 'workbench.action.quickOpenWithModes', + title: { + value: localize('quickOpenWithModes', "Launch Command Center"), + original: 'Launch Command Center' + }, f1: true, menu: { id: MenuId.CommandCenter, @@ -147,9 +166,13 @@ registerAction2(class QuickAccessAction extends Action2 { }); } - run(accessor: ServicesAccessor, prefix: undefined): void { + run(accessor: ServicesAccessor): void { const quickInputService = accessor.get(IQuickInputService); - quickInputService.quickAccess.show(typeof prefix === 'string' ? prefix : undefined, { preserveValue: typeof prefix === 'string' /* preserve as is if provided */ }); + quickInputService.quickAccess.show(undefined, { + providerOptions: { + includeHelp: true, + } as AnythingQuickAccessProviderRunOptions + }); } }); diff --git a/src/vs/workbench/browser/actions/textInputActions.ts b/src/vs/workbench/browser/actions/textInputActions.ts index 993f595d69e..60dad795312 100644 --- a/src/vs/workbench/browser/actions/textInputActions.ts +++ b/src/vs/workbench/browser/actions/textInputActions.ts @@ -99,4 +99,4 @@ export class TextInputActionsProvider extends Disposable implements IWorkbenchCo } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TextInputActionsProvider, 'TextInputActionsProvider', LifecyclePhase.Ready); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TextInputActionsProvider, LifecyclePhase.Ready); diff --git a/src/vs/workbench/browser/actions/windowActions.ts b/src/vs/workbench/browser/actions/windowActions.ts index 8858f514ec5..eaa2db307dd 100644 --- a/src/vs/workbench/browser/actions/windowActions.ts +++ b/src/vs/workbench/browser/actions/windowActions.ts @@ -10,7 +10,7 @@ import { MenuRegistry, MenuId, Action2, registerAction2, IAction2Options } from import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { IsFullscreenContext } from 'vs/workbench/common/contextkeys'; import { IsMacNativeContext, IsDevelopmentContext, IsWebContext, IsIOSContext } from 'vs/platform/contextkey/common/contextkeys'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IQuickInputButton, IQuickInputService, IQuickPickSeparator, IKeyMods, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { IWorkspaceContextService, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; @@ -289,7 +289,7 @@ class ToggleFullScreenAction extends Action2 { mnemonicTitle: localize({ key: 'miToggleFullScreen', comment: ['&& denotes a mnemonic'] }, "&&Full Screen"), original: 'Toggle Full Screen' }, - category: CATEGORIES.View, + category: Categories.View, f1: true, keybinding: { weight: KeybindingWeight.WorkbenchContrib, @@ -323,7 +323,7 @@ export class ReloadWindowAction extends Action2 { super({ id: ReloadWindowAction.ID, title: { value: localize('reloadWindow', "Reload Window"), original: 'Reload Window' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true, keybinding: { weight: KeybindingWeight.WorkbenchContrib + 50, @@ -350,7 +350,7 @@ class ShowAboutDialogAction extends Action2 { mnemonicTitle: localize({ key: 'miAbout', comment: ['&& denotes a mnemonic'] }, "&&About"), original: 'About' }, - category: CATEGORIES.Help, + category: Categories.Help, f1: true, menu: { id: MenuId.MenubarHelpMenu, diff --git a/src/vs/workbench/browser/codeeditor.ts b/src/vs/workbench/browser/codeeditor.ts index 00be0866e3e..3a77af1f995 100644 --- a/src/vs/workbench/browser/codeeditor.ts +++ b/src/vs/workbench/browser/codeeditor.ts @@ -26,6 +26,7 @@ import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IAction } from 'vs/base/common/actions'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; export interface IRangeHighlightDecoration { resource: URI; @@ -224,28 +225,31 @@ export class FloatingClickMenu extends Disposable implements IEditorContribution ) { super(); - const menu = menuService.createMenu(MenuId.EditorContent, contextKeyService); - const menuDisposables = new DisposableStore(); - const renderMenuAsFloatingClickBtn = () => { - menuDisposables.clear(); - if (!editor.hasModel() || editor.getOption(EditorOption.inDiffEditor)) { - return; - } - const actions: IAction[] = []; - createAndFillInActionBarActions(menu, { renderShortTitle: true, shouldForwardArgs: true }, actions); - if (actions.length === 0) { - return; - } - // todo@jrieken find a way to handle N actions, like showing a context menu - const [first] = actions; - const widget = instantiationService.createInstance(FloatingClickWidget, editor, first.label, first.id); - menuDisposables.add(widget); - menuDisposables.add(widget.onClick(() => first.run(editor.getModel().uri))); - widget.render(); - }; - this._store.add(menu); - this._store.add(menuDisposables); - this._store.add(menu.onDidChange(renderMenuAsFloatingClickBtn)); - renderMenuAsFloatingClickBtn(); + // DISABLED for embedded editors. In the future we can use a different MenuId for embedded editors + if (!(editor instanceof EmbeddedCodeEditorWidget)) { + const menu = menuService.createMenu(MenuId.EditorContent, contextKeyService); + const menuDisposables = new DisposableStore(); + const renderMenuAsFloatingClickBtn = () => { + menuDisposables.clear(); + if (!editor.hasModel() || editor.getOption(EditorOption.inDiffEditor)) { + return; + } + const actions: IAction[] = []; + createAndFillInActionBarActions(menu, { renderShortTitle: true, shouldForwardArgs: true }, actions); + if (actions.length === 0) { + return; + } + // todo@jrieken find a way to handle N actions, like showing a context menu + const [first] = actions; + const widget = instantiationService.createInstance(FloatingClickWidget, editor, first.label, first.id); + menuDisposables.add(widget); + menuDisposables.add(widget.onClick(() => first.run(editor.getModel().uri))); + widget.render(); + }; + this._store.add(menu); + this._store.add(menuDisposables); + this._store.add(menu.onDidChange(renderMenuAsFloatingClickBtn)); + renderMenuAsFloatingClickBtn(); + } } } diff --git a/src/vs/workbench/browser/composite.ts b/src/vs/workbench/browser/composite.ts index a619fe25a1f..ca2f9cdf638 100644 --- a/src/vs/workbench/browser/composite.ts +++ b/src/vs/workbench/browser/composite.ts @@ -15,6 +15,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { Disposable } from 'vs/base/common/lifecycle'; import { assertIsDefined } from 'vs/base/common/types'; import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { MenuId } from 'vs/platform/actions/common/actions'; /** * Composites are layed out in the sidebar and panel part of the workbench. At a time only one composite @@ -161,6 +162,15 @@ export abstract class Composite extends Component implements IComposite { super.updateStyles(); } + + /** + * + * @returns the action runner for this composite + */ + getMenuIds(): readonly MenuId[] { + return []; + } + /** * Returns an array of actions to show in the action bar of the composite. */ diff --git a/src/vs/workbench/browser/contextkeys.ts b/src/vs/workbench/browser/contextkeys.ts index 2ac8feeba1f..08c286d1e84 100644 --- a/src/vs/workbench/browser/contextkeys.ts +++ b/src/vs/workbench/browser/contextkeys.ts @@ -6,7 +6,7 @@ import { Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext, ProductQualityContext } from 'vs/platform/contextkey/common/contextkeys'; +import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext, ProductQualityContext, IsMobileContext } from 'vs/platform/contextkey/common/contextkeys'; import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext } from 'vs/workbench/common/contextkeys'; import { TEXT_DIFF_EDITOR_ID, EditorInputCapabilities, SIDE_BY_SIDE_EDITOR_ID, DEFAULT_EDITOR_ASSOCIATION } from 'vs/workbench/common/editor'; import { trackFocus, addDisposableListener, EventType } from 'vs/base/browser/dom'; @@ -96,6 +96,7 @@ export class WorkbenchContextKeysHandler extends Disposable { IsWebContext.bindTo(this.contextKeyService); IsMacNativeContext.bindTo(this.contextKeyService); IsIOSContext.bindTo(this.contextKeyService); + IsMobileContext.bindTo(this.contextKeyService); RemoteNameContext.bindTo(this.contextKeyService).set(getRemoteName(this.environmentService.remoteAuthority) || ''); diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index ea93a14b908..08a205b6ffa 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -161,9 +161,13 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi get offset() { let top = 0; let quickPickTop = 0; + if (this.isVisible(Parts.BANNER_PART)) { + top = this.getPart(Parts.BANNER_PART).maximumHeight; + quickPickTop = top; + } if (this.isVisible(Parts.TITLEBAR_PART)) { - top = this.getPart(Parts.TITLEBAR_PART).maximumHeight; - quickPickTop = this.titleService.isCommandCenterVisible ? 0 : top; + top += this.getPart(Parts.TITLEBAR_PART).maximumHeight; + quickPickTop = this.titleService.isCommandCenterVisible ? quickPickTop : top; } return { top, quickPickTop }; } @@ -1019,6 +1023,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi return !this.stateModel.getRuntimeValue(LayoutStateKeys.ACTIVITYBAR_HIDDEN); case Parts.EDITOR_PART: return !this.stateModel.getRuntimeValue(LayoutStateKeys.EDITOR_HIDDEN); + case Parts.BANNER_PART: + return this.workbenchGrid.isViewVisible(this.bannerPartView); default: return false; // any other part cannot be hidden } @@ -1061,11 +1067,6 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi return true; } - // with the command center enabled, we should always show - if (this.configurationService.getValue('window.commandCenter')) { - return true; - } - // remaining behavior is based on menubar visibility switch (getMenuBarVisibility(this.configurationService)) { case 'classic': @@ -1306,7 +1307,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.workbenchGrid = workbenchGrid; this.workbenchGrid.edgeSnapping = this.state.runtime.fullscreen; - for (const part of [titleBar, editorPart, activityBar, panelPart, sideBar, statusBar, auxiliaryBarPart]) { + for (const part of [titleBar, editorPart, activityBar, panelPart, sideBar, statusBar, auxiliaryBarPart, bannerPart]) { this._register(part.onDidVisibilityChange((visible) => { if (part === sideBar) { this.setSideBarHidden(!visible, true); @@ -2101,6 +2102,21 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const activityBarWidth = this.activityBarPartView.minimumWidth; const middleSectionHeight = height - titleBarHeight - statusBarHeight; + const titleAndBanner: ISerializedNode[] = [ + { + type: 'leaf', + data: { type: Parts.TITLEBAR_PART }, + size: titleBarHeight, + visible: this.isVisible(Parts.TITLEBAR_PART) + }, + { + type: 'leaf', + data: { type: Parts.BANNER_PART }, + size: bannerHeight, + visible: false + } + ]; + const activityBarNode: ISerializedLeafNode = { type: 'leaf', data: { type: Parts.ACTIVITYBAR_PART }, @@ -2150,18 +2166,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi type: 'branch', size: width, data: [ - { - type: 'leaf', - data: { type: Parts.TITLEBAR_PART }, - size: titleBarHeight, - visible: this.isVisible(Parts.TITLEBAR_PART) - }, - { - type: 'leaf', - data: { type: Parts.BANNER_PART }, - size: bannerHeight, - visible: false - }, + ...(isWeb ? titleAndBanner.reverse() : titleAndBanner), { type: 'branch', data: middleSection, diff --git a/src/vs/workbench/browser/panecomposite.ts b/src/vs/workbench/browser/panecomposite.ts index 21230d84325..31e6e3ce2da 100644 --- a/src/vs/workbench/browser/panecomposite.ts +++ b/src/vs/workbench/browser/panecomposite.ts @@ -10,7 +10,7 @@ import { URI } from 'vs/base/common/uri'; import { Dimension } from 'vs/base/browser/dom'; import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IAction, Separator } from 'vs/base/common/actions'; -import { SubmenuItemAction } from 'vs/platform/actions/common/actions'; +import { MenuId, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -20,6 +20,7 @@ import { ViewPaneContainer, ViewsSubMenu } from 'vs/workbench/browser/parts/view import { IPaneComposite } from 'vs/workbench/common/panecomposite'; import { IView } from 'vs/workbench/common/views'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { VIEWPANE_FILTER_ACTION } from 'vs/workbench/browser/parts/views/viewPane'; export abstract class PaneComposite extends Composite implements IPaneComposite { @@ -73,12 +74,27 @@ export abstract class PaneComposite extends Composite implements IPaneComposite return this.viewPaneContainer?.menuActions?.getContextMenuActions() ?? []; } + override getMenuIds(): MenuId[] { + const result: MenuId[] = []; + if (this.viewPaneContainer?.menuActions) { + result.push(this.viewPaneContainer.menuActions.menuId); + if (this.viewPaneContainer.isViewMergedWithContainer()) { + result.push(this.viewPaneContainer.panes[0].menuActions.menuId); + } + } + return result; + } + override getActions(): readonly IAction[] { const result = []; if (this.viewPaneContainer?.menuActions) { result.push(...this.viewPaneContainer.menuActions.getPrimaryActions()); if (this.viewPaneContainer.isViewMergedWithContainer()) { - result.push(...this.viewPaneContainer.panes[0].menuActions.getPrimaryActions()); + const viewPane = this.viewPaneContainer.panes[0]; + if (viewPane.shouldShowFilterInHeader()) { + result.push(VIEWPANE_FILTER_ACTION); + } + result.push(...viewPane.menuActions.getPrimaryActions()); } } return result; diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index bee1974abd0..ce2a3e8fe51 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -17,9 +17,9 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { activeContrastBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry'; import { IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ActivityAction, ActivityActionViewItem, IActivityActionViewItemOptions, IActivityHoverOptions, ICompositeBar, ICompositeBarColors, ToggleCompositePinnedAction } from 'vs/workbench/browser/parts/compositeBarActions'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { IActivity } from 'vs/workbench/common/activity'; -import { ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_ACTIVE_BORDER, ACTIVITY_BAR_ACTIVE_FOCUS_BORDER, ACTIVITY_BAR_ACTIVE_BACKGROUND, ACTIVITY_BAR_SETTINGS_PROFILE_HOVER_FOREGROUND } from 'vs/workbench/common/theme'; +import { ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_ACTIVE_BORDER, ACTIVITY_BAR_ACTIVE_FOCUS_BORDER, ACTIVITY_BAR_ACTIVE_BACKGROUND, ACTIVITY_BAR_SETTINGS_PROFILE_BACKGROUND, ACTIVITY_BAR_SETTINGS_PROFILE_HOVER_FOREGROUND } from 'vs/workbench/common/theme'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; @@ -37,8 +37,7 @@ import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/b import { ViewContainerLocation } from 'vs/workbench/common/views'; import { IPaneCompositePart } from 'vs/workbench/browser/parts/paneCompositePart'; import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IUserDataProfileService, MANAGE_PROFILES_ACTION_ID, PROFILES_CATEGORY } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService, ManageProfilesSubMenu, PROFILES_CATEGORY } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; export class ViewContainerActivityAction extends ActivityAction { @@ -186,6 +185,7 @@ class MenuActivityActionViewItem extends AbstractGlobalActivityActionViewItem { private readonly menuId: MenuId, action: ActivityAction, contextMenuActionsProvider: () => IAction[], + icon: boolean, colors: (theme: IColorTheme) => ICompositeBarColors, hoverOptions: IActivityHoverOptions, @IThemeService themeService: IThemeService, @@ -197,7 +197,7 @@ class MenuActivityActionViewItem extends AbstractGlobalActivityActionViewItem { @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, @IKeybindingService keybindingService: IKeybindingService, ) { - super(action, contextMenuActionsProvider, { draggable: false, colors, icon: true, hasPopup: true, hoverOptions }, themeService, hoverService, menuService, contextMenuService, contextKeyService, configurationService, environmentService, keybindingService); + super(action, contextMenuActionsProvider, { draggable: false, colors, icon, hasPopup: true, hoverOptions }, themeService, hoverService, menuService, contextMenuService, contextKeyService, configurationService, environmentService, keybindingService); } protected async run(): Promise { @@ -245,7 +245,7 @@ export class AccountsActivityActionViewItem extends MenuActivityActionViewItem { @IKeybindingService keybindingService: IKeybindingService, @ICredentialsService private readonly credentialsService: ICredentialsService, ) { - super(MenuId.AccountsContext, action, contextMenuActionsProvider, colors, activityHoverOptions, themeService, hoverService, menuService, contextMenuService, contextKeyService, configurationService, environmentService, keybindingService); + super(MenuId.AccountsContext, action, contextMenuActionsProvider, true, colors, activityHoverOptions, themeService, hoverService, menuService, contextMenuService, contextKeyService, configurationService, environmentService, keybindingService); } protected override async resolveMainMenuActions(accountsMenu: IMenu, disposables: DisposableStore): Promise { @@ -340,7 +340,7 @@ export interface IProfileActivity extends IActivity { readonly icon: boolean; } -export class ProfilesActivityActionViewItem extends AbstractGlobalActivityActionViewItem { +export class ProfilesActivityActionViewItem extends MenuActivityActionViewItem { static readonly PROFILES_VISIBILITY_PREFERENCE_KEY = 'workbench.activity.showProfiles'; @@ -350,7 +350,6 @@ export class ProfilesActivityActionViewItem extends AbstractGlobalActivityAction colors: (theme: IColorTheme) => ICompositeBarColors, hoverOptions: IActivityHoverOptions, @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, - @ICommandService private readonly commandService: ICommandService, @IStorageService private readonly storageService: IStorageService, @IThemeService themeService: IThemeService, @IHoverService hoverService: IHoverService, @@ -361,18 +360,19 @@ export class ProfilesActivityActionViewItem extends AbstractGlobalActivityAction @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, @IKeybindingService keybindingService: IKeybindingService, ) { - super(action, contextMenuActionsProvider, { draggable: false, colors, icon: (action.activity).icon, hasPopup: true, hoverOptions }, themeService, hoverService, menuService, contextMenuService, contextKeyService, configurationService, environmentService, keybindingService); + super(ManageProfilesSubMenu, action, contextMenuActionsProvider, (action.activity).icon, colors, hoverOptions, themeService, hoverService, menuService, contextMenuService, contextKeyService, configurationService, environmentService, keybindingService); } - protected run(): Promise { - return this.commandService.executeCommand(MANAGE_PROFILES_ACTION_ID); + override render(container: HTMLElement): void { + super.render(container); + this.container.classList.add('profile-activity-item'); } protected override async resolveContextMenuActions(disposables: DisposableStore): Promise { const actions = await super.resolveContextMenuActions(disposables); actions.unshift(...[ - toAction({ id: 'hideprofiles', label: localize('hideprofiles', "Hide {0}", PROFILES_CATEGORY), run: () => this.storageService.store(ProfilesActivityActionViewItem.PROFILES_VISIBILITY_PREFERENCE_KEY, false, StorageScope.PROFILE, StorageTarget.USER) }), + toAction({ id: 'hideprofiles', label: localize('hideprofiles', "Hide {0}", PROFILES_CATEGORY.value), run: () => this.storageService.store(ProfilesActivityActionViewItem.PROFILES_VISIBILITY_PREFERENCE_KEY, false, StorageScope.PROFILE, StorageTarget.USER) }), new Separator() ]); @@ -401,7 +401,7 @@ export class GlobalActivityActionViewItem extends MenuActivityActionViewItem { @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, @IKeybindingService keybindingService: IKeybindingService, ) { - super(MenuId.GlobalActivity, action, contextMenuActionsProvider, colors, activityHoverOptions, themeService, hoverService, menuService, contextMenuService, contextKeyService, configurationService, environmentService, keybindingService); + super(MenuId.GlobalActivity, action, contextMenuActionsProvider, true, colors, activityHoverOptions, themeService, hoverService, menuService, contextMenuService, contextKeyService, configurationService, environmentService, keybindingService); } } @@ -454,7 +454,7 @@ registerAction2( super({ id: 'workbench.action.previousSideBarView', title: { value: localize('previousSideBarView', "Previous Primary Side Bar View"), original: 'Previous Primary Side Bar View' }, - category: CATEGORIES.View, + category: Categories.View, f1: true }, -1); } @@ -467,7 +467,7 @@ registerAction2( super({ id: 'workbench.action.nextSideBarView', title: { value: localize('nextSideBarView', "Next Primary Side Bar View"), original: 'Next Primary Side Bar View' }, - category: CATEGORIES.View, + category: Categories.View, f1: true }, 1); } @@ -480,7 +480,7 @@ registerAction2( super({ id: 'workbench.action.focusActivityBar', title: { value: localize('focusActivityBar', "Focus Activity Bar"), original: 'Focus Activity Bar' }, - category: CATEGORIES.View, + category: Categories.View, f1: true }); } @@ -509,13 +509,24 @@ registerThemingParticipant((theme, collector) => { `); } - const activityBarSettingsProfileHoveFgColor = theme.getColor(ACTIVITY_BAR_SETTINGS_PROFILE_HOVER_FOREGROUND); - if (activityBarSettingsProfileHoveFgColor) { + const activityBarSettingsProfileBgColor = theme.getColor(ACTIVITY_BAR_SETTINGS_PROFILE_BACKGROUND); + if (activityBarSettingsProfileBgColor) { + collector.addRule(` + .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item .action-label.profile-activity-item, + .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item .action-label.profile-activity-item, + .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item .action-label.profile-activity-item { + background-color: ${activityBarSettingsProfileBgColor} !important; + } + `); + } + + const activityBarSettingsProfileHoverFgColor = theme.getColor(ACTIVITY_BAR_SETTINGS_PROFILE_HOVER_FOREGROUND); + if (activityBarSettingsProfileHoverFgColor) { collector.addRule(` .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.active .action-label.profile-activity-item, .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus .action-label.profile-activity-item, .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:hover .action-label.profile-activity-item { - color: ${activityBarSettingsProfileHoveFgColor} !important; + color: ${activityBarSettingsProfileHoverFgColor} !important; } `); } @@ -567,6 +578,10 @@ registerThemingParticipant((theme, collector) => { z-index: 1; } + .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.profile-activity-item:before { + top: -6px; + } + .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.active:before, .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.active:hover:before, .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked:before, diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 2c6a94b9877..61d1afe68fe 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -46,7 +46,7 @@ import { IPaneCompositePart, IPaneCompositeSelectorPart } from 'vs/workbench/bro import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions, IProfileStorageRegistry } from 'vs/workbench/services/userDataProfile/common/userDataProfileStorageRegistry'; import { IUserDataProfileService, PROFILES_TTILE } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; -import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; interface IPlaceholderViewContainer { readonly id: string; @@ -196,7 +196,9 @@ export class ActivitybarPart extends Part implements IPaneCompositeSelectorPart // Accounts actions.push(new Separator()); actions.push(toAction({ id: 'toggleAccountsVisibility', label: localize('accounts', "Accounts"), checked: this.accountsVisibilityPreference, run: () => this.accountsVisibilityPreference = !this.accountsVisibilityPreference })); - actions.push(toAction({ id: 'toggleProfilesVisibility', label: PROFILES_TTILE.value, checked: this.profilesVisibilityPreference, run: () => this.profilesVisibilityPreference = !this.profilesVisibilityPreference })); + if (this.userDataProfilesService.isEnabled()) { + actions.push(toAction({ id: 'toggleProfilesVisibility', label: PROFILES_TTILE.value, checked: this.profilesVisibilityPreference, run: () => this.profilesVisibilityPreference = !this.profilesVisibilityPreference })); + } actions.push(new Separator()); // Toggle Sidebar @@ -621,25 +623,16 @@ export class ActivitybarPart extends Part implements IPaneCompositeSelectorPart } private createProfilesActivity(): IProfileActivity { - const icon = this.userDataProfileService.currentProfile.shortName ? ThemeIcon.fromString(this.userDataProfileService.currentProfile.shortName) : undefined; + const shortName = this.userDataProfileService.getShortName(this.userDataProfileService.currentProfile); + const icon = ThemeIcon.fromString(shortName); return { id: 'workbench.actions.profiles', - name: icon ? this.userDataProfileService.currentProfile.name : this.getProfileEntryDisplayName(this.userDataProfileService.currentProfile), + name: icon ? this.userDataProfileService.currentProfile.name : shortName, cssClass: icon ? `${ThemeIcon.asClassName(icon)} profile-activity-item` : 'profile-activity-item', icon: !!icon }; } - private getProfileEntryDisplayName(profile: IUserDataProfile): string { - if (profile.shortName) { - return profile.shortName; - } - if (profile.isTransient) { - return `T${this.userDataProfileService.currentProfile.name.charAt(this.userDataProfileService.currentProfile.name.length - 1)}`; - } - return this.userDataProfileService.currentProfile.name.substring(0, 2).toUpperCase(); - } - private getCompositeActions(compositeId: string): { activityAction: ViewContainerActivityAction; pinnedAction: ToggleCompositePinnedAction } { let compositeActions = this.compositeActions.get(compositeId); if (!compositeActions) { @@ -948,7 +941,7 @@ export class ActivitybarPart extends Part implements IPaneCompositeSelectorPart state.push({ id: compositeItem.id, name: viewContainerModel.title, - icon: URI.isUri(viewContainerModel.icon) && this.environmentService.remoteAuthority && isNative ? undefined : viewContainerModel.icon, /* Donot cache uri icons in desktop with remote connection */ + icon: URI.isUri(viewContainerModel.icon) && this.environmentService.remoteAuthority ? undefined : viewContainerModel.icon, /* Donot cache uri icons with remote connection */ views, pinned: compositeItem.pinned, order: compositeItem.order, @@ -1073,7 +1066,7 @@ export class ActivitybarPart extends Part implements IPaneCompositeSelectorPart } private get profilesVisibilityPreference(): boolean { - return this.userDataProfilesService.profiles.length > 1 && !this.userDataProfileService.currentProfile.isDefault && this.storageService.getBoolean(ProfilesActivityActionViewItem.PROFILES_VISIBILITY_PREFERENCE_KEY, StorageScope.PROFILE, true); + return this.userDataProfilesService.isEnabled() && this.storageService.getBoolean(ProfilesActivityActionViewItem.PROFILES_VISIBILITY_PREFERENCE_KEY, StorageScope.PROFILE, this.userDataProfilesService.profiles.length > 1); } private set profilesVisibilityPreference(value: boolean) { diff --git a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css index 292c36545bc..62447ad2ad4 100644 --- a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css +++ b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css @@ -178,19 +178,19 @@ .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-label.profile-activity-item { height: 20px; - width: 28px; - margin: 9px; + width: 32px; + margin: 14px 8px; padding: 0px; justify-content: center; align-items: center; - font-size: 12px; - line-height: 16px; - border: 1.5px solid; + font-size: 11px; + font-weight: 600; + border-radius: 10px; } /* Right aligned */ -.monaco-workbench .activitybar.right > .content :not(.monaco-menu) > .monaco-action-bar .action-label:not(.codicon) { +.monaco-workbench .activitybar.right > .content :not(.monaco-menu) > .monaco-action-bar .action-label:not(.codicon):not(.profile-activity-item) { margin-left: 0; padding: 0 48px 0 0; background-position: calc(100% - 9px) center; diff --git a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions.ts b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions.ts index 4603210c471..f53afec57bc 100644 --- a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions.ts +++ b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions.ts @@ -3,18 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Action } from 'vs/base/common/actions'; import { Codicon } from 'vs/base/common/codicons'; import { localize } from 'vs/nls'; -import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { Action2, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { Registry } from 'vs/platform/registry/common/platform'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; -import { CATEGORIES, Extensions as WorkbenchExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { AuxiliaryBarVisibleContext } from 'vs/workbench/common/contextkeys'; import { ViewContainerLocation, ViewContainerLocationToString } from 'vs/workbench/common/views'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; const auxiliaryBarRightIcon = registerIcon('auxiliarybar-right-layout-icon', Codicon.layoutSidebarRight, localize('toggleAuxiliaryIconRight', 'Icon to toggle the auxiliary bar off in its right position.')); @@ -22,50 +21,57 @@ const auxiliaryBarRightOffIcon = registerIcon('auxiliarybar-right-off-layout-ico const auxiliaryBarLeftIcon = registerIcon('auxiliarybar-left-layout-icon', Codicon.layoutSidebarLeft, localize('toggleAuxiliaryIconLeft', 'Icon to toggle the auxiliary bar in its left position.')); const auxiliaryBarLeftOffIcon = registerIcon('auxiliarybar-left-off-layout-icon', Codicon.layoutSidebarLeftOff, localize('toggleAuxiliaryIconLeftOn', 'Icon to toggle the auxiliary bar on in its left position.')); -export class ToggleAuxiliaryBarAction extends Action { +export class ToggleAuxiliaryBarAction extends Action2 { static readonly ID = 'workbench.action.toggleAuxiliaryBar'; static readonly LABEL = localize('toggleAuxiliaryBar', "Toggle Secondary Side Bar Visibility"); - constructor( - id: string, - name: string, - @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService - ) { - super(id, name, layoutService.isVisible(Parts.AUXILIARYBAR_PART) ? 'auxiliaryBar expanded' : 'auxiliaryBar'); + constructor() { + super({ + id: ToggleAuxiliaryBarAction.ID, + title: { value: ToggleAuxiliaryBarAction.LABEL, original: 'Toggle Secondary Side Bar Visibility' }, + category: Categories.View, + f1: true, + }); } - override async run(): Promise { - this.layoutService.setPartHidden(this.layoutService.isVisible(Parts.AUXILIARYBAR_PART), Parts.AUXILIARYBAR_PART); + override async run(accessor: ServicesAccessor): Promise { + const layoutService = accessor.get(IWorkbenchLayoutService); + layoutService.setPartHidden(layoutService.isVisible(Parts.AUXILIARYBAR_PART), Parts.AUXILIARYBAR_PART); } } -class FocusAuxiliaryBarAction extends Action { +registerAction2(ToggleAuxiliaryBarAction); + +registerAction2(class FocusAuxiliaryBarAction extends Action2 { static readonly ID = 'workbench.action.focusAuxiliaryBar'; static readonly LABEL = localize('focusAuxiliaryBar', "Focus into Secondary Side Bar"); - constructor( - id: string, - label: string, - @IPaneCompositePartService private readonly paneCompositeService: IPaneCompositePartService, - @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService - ) { - super(id, label); + + constructor() { + super({ + id: FocusAuxiliaryBarAction.ID, + title: { value: FocusAuxiliaryBarAction.LABEL, original: 'Focus into Secondary Side Bar' }, + category: Categories.View, + f1: true, + }); } - override async run(): Promise { + override async run(accessor: ServicesAccessor): Promise { + const paneCompositeService = accessor.get(IPaneCompositePartService); + const layoutService = accessor.get(IWorkbenchLayoutService); // Show auxiliary bar - if (!this.layoutService.isVisible(Parts.AUXILIARYBAR_PART)) { - this.layoutService.setPartHidden(false, Parts.AUXILIARYBAR_PART); + if (!layoutService.isVisible(Parts.AUXILIARYBAR_PART)) { + layoutService.setPartHidden(false, Parts.AUXILIARYBAR_PART); } // Focus into active composite - const composite = this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.AuxiliaryBar); + const composite = paneCompositeService.getActivePaneComposite(ViewContainerLocation.AuxiliaryBar); composite?.focus(); } -} +}); MenuRegistry.appendMenuItems([ { @@ -132,7 +138,3 @@ MenuRegistry.appendMenuItems([ } } ]); - -const actionRegistry = Registry.as(WorkbenchExtensions.WorkbenchActions); -actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleAuxiliaryBarAction), 'View: Toggle Secondary Side Bar Visibility', CATEGORIES.View.value); -actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(FocusAuxiliaryBarAction), 'View: Focus into Secondary Side Bar', CATEGORIES.View.value); diff --git a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts index e0303f27bb6..404ea6236bf 100644 --- a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts +++ b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts @@ -113,10 +113,8 @@ export class AuxiliaryBarPart extends BasePanelPart { const currentPositionRight = this.layoutService.getSideBarPosition() === Position.LEFT; actions.push(...[ new Separator(), - toAction({ - id: ToggleSidebarPositionAction.ID, label: currentPositionRight ? localize('move second side bar left', "Move Secondary Side Bar Left") : localize('move second side bar right', "Move Secondary Side Bar Right"), run: () => this.commandService.executeCommand(ToggleSidebarPositionAction.ID) - }), - this.instantiationService.createInstance(ToggleAuxiliaryBarAction, ToggleAuxiliaryBarAction.ID, localize('hideAuxiliaryBar', "Hide Secondary Side Bar")) + toAction({ id: ToggleSidebarPositionAction.ID, label: currentPositionRight ? localize('move second side bar left', "Move Secondary Side Bar Left") : localize('move second side bar right', "Move Secondary Side Bar Right"), run: () => this.commandService.executeCommand(ToggleSidebarPositionAction.ID) }), + toAction({ id: ToggleAuxiliaryBarAction.ID, label: localize('hide second side bar', "Hide Secondary Side Bar"), run: () => this.commandService.executeCommand(ToggleAuxiliaryBarAction.ID) }) ]); } diff --git a/src/vs/workbench/browser/parts/banner/bannerPart.ts b/src/vs/workbench/browser/parts/banner/bannerPart.ts index 667296666ae..0bb128fd96e 100644 --- a/src/vs/workbench/browser/parts/banner/bannerPart.ts +++ b/src/vs/workbench/browser/parts/banner/bannerPart.ts @@ -22,7 +22,7 @@ import { IBannerItem, IBannerService } from 'vs/workbench/services/banner/browse import { MarkdownRenderer } from 'vs/editor/contrib/markdownRenderer/browser/markdownRenderer'; import { BANNER_BACKGROUND, BANNER_FOREGROUND, BANNER_ICON_FOREGROUND } from 'vs/workbench/common/theme'; import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -318,7 +318,7 @@ class FocusBannerAction extends Action2 { super({ id: FocusBannerAction.ID, title: { value: FocusBannerAction.LABEL, original: 'Focus Banner' }, - category: CATEGORIES.View, + category: Categories.View, f1: true }); } diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index af7db9400e1..18a4d42ea90 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -329,6 +329,7 @@ export abstract class CompositePart extends Part { private collectCompositeActions(composite?: Composite): () => void { // From Composite + const menuIds = composite?.getMenuIds(); const primaryActions: IAction[] = composite?.getActions().slice(0) || []; const secondaryActions: IAction[] = composite?.getSecondaryActions().slice(0) || []; @@ -337,7 +338,7 @@ export abstract class CompositePart extends Part { toolBar.context = this.actionsContextProvider(); // Return fn to set into toolbar - return () => toolBar.setActions(prepareActions(primaryActions), prepareActions(secondaryActions)); + return () => toolBar.setActions(prepareActions(primaryActions), prepareActions(secondaryActions), menuIds); } protected getActiveComposite(): IComposite | undefined { diff --git a/src/vs/workbench/browser/parts/dialogs/dialog.web.contribution.ts b/src/vs/workbench/browser/parts/dialogs/dialog.web.contribution.ts index 2781c45869f..4fa42a97578 100644 --- a/src/vs/workbench/browser/parts/dialogs/dialog.web.contribution.ts +++ b/src/vs/workbench/browser/parts/dialogs/dialog.web.contribution.ts @@ -75,4 +75,4 @@ export class DialogHandlerContribution extends Disposable implements IWorkbenchC } const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(DialogHandlerContribution, 'DialogHandlerContribution', LifecyclePhase.Starting); +workbenchRegistry.registerWorkbenchContribution(DialogHandlerContribution, LifecyclePhase.Starting); diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbs.ts b/src/vs/workbench/browser/parts/editor/breadcrumbs.ts index f2a44d8b1db..af5bdc2b61f 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbs.ts @@ -10,7 +10,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; import { IConfigurationOverrides, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Extensions, IConfigurationRegistry, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Registry } from 'vs/platform/registry/common/platform'; import { GroupIdentifier, IEditorPartOptions } from 'vs/workbench/common/editor'; @@ -48,7 +48,7 @@ export class BreadcrumbsService implements IBreadcrumbsService { } } -registerSingleton(IBreadcrumbsService, BreadcrumbsService, true); +registerSingleton(IBreadcrumbsService, BreadcrumbsService, InstantiationType.Delayed); //#region config diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 120b7c0343a..0e7150e5a96 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -36,7 +36,7 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor import { IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor'; import { PixelRatio } from 'vs/base/browser/browser'; import { ILabelService } from 'vs/platform/label/common/label'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { ITreeNode } from 'vs/base/browser/ui/tree/tree'; import { IOutline } from 'vs/workbench/services/outline/browser/outline'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; @@ -504,15 +504,20 @@ registerAction2(class ToggleBreadcrumb extends Action2 { id: 'breadcrumbs.toggle', title: { value: localize('cmd.toggle', "Toggle Breadcrumbs"), - mnemonicTitle: localize('miBreadcrumbs', "&&Breadcrumbs"), + mnemonicTitle: localize('miBreadcrumbs', "Toggle &&Breadcrumbs"), original: 'Toggle Breadcrumbs', }, - category: CATEGORIES.View, - toggled: ContextKeyExpr.equals('config.breadcrumbs.enabled', true), + category: Categories.View, + toggled: { + condition: ContextKeyExpr.equals('config.breadcrumbs.enabled', true), + title: localize('cmd.toggle2', "Breadcrumbs"), + mnemonicTitle: localize('miBreadcrumbs2', "&&Breadcrumbs") + }, menu: [ { id: MenuId.CommandPalette }, - { id: MenuId.MenubarViewMenu, group: '5_editor', order: 3 }, - { id: MenuId.NotebookToolbar, group: 'notebookLayout', order: 2 } + { id: MenuId.MenubarEditorFeaturesMenu, order: 3 }, + { id: MenuId.NotebookToolbar, group: 'notebookLayout', order: 2 }, + { id: MenuId.StickyScrollContext } ] }); } diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index e6535720005..28894f417fe 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts @@ -381,6 +381,7 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker { identityProvider: new FileIdentityProvider(), keyboardNavigationLabelProvider: new FileNavigationLabelProvider(), accessibilityProvider: this._instantiationService.createInstance(FileAccessibilityProvider), + showNotFoundMessage: false, overrideStyles: { listBackground: breadcrumbsPickerBackground }, @@ -474,6 +475,7 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { collapseByDefault: true, expandOnlyOnTwistieClick: true, multipleSelectionSupport: false, + showNotFoundMessage: false } ); } diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 8651d5bfbe2..80a9fe8e82d 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -22,7 +22,8 @@ import { TextResourceEditorInput } from 'vs/workbench/common/editor/textResource import { TextDiffEditor } from 'vs/workbench/browser/parts/editor/textDiffEditor'; import { BinaryResourceDiffEditor } from 'vs/workbench/browser/parts/editor/binaryDiffEditor'; import { ChangeEncodingAction, ChangeEOLAction, ChangeLanguageAction, EditorStatus } from 'vs/workbench/browser/parts/editor/editorStatus'; -import { IWorkbenchActionRegistry, Extensions as ActionExtensions, CATEGORIES } from 'vs/workbench/common/actions'; +import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { SyncActionDescriptor, MenuRegistry, MenuId, IMenuItem, registerAction2 } from 'vs/platform/actions/common/actions'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; @@ -122,10 +123,10 @@ Registry.as(EditorExtensions.EditorFactory).registerEdit //#region Workbench Contributions -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(EditorAutoSave, 'EditorAutoSave', LifecyclePhase.Ready); -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(EditorStatus, 'EditorStatus', LifecyclePhase.Ready); -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(UntitledTextEditorWorkingCopyEditorHandler, 'UntitledTextEditorWorkingCopyEditorHandler', LifecyclePhase.Ready); -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DynamicEditorConfigurations, 'DynamicEditorConfigurations', LifecyclePhase.Ready); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(EditorAutoSave, LifecyclePhase.Ready); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(EditorStatus, LifecyclePhase.Ready); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(UntitledTextEditorWorkingCopyEditorHandler, LifecyclePhase.Ready); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DynamicEditorConfigurations, LifecyclePhase.Ready); registerEditorContribution(FloatingClickMenu.ID, FloatingClickMenu); @@ -172,78 +173,78 @@ registry.registerWorkbenchAction(SyncActionDescriptor.from(ChangeEOLAction), 'Ch registry.registerWorkbenchAction(SyncActionDescriptor.from(ChangeEncodingAction), 'Change File Encoding'); // Editor Management -registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenNextEditor, { primary: KeyMod.CtrlCmd | KeyCode.PageDown, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.BracketRight] } }), 'View: Open Next Editor', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenPreviousEditor, { primary: KeyMod.CtrlCmd | KeyCode.PageUp, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.BracketLeft] } }), 'View: Open Previous Editor', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenNextEditorInGroup, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.PageDown), mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow) } }), 'View: Open Next Editor in Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenPreviousEditorInGroup, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.PageUp), mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow) } }), 'View: Open Previous Editor in Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenNextRecentlyUsedEditorAction), 'View: Open Next Recently Used Editor', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenPreviousRecentlyUsedEditorAction), 'View: Open Previous Recently Used Editor', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenNextRecentlyUsedEditorInGroupAction), 'View: Open Next Recently Used Editor In Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenPreviousRecentlyUsedEditorInGroupAction), 'View: Open Previous Recently Used Editor In Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenFirstEditorInGroup), 'View: Open First Editor in Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenLastEditorInGroup, { primary: KeyMod.Alt | KeyCode.Digit0, secondary: [KeyMod.CtrlCmd | KeyCode.Digit9], mac: { primary: KeyMod.WinCtrl | KeyCode.Digit0, secondary: [KeyMod.CtrlCmd | KeyCode.Digit9] } }), 'View: Open Last Editor in Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(ReopenClosedEditorAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyT }), 'View: Reopen Closed Editor', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(ShowAllEditorsByAppearanceAction, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.KeyP), mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Tab } }), 'View: Show All Editors By Appearance', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(ShowAllEditorsByMostRecentlyUsedAction), 'View: Show All Editors By Most Recently Used', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(ShowEditorsInActiveGroupByMostRecentlyUsedAction), 'View: Show Editors in Active Group By Most Recently Used', CATEGORIES.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenNextEditor, { primary: KeyMod.CtrlCmd | KeyCode.PageDown, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.BracketRight] } }), 'View: Open Next Editor', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenPreviousEditor, { primary: KeyMod.CtrlCmd | KeyCode.PageUp, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.BracketLeft] } }), 'View: Open Previous Editor', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenNextEditorInGroup, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.PageDown), mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow) } }), 'View: Open Next Editor in Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenPreviousEditorInGroup, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.PageUp), mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow) } }), 'View: Open Previous Editor in Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenNextRecentlyUsedEditorAction), 'View: Open Next Recently Used Editor', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenPreviousRecentlyUsedEditorAction), 'View: Open Previous Recently Used Editor', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenNextRecentlyUsedEditorInGroupAction), 'View: Open Next Recently Used Editor In Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenPreviousRecentlyUsedEditorInGroupAction), 'View: Open Previous Recently Used Editor In Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenFirstEditorInGroup), 'View: Open First Editor in Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenLastEditorInGroup, { primary: KeyMod.Alt | KeyCode.Digit0, secondary: [KeyMod.CtrlCmd | KeyCode.Digit9], mac: { primary: KeyMod.WinCtrl | KeyCode.Digit0, secondary: [KeyMod.CtrlCmd | KeyCode.Digit9] } }), 'View: Open Last Editor in Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(ReopenClosedEditorAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyT }), 'View: Reopen Closed Editor', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(ShowAllEditorsByAppearanceAction, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.KeyP), mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Tab } }), 'View: Show All Editors By Appearance', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(ShowAllEditorsByMostRecentlyUsedAction), 'View: Show All Editors By Most Recently Used', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(ShowEditorsInActiveGroupByMostRecentlyUsedAction), 'View: Show Editors in Active Group By Most Recently Used', Categories.View.value); registry.registerWorkbenchAction(SyncActionDescriptor.from(ClearRecentFilesAction), 'File: Clear Recently Opened', localize('file', "File")); -registry.registerWorkbenchAction(SyncActionDescriptor.from(CloseAllEditorsAction, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.KeyW) }), 'View: Close All Editors', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(CloseAllEditorGroupsAction, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyW) }), 'View: Close All Editor Groups', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(CloseLeftEditorsInGroupAction), 'View: Close Editors to the Left in Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(CloseEditorsInOtherGroupsAction), 'View: Close Editors in Other Groups', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(CloseEditorInAllGroupsAction), 'View: Close Editor in All Groups', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorAction, { primary: KeyMod.CtrlCmd | KeyCode.Backslash }), 'View: Split Editor', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorOrthogonalAction, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.Backslash) }), 'View: Split Editor Orthogonal', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorLeftAction), 'View: Split Editor Left', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorRightAction), 'View: Split Editor Right', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorUpAction), 'View: Split Editor Up', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorDownAction), 'View: Split Editor Down', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(JoinTwoGroupsAction), 'View: Join Editor Group with Next Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(JoinAllGroupsAction), 'View: Join All Editor Groups', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateBetweenGroupsAction), 'View: Navigate Between Editor Groups', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(ResetGroupSizesAction), 'View: Reset Editor Group Sizes', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleGroupSizesAction), 'View: Toggle Editor Group Sizes', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(MaximizeGroupAction), 'View: Maximize Editor Group and Hide Side Bars', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(MinimizeOtherGroupsAction), 'View: Maximize Editor Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveEditorLeftInGroupAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.PageUp, mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow) } }), 'View: Move Editor Left', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveEditorRightInGroupAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.PageDown, mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow) } }), 'View: Move Editor Right', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveGroupLeftAction, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.LeftArrow) }), 'View: Move Editor Group Left', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveGroupRightAction, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.RightArrow) }), 'View: Move Editor Group Right', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveGroupUpAction, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.UpArrow) }), 'View: Move Editor Group Up', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveGroupDownAction, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.DownArrow) }), 'View: Move Editor Group Down', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(DuplicateGroupLeftAction), 'View: Duplicate Editor Group Left', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(DuplicateGroupRightAction), 'View: Duplicate Editor Group Right', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(DuplicateGroupUpAction), 'View: Duplicate Editor Group Up', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(DuplicateGroupDownAction), 'View: Duplicate Editor Group Down', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveEditorToPreviousGroupAction, { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.LeftArrow } }), 'View: Move Editor into Previous Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveEditorToNextGroupAction, { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.RightArrow } }), 'View: Move Editor into Next Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveEditorToFirstGroupAction, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.Digit1, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.Digit1 } }), 'View: Move Editor into First Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveEditorToLastGroupAction, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.Digit9, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.Digit9 } }), 'View: Move Editor into Last Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveEditorToLeftGroupAction), 'View: Move Editor into Left Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveEditorToRightGroupAction), 'View: Move Editor into Right Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveEditorToAboveGroupAction), 'View: Move Editor into Group Above', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveEditorToBelowGroupAction), 'View: Move Editor into Group Below', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorToPreviousGroupAction), 'View: Split Editor into Previous Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorToNextGroupAction), 'View: Split Editor into Next Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorToFirstGroupAction), 'View: Split Editor into First Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorToLastGroupAction), 'View: Split Editor into Last Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorToLeftGroupAction), 'View: Split Editor into Left Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorToRightGroupAction), 'View: Split Editor into Right Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorToAboveGroupAction), 'View: Split Editor into Group Above', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorToBelowGroupAction), 'View: Split Editor into Group Below', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusActiveGroupAction), 'View: Focus Active Editor Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusFirstGroupAction, { primary: KeyMod.CtrlCmd | KeyCode.Digit1 }), 'View: Focus First Editor Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusLastGroupAction), 'View: Focus Last Editor Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusPreviousGroup), 'View: Focus Previous Editor Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusNextGroup), 'View: Focus Next Editor Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusLeftGroup, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.LeftArrow) }), 'View: Focus Left Editor Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusRightGroup, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.RightArrow) }), 'View: Focus Right Editor Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusAboveGroup, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.UpArrow) }), 'View: Focus Editor Group Above', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusBelowGroup, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.DownArrow) }), 'View: Focus Editor Group Below', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(NewEditorGroupLeftAction), 'View: New Editor Group to the Left', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(NewEditorGroupRightAction), 'View: New Editor Group to the Right', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(NewEditorGroupAboveAction), 'View: New Editor Group Above', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(NewEditorGroupBelowAction), 'View: New Editor Group Below', CATEGORIES.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(CloseAllEditorsAction, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.KeyW) }), 'View: Close All Editors', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(CloseAllEditorGroupsAction, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyW) }), 'View: Close All Editor Groups', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(CloseLeftEditorsInGroupAction), 'View: Close Editors to the Left in Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(CloseEditorsInOtherGroupsAction), 'View: Close Editors in Other Groups', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(CloseEditorInAllGroupsAction), 'View: Close Editor in All Groups', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorAction, { primary: KeyMod.CtrlCmd | KeyCode.Backslash }), 'View: Split Editor', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorOrthogonalAction, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.Backslash) }), 'View: Split Editor Orthogonal', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorLeftAction), 'View: Split Editor Left', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorRightAction), 'View: Split Editor Right', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorUpAction), 'View: Split Editor Up', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorDownAction), 'View: Split Editor Down', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(JoinTwoGroupsAction), 'View: Join Editor Group with Next Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(JoinAllGroupsAction), 'View: Join All Editor Groups', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateBetweenGroupsAction), 'View: Navigate Between Editor Groups', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(ResetGroupSizesAction), 'View: Reset Editor Group Sizes', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleGroupSizesAction), 'View: Toggle Editor Group Sizes', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(MaximizeGroupAction), 'View: Maximize Editor Group and Hide Side Bars', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(MinimizeOtherGroupsAction), 'View: Maximize Editor Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveEditorLeftInGroupAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.PageUp, mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow) } }), 'View: Move Editor Left', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveEditorRightInGroupAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.PageDown, mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow) } }), 'View: Move Editor Right', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveGroupLeftAction, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.LeftArrow) }), 'View: Move Editor Group Left', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveGroupRightAction, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.RightArrow) }), 'View: Move Editor Group Right', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveGroupUpAction, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.UpArrow) }), 'View: Move Editor Group Up', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveGroupDownAction, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.DownArrow) }), 'View: Move Editor Group Down', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(DuplicateGroupLeftAction), 'View: Duplicate Editor Group Left', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(DuplicateGroupRightAction), 'View: Duplicate Editor Group Right', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(DuplicateGroupUpAction), 'View: Duplicate Editor Group Up', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(DuplicateGroupDownAction), 'View: Duplicate Editor Group Down', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveEditorToPreviousGroupAction, { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.LeftArrow } }), 'View: Move Editor into Previous Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveEditorToNextGroupAction, { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.RightArrow } }), 'View: Move Editor into Next Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveEditorToFirstGroupAction, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.Digit1, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.Digit1 } }), 'View: Move Editor into First Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveEditorToLastGroupAction, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.Digit9, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.Digit9 } }), 'View: Move Editor into Last Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveEditorToLeftGroupAction), 'View: Move Editor into Left Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveEditorToRightGroupAction), 'View: Move Editor into Right Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveEditorToAboveGroupAction), 'View: Move Editor into Group Above', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveEditorToBelowGroupAction), 'View: Move Editor into Group Below', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorToPreviousGroupAction), 'View: Split Editor into Previous Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorToNextGroupAction), 'View: Split Editor into Next Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorToFirstGroupAction), 'View: Split Editor into First Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorToLastGroupAction), 'View: Split Editor into Last Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorToLeftGroupAction), 'View: Split Editor into Left Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorToRightGroupAction), 'View: Split Editor into Right Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorToAboveGroupAction), 'View: Split Editor into Group Above', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorToBelowGroupAction), 'View: Split Editor into Group Below', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusActiveGroupAction), 'View: Focus Active Editor Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusFirstGroupAction, { primary: KeyMod.CtrlCmd | KeyCode.Digit1 }), 'View: Focus First Editor Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusLastGroupAction), 'View: Focus Last Editor Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusPreviousGroup), 'View: Focus Previous Editor Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusNextGroup), 'View: Focus Next Editor Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusLeftGroup, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.LeftArrow) }), 'View: Focus Left Editor Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusRightGroup, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.RightArrow) }), 'View: Focus Right Editor Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusAboveGroup, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.UpArrow) }), 'View: Focus Editor Group Above', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusBelowGroup, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.DownArrow) }), 'View: Focus Editor Group Below', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(NewEditorGroupLeftAction), 'View: New Editor Group to the Left', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(NewEditorGroupRightAction), 'View: New Editor Group to the Right', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(NewEditorGroupAboveAction), 'View: New Editor Group Above', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(NewEditorGroupBelowAction), 'View: New Editor Group Below', Categories.View.value); registry.registerWorkbenchAction(SyncActionDescriptor.from(NavigatePreviousAction), 'Go Previous'); registry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateForwardInEditsAction), 'Go Forward in Edit Locations'); registry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateBackwardsInEditsAction), 'Go Back in Edit Locations'); @@ -254,21 +255,21 @@ registry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateBackwardsInNa registry.registerWorkbenchAction(SyncActionDescriptor.from(NavigatePreviousInNavigationsAction), 'Go Previous in Navigation Locations'); registry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateToLastNavigationLocationAction), 'Go to Last Navigation Location'); registry.registerWorkbenchAction(SyncActionDescriptor.from(ClearEditorHistoryAction), 'Clear Editor History'); -registry.registerWorkbenchAction(SyncActionDescriptor.from(RevertAndCloseEditorAction), 'View: Revert and Close Editor', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutSingleAction), 'View: Single Column Editor Layout', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutTwoColumnsAction), 'View: Two Columns Editor Layout', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutThreeColumnsAction), 'View: Three Columns Editor Layout', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutTwoRowsAction), 'View: Two Rows Editor Layout', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutThreeRowsAction), 'View: Three Rows Editor Layout', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutTwoByTwoGridAction), 'View: Grid Editor Layout (2x2)', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutTwoRowsRightAction), 'View: Two Rows Right Editor Layout', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutTwoColumnsBottomAction), 'View: Two Columns Bottom Editor Layout', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleEditorTypeAction), 'View: Toggle Editor Type', CATEGORIES.View.value, ActiveEditorAvailableEditorIdsContext); -registry.registerWorkbenchAction(SyncActionDescriptor.from(ReOpenInTextEditorAction), 'View: Reopen Editor With Text Editor', CATEGORIES.View.value, ActiveEditorAvailableEditorIdsContext); -registry.registerWorkbenchAction(SyncActionDescriptor.from(QuickAccessPreviousRecentlyUsedEditorAction), 'View: Quick Open Previous Recently Used Editor', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(QuickAccessLeastRecentlyUsedEditorAction), 'View: Quick Open Least Recently Used Editor', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(QuickAccessPreviousRecentlyUsedEditorInGroupAction, { primary: KeyMod.CtrlCmd | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyCode.Tab } }, ActiveEditorGroupEmptyContext.toNegated()), 'View: Quick Open Previous Recently Used Editor in Group', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(QuickAccessLeastRecentlyUsedEditorInGroupAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.Tab } }, ActiveEditorGroupEmptyContext.toNegated()), 'View: Quick Open Least Recently Used Editor in Group', CATEGORIES.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(RevertAndCloseEditorAction), 'View: Revert and Close Editor', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutSingleAction), 'View: Single Column Editor Layout', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutTwoColumnsAction), 'View: Two Columns Editor Layout', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutThreeColumnsAction), 'View: Three Columns Editor Layout', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutTwoRowsAction), 'View: Two Rows Editor Layout', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutThreeRowsAction), 'View: Three Rows Editor Layout', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutTwoByTwoGridAction), 'View: Grid Editor Layout (2x2)', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutTwoRowsRightAction), 'View: Two Rows Right Editor Layout', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutTwoColumnsBottomAction), 'View: Two Columns Bottom Editor Layout', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleEditorTypeAction), 'View: Toggle Editor Type', Categories.View.value, ActiveEditorAvailableEditorIdsContext); +registry.registerWorkbenchAction(SyncActionDescriptor.from(ReOpenInTextEditorAction), 'View: Reopen Editor With Text Editor', Categories.View.value, ActiveEditorAvailableEditorIdsContext); +registry.registerWorkbenchAction(SyncActionDescriptor.from(QuickAccessPreviousRecentlyUsedEditorAction), 'View: Quick Open Previous Recently Used Editor', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(QuickAccessLeastRecentlyUsedEditorAction), 'View: Quick Open Least Recently Used Editor', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(QuickAccessPreviousRecentlyUsedEditorInGroupAction, { primary: KeyMod.CtrlCmd | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyCode.Tab } }, ActiveEditorGroupEmptyContext.toNegated()), 'View: Quick Open Previous Recently Used Editor in Group', Categories.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(QuickAccessLeastRecentlyUsedEditorInGroupAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.Tab } }, ActiveEditorGroupEmptyContext.toNegated()), 'View: Quick Open Least Recently Used Editor in Group', Categories.View.value); registry.registerWorkbenchAction(SyncActionDescriptor.from(QuickAccessPreviousEditorFromHistoryAction), 'Quick Open Previous Editor from History'); registerAction2(NavigateForwardAction); @@ -547,17 +548,17 @@ appendEditorToolItem( ); // Editor Commands for Command Palette -MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: KEEP_EDITOR_COMMAND_ID, title: { value: localize('keepEditor', "Keep Editor"), original: 'Keep Editor' }, category: CATEGORIES.View }, when: ContextKeyExpr.has('config.workbench.editor.enablePreview') }); -MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: PIN_EDITOR_COMMAND_ID, title: { value: localize('pinEditor', "Pin Editor"), original: 'Pin Editor' }, category: CATEGORIES.View } }); -MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: UNPIN_EDITOR_COMMAND_ID, title: { value: localize('unpinEditor', "Unpin Editor"), original: 'Unpin Editor' }, category: CATEGORIES.View } }); -MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: CLOSE_EDITOR_COMMAND_ID, title: { value: localize('closeEditor', "Close Editor"), original: 'Close Editor' }, category: CATEGORIES.View } }); -MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: CLOSE_PINNED_EDITOR_COMMAND_ID, title: { value: localize('closePinnedEditor', "Close Pinned Editor"), original: 'Close Pinned Editor' }, category: CATEGORIES.View } }); -MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: { value: localize('closeEditorsInGroup', "Close All Editors in Group"), original: 'Close All Editors in Group' }, category: CATEGORIES.View } }); -MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: CLOSE_SAVED_EDITORS_COMMAND_ID, title: { value: localize('closeSavedEditors', "Close Saved Editors in Group"), original: 'Close Saved Editors in Group' }, category: CATEGORIES.View } }); -MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, title: { value: localize('closeOtherEditors', "Close Other Editors in Group"), original: 'Close Other Editors in Group' }, category: CATEGORIES.View } }); -MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, title: { value: localize('closeRightEditors', "Close Editors to the Right in Group"), original: 'Close Editors to the Right in Group' }, category: CATEGORIES.View }, when: ActiveEditorLastInGroupContext.toNegated() }); -MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: CLOSE_EDITORS_AND_GROUP_COMMAND_ID, title: { value: localize('closeEditorGroup', "Close Editor Group"), original: 'Close Editor Group' }, category: CATEGORIES.View }, when: MultipleEditorGroupsContext }); -MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: REOPEN_WITH_COMMAND_ID, title: { value: localize('reopenWith', "Reopen Editor With..."), original: 'Reopen Editor With...' }, category: CATEGORIES.View }, when: ActiveEditorAvailableEditorIdsContext }); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: KEEP_EDITOR_COMMAND_ID, title: { value: localize('keepEditor', "Keep Editor"), original: 'Keep Editor' }, category: Categories.View }, when: ContextKeyExpr.has('config.workbench.editor.enablePreview') }); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: PIN_EDITOR_COMMAND_ID, title: { value: localize('pinEditor', "Pin Editor"), original: 'Pin Editor' }, category: Categories.View } }); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: UNPIN_EDITOR_COMMAND_ID, title: { value: localize('unpinEditor', "Unpin Editor"), original: 'Unpin Editor' }, category: Categories.View } }); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: CLOSE_EDITOR_COMMAND_ID, title: { value: localize('closeEditor', "Close Editor"), original: 'Close Editor' }, category: Categories.View } }); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: CLOSE_PINNED_EDITOR_COMMAND_ID, title: { value: localize('closePinnedEditor', "Close Pinned Editor"), original: 'Close Pinned Editor' }, category: Categories.View } }); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: { value: localize('closeEditorsInGroup', "Close All Editors in Group"), original: 'Close All Editors in Group' }, category: Categories.View } }); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: CLOSE_SAVED_EDITORS_COMMAND_ID, title: { value: localize('closeSavedEditors', "Close Saved Editors in Group"), original: 'Close Saved Editors in Group' }, category: Categories.View } }); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, title: { value: localize('closeOtherEditors', "Close Other Editors in Group"), original: 'Close Other Editors in Group' }, category: Categories.View } }); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, title: { value: localize('closeRightEditors', "Close Editors to the Right in Group"), original: 'Close Editors to the Right in Group' }, category: Categories.View }, when: ActiveEditorLastInGroupContext.toNegated() }); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: CLOSE_EDITORS_AND_GROUP_COMMAND_ID, title: { value: localize('closeEditorGroup', "Close Editor Group"), original: 'Close Editor Group' }, category: Categories.View }, when: MultipleEditorGroupsContext }); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: REOPEN_WITH_COMMAND_ID, title: { value: localize('reopenWith', "Reopen Editor With..."), original: 'Reopen Editor With...' }, category: Categories.View }, when: ActiveEditorAvailableEditorIdsContext }); // File menu MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, { @@ -778,6 +779,14 @@ MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { order: 9 }); +// Features menu +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '2_appearance', + title: localize({ key: 'miEditorFeatures', comment: ['&& denotes a mnemonic'] }, "Editor &&Features"), + submenu: MenuId.MenubarEditorFeaturesMenu, + order: 3 +}); + // Main Menu Bar Contributions: MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index 69f5e571700..31d6bb4bf3f 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -25,7 +25,7 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { CommandsRegistry, ICommandHandler, ICommandService } from 'vs/platform/commands/common/commands'; import { MenuRegistry, MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { ActiveGroupEditorsByMostRecentlyUsedQuickAccess } from 'vs/workbench/browser/parts/editor/editorQuickAccess'; import { IOpenerService, matchesScheme } from 'vs/platform/opener/common/opener'; import { EditorResolution, IEditorOptions, IResourceEditorInput, ITextEditorOptions } from 'vs/platform/editor/common/editor'; @@ -1058,7 +1058,7 @@ function registerSplitEditorInGroupCommands(): void { super({ id: SPLIT_EDITOR_IN_GROUP, title: { value: localize('splitEditorInGroup', "Split Editor in Group"), original: 'Split Editor in Group' }, - category: CATEGORIES.View, + category: Categories.View, precondition: ActiveEditorCanSplitInGroupContext, f1: true, keybinding: { @@ -1104,7 +1104,7 @@ function registerSplitEditorInGroupCommands(): void { super({ id: JOIN_EDITOR_IN_GROUP, title: { value: localize('joinEditorInGroup', "Join Editor in Group"), original: 'Join Editor in Group' }, - category: CATEGORIES.View, + category: Categories.View, precondition: SideBySideEditorActiveContext, f1: true, keybinding: { @@ -1124,7 +1124,7 @@ function registerSplitEditorInGroupCommands(): void { super({ id: TOGGLE_SPLIT_EDITOR_IN_GROUP, title: { value: localize('toggleJoinEditorInGroup', "Toggle Split Editor in Group"), original: 'Toggle Split Editor in Group' }, - category: CATEGORIES.View, + category: Categories.View, precondition: ContextKeyExpr.or(ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext), f1: true }); @@ -1146,7 +1146,7 @@ function registerSplitEditorInGroupCommands(): void { super({ id: TOGGLE_SPLIT_EDITOR_IN_GROUP_LAYOUT, title: { value: localize('toggleSplitEditorInGroupLayout', "Toggle Layout of Split Editor in Group"), original: 'Toggle Layout of Split Editor in Group' }, - category: CATEGORIES.View, + category: Categories.View, precondition: SideBySideEditorActiveContext, f1: true }); @@ -1174,7 +1174,7 @@ function registerFocusSideEditorsCommands(): void { super({ id: FOCUS_FIRST_SIDE_EDITOR, title: { value: localize('focusLeftSideEditor', "Focus First Side in Active Editor"), original: 'Focus First Side in Active Editor' }, - category: CATEGORIES.View, + category: Categories.View, precondition: ContextKeyExpr.or(SideBySideEditorActiveContext, TextCompareEditorActiveContext), f1: true }); @@ -1197,7 +1197,7 @@ function registerFocusSideEditorsCommands(): void { super({ id: FOCUS_SECOND_SIDE_EDITOR, title: { value: localize('focusRightSideEditor', "Focus Second Side in Active Editor"), original: 'Focus Second Side in Active Editor' }, - category: CATEGORIES.View, + category: Categories.View, precondition: ContextKeyExpr.or(SideBySideEditorActiveContext, TextCompareEditorActiveContext), f1: true }); @@ -1220,7 +1220,7 @@ function registerFocusSideEditorsCommands(): void { super({ id: FOCUS_OTHER_SIDE_EDITOR, title: { value: localize('focusOtherSideEditor', "Focus Other Side in Active Editor"), original: 'Focus Other Side in Active Editor' }, - category: CATEGORIES.View, + category: Categories.View, precondition: ContextKeyExpr.or(SideBySideEditorActiveContext, TextCompareEditorActiveContext), f1: true }); @@ -1283,7 +1283,7 @@ function registerOtherEditorCommands(): void { super({ id: TOGGLE_LOCK_GROUP_COMMAND_ID, title: { value: localize('toggleEditorGroupLock', "Toggle Editor Group Lock"), original: 'Toggle Editor Group Lock' }, - category: CATEGORIES.View, + category: Categories.View, precondition: MultipleEditorGroupsContext, f1: true }); @@ -1298,7 +1298,7 @@ function registerOtherEditorCommands(): void { super({ id: LOCK_GROUP_COMMAND_ID, title: { value: localize('lockEditorGroup', "Lock Editor Group"), original: 'Lock Editor Group' }, - category: CATEGORIES.View, + category: Categories.View, precondition: ContextKeyExpr.and(MultipleEditorGroupsContext, ActiveEditorGroupLockedContext.toNegated()), f1: true }); @@ -1314,7 +1314,7 @@ function registerOtherEditorCommands(): void { id: UNLOCK_GROUP_COMMAND_ID, title: { value: localize('unlockEditorGroup', "Unlock Editor Group"), original: 'Unlock Editor Group' }, precondition: ContextKeyExpr.and(MultipleEditorGroupsContext, ActiveEditorGroupLockedContext), - category: CATEGORIES.View, + category: Categories.View, f1: true }); } diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 3049500d7f8..2495583b51f 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -36,9 +36,9 @@ import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IAction } from 'vs/base/common/actions'; import { NoTabsTitleControl } from 'vs/workbench/browser/parts/editor/noTabsTitleControl'; -import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; +import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; -import { createAndFillInActionBarActions, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { hash } from 'vs/base/common/hash'; @@ -364,13 +364,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } private createContainerContextMenu(): void { - const menu = this._register(this.menuService.createMenu(MenuId.EmptyEditorGroupContext, this.contextKeyService)); - - this._register(addDisposableListener(this.element, EventType.CONTEXT_MENU, e => this.onShowContainerContextMenu(menu, e))); - this._register(addDisposableListener(this.element, TouchEventType.Contextmenu, () => this.onShowContainerContextMenu(menu))); + this._register(addDisposableListener(this.element, EventType.CONTEXT_MENU, e => this.onShowContainerContextMenu(e))); + this._register(addDisposableListener(this.element, TouchEventType.Contextmenu, () => this.onShowContainerContextMenu())); } - private onShowContainerContextMenu(menu: IMenu, e?: MouseEvent): void { + private onShowContainerContextMenu(e?: MouseEvent): void { if (!this.isEmpty) { return; // only for empty editor groups } @@ -382,14 +380,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView { anchor = { x: event.posx, y: event.posy }; } - // Fill in contributed actions - const actions: IAction[] = []; - createAndFillInContextMenuActions(menu, undefined, actions); - // Show it this.contextMenuService.showContextMenu({ + menuId: MenuId.EmptyEditorGroupContext, + contextKeyService: this.contextKeyService, getAnchor: () => anchor, - getActions: () => actions, onHide: () => { this.focus(); } diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index a1d39f06c68..4e255db9e5f 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -8,7 +8,7 @@ import { localize } from 'vs/nls'; import { runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; import { format, compare, splitLines } from 'vs/base/common/strings'; import { extname, basename, isEqual } from 'vs/base/common/resources'; -import { areFunctions, withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types'; +import { areFunctions, assertIsDefined, withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { Action } from 'vs/base/common/actions'; import { Language } from 'vs/base/common/platform'; @@ -379,12 +379,12 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { } const picks: QuickPickInput[] = [ - activeTextEditorControl.getAction(IndentUsingSpaces.ID), - activeTextEditorControl.getAction(IndentUsingTabs.ID), - activeTextEditorControl.getAction(DetectIndentation.ID), - activeTextEditorControl.getAction(IndentationToSpacesAction.ID), - activeTextEditorControl.getAction(IndentationToTabsAction.ID), - activeTextEditorControl.getAction(TrimTrailingWhitespaceAction.ID) + assertIsDefined(activeTextEditorControl.getAction(IndentUsingSpaces.ID)), + assertIsDefined(activeTextEditorControl.getAction(IndentUsingTabs.ID)), + assertIsDefined(activeTextEditorControl.getAction(DetectIndentation.ID)), + assertIsDefined(activeTextEditorControl.getAction(IndentationToSpacesAction.ID)), + assertIsDefined(activeTextEditorControl.getAction(IndentationToTabsAction.ID)), + assertIsDefined(activeTextEditorControl.getAction(TrimTrailingWhitespaceAction.ID)) ].map((a: IEditorAction) => { return { id: a.id, diff --git a/src/vs/workbench/browser/parts/editor/titleControl.ts b/src/vs/workbench/browser/parts/editor/titleControl.ts index 57fae3e0bc1..2ff9d39896b 100644 --- a/src/vs/workbench/browser/parts/editor/titleControl.ts +++ b/src/vs/workbench/browser/parts/editor/titleControl.ts @@ -12,8 +12,8 @@ import { ActionsOrientation, IActionViewItem, prepareActions } from 'vs/base/bro import { IAction, SubmenuAction, ActionRunner } from 'vs/base/common/actions'; import { ResolvedKeybinding } from 'vs/base/common/keybindings'; import { dispose, DisposableStore } from 'vs/base/common/lifecycle'; -import { createActionViewItem, createAndFillInActionBarActions, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { createActionViewItem, createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -108,7 +108,6 @@ export abstract class TitleControl extends Themable { private readonly editorToolBarMenuDisposables = this._register(new DisposableStore()); - private contextMenu: IMenu; private renderDropdownAsChildElement: boolean; constructor( @@ -140,7 +139,6 @@ export abstract class TitleControl extends Themable { this.groupLockedContext = ActiveEditorGroupLockedContext.bindTo(contextKeyService); - this.contextMenu = this._register(this.menuService.createMenu(MenuId.EditorTitleContext, this.contextKeyService)); this.renderDropdownAsChildElement = false; this.create(parent); @@ -374,14 +372,12 @@ export abstract class TitleControl extends Themable { anchor = { x: event.posx, y: event.posy }; } - // Fill in contributed actions - const actions: IAction[] = []; - createAndFillInContextMenuActions(this.contextMenu, { shouldForwardArgs: true, arg: this.resourceContext.get() }, actions); - // Show it this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => actions, + menuId: MenuId.EditorTitleContext, + menuActionOptions: { shouldForwardArgs: true, arg: this.resourceContext.get() }, + contextKeyService: this.contextKeyService, getActionsContext: () => ({ groupId: this.group.id, editorIndex: this.group.getIndexOfEditor(editor) }), getKeyBinding: action => this.getKeybinding(action), onHide: () => { diff --git a/src/vs/workbench/browser/parts/paneCompositePart.ts b/src/vs/workbench/browser/parts/paneCompositePart.ts index c0ee4b101f3..dd035d916fb 100644 --- a/src/vs/workbench/browser/parts/paneCompositePart.ts +++ b/src/vs/workbench/browser/parts/paneCompositePart.ts @@ -5,7 +5,7 @@ import { Event } from 'vs/base/common/event'; import { assertIsDefined } from 'vs/base/common/types'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IProgressIndicator } from 'vs/platform/progress/common/progress'; import { PaneCompositeDescriptor } from 'vs/workbench/browser/panecomposite'; @@ -159,4 +159,4 @@ export class PaneCompositeParts extends Disposable implements IPaneCompositePart } } -registerSingleton(IPaneCompositePartService, PaneCompositeParts, true); +registerSingleton(IPaneCompositePartService, PaneCompositeParts, InstantiationType.Delayed); diff --git a/src/vs/workbench/browser/parts/panel/panelActions.ts b/src/vs/workbench/browser/parts/panel/panelActions.ts index 074fafd9d61..0b629fa63ba 100644 --- a/src/vs/workbench/browser/parts/panel/panelActions.ts +++ b/src/vs/workbench/browser/parts/panel/panelActions.ts @@ -7,9 +7,8 @@ import 'vs/css!./media/panelpart'; import { localize } from 'vs/nls'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { Action } from 'vs/base/common/actions'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { SyncActionDescriptor, MenuId, MenuRegistry, registerAction2, Action2, IAction2Options } from 'vs/platform/actions/common/actions'; -import { IWorkbenchActionRegistry, Extensions as WorkbenchExtensions, CATEGORIES } from 'vs/workbench/common/actions'; +import { MenuId, MenuRegistry, registerAction2, Action2, IAction2Options } from 'vs/platform/actions/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { IWorkbenchLayoutService, PanelAlignment, Parts, Position, positionToString } from 'vs/workbench/services/layout/browser/layoutService'; import { ActivityAction, ToggleCompositePinnedAction, ICompositeBar } from 'vs/workbench/browser/parts/compositeBarActions'; import { IActivity } from 'vs/workbench/common/activity'; @@ -22,6 +21,7 @@ import { ViewContainerLocationToString, ViewContainerLocation, IViewDescriptorSe import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ICommandActionTitle } from 'vs/platform/action/common/action'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; const maximizeIcon = registerIcon('panel-maximize', Codicon.chevronUp, localize('maximizeIcon', 'Icon to maximize a panel.')); const restoreIcon = registerIcon('panel-restore', Codicon.chevronDown, localize('restoreIcon', 'Icon to restore a panel.')); @@ -29,50 +29,57 @@ const closeIcon = registerIcon('panel-close', Codicon.close, localize('closeIcon const panelIcon = registerIcon('panel-layout-icon', Codicon.layoutPanel, localize('togglePanelOffIcon', 'Icon to toggle the panel off when it is on.')); const panelOffIcon = registerIcon('panel-layout-icon-off', Codicon.layoutPanelOff, localize('togglePanelOnIcon', 'Icon to toggle the panel on when it is off.')); -export class TogglePanelAction extends Action { +export class TogglePanelAction extends Action2 { static readonly ID = 'workbench.action.togglePanel'; static readonly LABEL = localize('togglePanelVisibility', "Toggle Panel Visibility"); - constructor( - id: string, - name: string, - @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService - ) { - super(id, name, layoutService.isVisible(Parts.PANEL_PART) ? 'panel expanded' : 'panel'); + constructor() { + super({ + id: TogglePanelAction.ID, + title: { value: TogglePanelAction.LABEL, original: 'Toggle Panel Visibility' }, + f1: true, + category: Categories.View, + keybinding: { primary: KeyMod.CtrlCmd | KeyCode.KeyJ, weight: KeybindingWeight.WorkbenchContrib }, + }); } - override async run(): Promise { - this.layoutService.setPartHidden(this.layoutService.isVisible(Parts.PANEL_PART), Parts.PANEL_PART); + override async run(accessor: ServicesAccessor): Promise { + const layoutService = accessor.get(IWorkbenchLayoutService); + layoutService.setPartHidden(layoutService.isVisible(Parts.PANEL_PART), Parts.PANEL_PART); } } -class FocusPanelAction extends Action { +registerAction2(TogglePanelAction); + +registerAction2(class extends Action2 { static readonly ID = 'workbench.action.focusPanel'; static readonly LABEL = localize('focusPanel', "Focus into Panel"); - constructor( - id: string, - label: string, - @IPaneCompositePartService private readonly paneCompositeService: IPaneCompositePartService, - @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService - ) { - super(id, label); + constructor() { + super({ + id: 'workbench.action.focusPanel', + title: { value: localize('focusPanel', "Focus into Panel"), original: 'Focus into Panel' }, + category: Categories.View, + f1: true, + }); } - override async run(): Promise { + override async run(accessor: ServicesAccessor): Promise { + const layoutService = accessor.get(IWorkbenchLayoutService); + const paneCompositeService = accessor.get(IPaneCompositePartService); // Show panel - if (!this.layoutService.isVisible(Parts.PANEL_PART)) { - this.layoutService.setPartHidden(false, Parts.PANEL_PART); + if (!layoutService.isVisible(Parts.PANEL_PART)) { + layoutService.setPartHidden(false, Parts.PANEL_PART); } // Focus into active panel - const panel = this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.Panel); + const panel = paneCompositeService.getActivePaneComposite(ViewContainerLocation.Panel); panel?.focus(); } -} +}); const PositionPanelActionId = { LEFT: 'workbench.action.positionPanelLeft', @@ -159,7 +166,7 @@ PositionPanelActionConfigs.forEach(positionPanelAction => { super({ id, title, - category: CATEGORIES.View, + category: Categories.View, f1: true }); } @@ -193,7 +200,7 @@ AlignPanelActionConfigs.forEach(alignPanelAction => { super({ id, title: title, - category: CATEGORIES.View, + category: Categories.View, toggled: when.negate(), f1: true }); @@ -256,19 +263,21 @@ export class PlaceHolderToggleCompositePinnedAction extends ToggleCompositePinne } } -export class SwitchPanelViewAction extends Action { +class SwitchPanelViewAction extends Action2 { - constructor( - id: string, - name: string, - @IPaneCompositePartService private readonly paneCompositeService: IPaneCompositePartService - ) { - super(id, name); + constructor(id: string, title: ICommandActionTitle) { + super({ + id, + title, + category: Categories.View, + f1: true, + }); } - override async run(offset: number): Promise { - const pinnedPanels = this.paneCompositeService.getPinnedPaneCompositeIds(ViewContainerLocation.Panel); - const activePanel = this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.Panel); + override async run(accessor: ServicesAccessor, offset: number): Promise { + const paneCompositeService = accessor.get(IPaneCompositePartService); + const pinnedPanels = paneCompositeService.getPinnedPaneCompositeIds(ViewContainerLocation.Panel); + const activePanel = paneCompositeService.getActivePaneComposite(ViewContainerLocation.Panel); if (!activePanel) { return; } @@ -280,52 +289,36 @@ export class SwitchPanelViewAction extends Action { } } if (typeof targetPanelId === 'string') { - await this.paneCompositeService.openPaneComposite(targetPanelId, ViewContainerLocation.Panel, true); + await paneCompositeService.openPaneComposite(targetPanelId, ViewContainerLocation.Panel, true); } } } -export class PreviousPanelViewAction extends SwitchPanelViewAction { - - static readonly ID = 'workbench.action.previousPanelView'; - static readonly LABEL = localize('previousPanelView', 'Previous Panel View'); - - constructor( - id: string, - name: string, - @IPaneCompositePartService paneCompositeService: IPaneCompositePartService - ) { - super(id, name, paneCompositeService); +registerAction2(class extends SwitchPanelViewAction { + constructor() { + super('workbench.action.previousPanelView', { + value: localize('previousPanelView', 'Previous Panel View'), + original: 'Previous Panel View' + }); } - override run(): Promise { - return super.run(-1); + override run(accessor: ServicesAccessor): Promise { + return super.run(accessor, -1); } -} +}); -export class NextPanelViewAction extends SwitchPanelViewAction { - - static readonly ID = 'workbench.action.nextPanelView'; - static readonly LABEL = localize('nextPanelView', 'Next Panel View'); - - constructor( - id: string, - name: string, - @IPaneCompositePartService paneCompositeService: IPaneCompositePartService - ) { - super(id, name, paneCompositeService); +registerAction2(class extends SwitchPanelViewAction { + constructor() { + super('workbench.action.nextPanelView', { + value: localize('nextPanelView', 'Next Panel View'), + original: 'Next Panel View' + }); } - override run(): Promise { - return super.run(1); + override run(accessor: ServicesAccessor): Promise { + return super.run(accessor, 1); } -} - -const actionRegistry = Registry.as(WorkbenchExtensions.WorkbenchActions); -actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(TogglePanelAction, { primary: KeyMod.CtrlCmd | KeyCode.KeyJ }), 'View: Toggle Panel Visibility', CATEGORIES.View.value); -actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(FocusPanelAction), 'View: Focus into Panel', CATEGORIES.View.value); -actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(PreviousPanelViewAction), 'View: Previous Panel View', CATEGORIES.View.value); -actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(NextPanelViewAction), 'View: Next Panel View', CATEGORIES.View.value); +}); registerAction2(class extends Action2 { constructor() { @@ -333,7 +326,7 @@ registerAction2(class extends Action2 { id: 'workbench.action.toggleMaximizedPanel', title: { value: localize('toggleMaximizedPanel', "Toggle Maximized Panel"), original: 'Toggle Maximized Panel' }, tooltip: localize('maximizePanel', "Maximize Panel Size"), - category: CATEGORIES.View, + category: Categories.View, f1: true, icon: maximizeIcon, // the workbench grid currently prevents us from supporting panel maximization with non-center panel alignment @@ -374,7 +367,7 @@ registerAction2(class extends Action2 { super({ id: 'workbench.action.closePanel', title: { value: localize('closePanel', "Close Panel"), original: 'Close Panel' }, - category: CATEGORIES.View, + category: Categories.View, icon: closeIcon, menu: [{ id: MenuId.CommandPalette, @@ -396,7 +389,7 @@ registerAction2(class extends Action2 { super({ id: 'workbench.action.closeAuxiliaryBar', title: { value: localize('closeSecondarySideBar', "Close Secondary Side Bar"), original: 'Close Secondary Side Bar' }, - category: CATEGORIES.View, + category: Categories.View, icon: closeIcon, menu: [{ id: MenuId.CommandPalette, @@ -500,7 +493,7 @@ class MovePanelToSidePanelAction extends MoveViewsBetweenPanelsAction { value: localize('movePanelToSecondarySideBar', "Move Panel Views To Secondary Side Bar"), original: 'Move Panel Views To Secondary Side Bar' }, - category: CATEGORIES.View, + category: Categories.View, f1: false }); } @@ -515,7 +508,7 @@ export class MovePanelToSecondarySideBarAction extends MoveViewsBetweenPanelsAct value: localize('movePanelToSecondarySideBar', "Move Panel Views To Secondary Side Bar"), original: 'Move Panel Views To Secondary Side Bar' }, - category: CATEGORIES.View, + category: Categories.View, f1: true }); } @@ -536,7 +529,7 @@ class MoveSidePanelToPanelAction extends MoveViewsBetweenPanelsAction { value: localize('moveSidePanelToPanel', "Move Secondary Side Bar Views To Panel"), original: 'Move Secondary Side Bar Views To Panel' }, - category: CATEGORIES.View, + category: Categories.View, f1: false }); } @@ -552,7 +545,7 @@ export class MoveSecondarySideBarToPanelAction extends MoveViewsBetweenPanelsAct value: localize('moveSidePanelToPanel', "Move Secondary Side Bar Views To Panel"), original: 'Move Secondary Side Bar Views To Panel' }, - category: CATEGORIES.View, + category: Categories.View, f1: true }); } diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index d3dfaf39c5f..a0c8283723b 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -45,7 +45,8 @@ import { IPartOptions } from 'vs/workbench/browser/part'; import { StringSHA1 } from 'vs/base/common/hash'; import { URI } from 'vs/base/common/uri'; import { Extensions, IProfileStorageRegistry } from 'vs/workbench/services/userDataProfile/common/userDataProfileStorageRegistry'; -import { WorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; +import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; +import { ICommandService } from 'vs/platform/commands/common/commands'; interface ICachedPanel { id: string; @@ -110,7 +111,7 @@ export abstract class BasePanelPart extends CompositePart impleme private compositeBar: CompositeBar; private readonly compositeActions = new Map(); - private globalToolBar: WorkbenchToolBar | undefined; + private globalToolBar: ToolBar | undefined; private globalActions: CompositeMenuActions; private readonly panelDisposables: Map = new Map(); @@ -549,13 +550,12 @@ export abstract class BasePanelPart extends CompositePart impleme const globalTitleActionsContainer = element.appendChild($('.global-actions')); // Global Actions Toolbar - this.globalToolBar = this._register(this.instantiationService.createInstance(WorkbenchToolBar, globalTitleActionsContainer, { + this.globalToolBar = this._register(new ToolBar(globalTitleActionsContainer, this.contextMenuService, { actionViewItemProvider: action => this.actionViewItemProvider(action), orientation: ActionsOrientation.HORIZONTAL, getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id), anchorAlignmentProvider: () => this.getTitleAreaDropDownAnchorAlignment(), - toggleMenuTitle: localize('moreActions', "More Actions..."), - resetMenu: this.globalActions.menuId + toggleMenuTitle: localize('moreActions', "More Actions...") })); this.updateGlobalToolbarActions(); @@ -928,6 +928,7 @@ export class PanelPart extends BasePanelPart { @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IContextKeyService contextKeyService: IContextKeyService, @IExtensionService extensionService: IExtensionService, + @ICommandService private commandService: ICommandService, ) { super( notificationService, @@ -985,7 +986,7 @@ export class PanelPart extends BasePanelPart { // show the contextual menu item if it is not in that position .filter(({ when }) => this.contextKeyService.contextMatchesRules(when)) .map(({ id, title }) => this.instantiationService.createInstance(SetPanelPositionAction, id, title.value)), - this.instantiationService.createInstance(TogglePanelAction, TogglePanelAction.ID, localize('hidePanel', "Hide Panel")) + toAction({ id: TogglePanelAction.ID, label: localize('hidePanel', "Hide Panel"), run: () => this.commandService.executeCommand(TogglePanelAction.ID) }) ]); } diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarActions.ts b/src/vs/workbench/browser/parts/sidebar/sidebarActions.ts index 9ebb1e99eb7..974349fe5b3 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarActions.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarActions.ts @@ -10,7 +10,7 @@ import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/bro import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { ViewContainerLocation } from 'vs/workbench/common/views'; @@ -20,7 +20,7 @@ export class FocusSideBarAction extends Action2 { super({ id: 'workbench.action.focusSideBar', title: { value: localize('focusSideBar', "Focus into Primary Side Bar"), original: 'Focus into Primary Side Bar' }, - category: CATEGORIES.View, + category: Categories.View, f1: true, keybinding: { weight: KeybindingWeight.WorkbenchContrib, diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarActions.ts b/src/vs/workbench/browser/parts/statusbar/statusbarActions.ts index 1af3a67ebc2..d484e5a0ad2 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarActions.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarActions.ts @@ -11,7 +11,7 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { StatusbarViewModel } from 'vs/workbench/browser/parts/statusbar/statusbarModel'; import { StatusBarFocused } from 'vs/workbench/common/contextkeys'; @@ -114,7 +114,7 @@ class FocusStatusBarAction extends Action2 { super({ id: 'workbench.action.focusStatusBar', title: { value: localize('focusStatusBar', "Focus Status Bar"), original: 'Focus Status Bar' }, - category: CATEGORIES.View, + category: Categories.View, f1: true }); } diff --git a/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts b/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts index 8986a0879ac..e59c60f87f6 100644 --- a/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts @@ -50,7 +50,7 @@ export class CommandCenterControl { telemetrySource: 'commandCenter', actionViewItemProvider: (action) => { - if (action instanceof MenuItemAction && action.id === 'workbench.action.quickOpen') { + if (action instanceof MenuItemAction && action.id === 'workbench.action.quickOpenWithModes') { class CommandCenterViewItem extends BaseActionViewItem { @@ -72,6 +72,7 @@ export class CommandCenterControl { // label: just workspace name and optional decorations const label = this._getLabel(); const labelElement = document.createElement('span'); + labelElement.classList.add('search-label'); labelElement.innerText = label; reset(left, searchIcon, labelElement); diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 654b1fc7eb7..47f2e8a396a 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -100,21 +100,6 @@ display: none; } -.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.command-center .action-item.quickopen { - display: flex; - color: var(--vscode-commandCenter-foreground); - background-color: var(--vscode-commandCenter-background); - border: 1px solid var(--vscode-commandCenter-border); - flex-direction: row; - justify-content: center; - overflow: hidden; -} - -.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.command-center .action-item.quickopen:HOVER { - color: var(--vscode-commandCenter-activeForeground); - background-color: var(--vscode-commandCenter-activeBackground); -} - .monaco-workbench .part.titlebar>.titlebar-container>.window-title>.command-center .action-item.command-center { display: flex; align-items: stretch; @@ -127,16 +112,19 @@ border-bottom-left-radius: 6px; border-top-right-radius: 6px; border-bottom-right-radius: 6px; + + + width: 38vw; + max-width: 600px; } .monaco-workbench .part.titlebar>.titlebar-container>.window-title>.command-center .action-item.command-center .left { display: inline-flex; justify-content: center; - - /* width */ - width: 38vw; - max-width: 600px; - min-width: 32px; + width: 100%; + margin: auto; + overflow: hidden; + text-overflow: ellipsis; } .monaco-workbench .part.titlebar>.titlebar-container>.window-title>.command-center .action-item.command-center .left .search-icon { @@ -152,7 +140,7 @@ .monaco-workbench .part.titlebar>.titlebar-container>.window-title>.command-center .action-item.command-center .right { margin-left: auto; - padding: 2px 3px 0 0; + padding: 2px 2px 0 0; width: 16px; flex-shrink: 0; } diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index 7930effaae7..df4e45e65f7 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -40,6 +40,7 @@ import { IsMacNativeContext, IsWebContext } from 'vs/platform/contextkey/common/ import { ICommandService } from 'vs/platform/commands/common/commands'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { OpenRecentAction } from 'vs/workbench/browser/actions/windowActions'; +import { isICommandActionToggleInfo } from 'vs/platform/action/common/action'; export type IOpenRecentAction = IAction & { uri: URI; remoteAuthority?: string }; @@ -701,7 +702,7 @@ export class CustomMenubarControl extends MenubarControl { this.insertActionsBefore(action, target); // use mnemonicTitle whenever possible - const title = typeof action.item.title === 'string' + let title = typeof action.item.title === 'string' ? action.item.title : action.item.title.mnemonicTitle ?? action.item.title.value; @@ -727,6 +728,10 @@ export class CustomMenubarControl extends MenubarControl { target.push(new SubmenuAction(action.id, mnemonicMenuLabel(title), submenuActions)); } } else { + if (isICommandActionToggleInfo(action.item.toggled)) { + title = action.item.toggled.mnemonicTitle ?? action.item.toggled.title ?? title; + } + const newAction = new Action(action.id, mnemonicMenuLabel(title), action.class, action.enabled, () => this.commandService.executeCommand(action.id)); newAction.tooltip = action.tooltip; newAction.checked = action.checked; diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 90cb274b662..29b322622bd 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -11,7 +11,6 @@ import { getZoomFactor } from 'vs/base/browser/browser'; import { MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility } from 'vs/platform/window/common/window'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; -import { IAction } from 'vs/base/common/actions'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; @@ -25,8 +24,8 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati import { Emitter, Event } from 'vs/base/common/event'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; -import { createActionViewItem, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { Action2, IMenuService, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; +import { createActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { Codicon } from 'vs/base/common/codicons'; @@ -35,7 +34,7 @@ import { WindowTitle } from 'vs/workbench/browser/parts/titlebar/windowTitle'; import { CommandCenterControl } from 'vs/workbench/browser/parts/titlebar/commandCenterControl'; import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { MenuWorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; export class TitlebarPart extends Part implements ITitleService { @@ -92,7 +91,6 @@ export class TitlebarPart extends Part implements ITitleService { @IThemeService themeService: IThemeService, @IStorageService storageService: IStorageService, @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, - @IMenuService private readonly menuService: IMenuService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IHostService private readonly hostService: IHostService, @IHoverService hoverService: IHoverService, @@ -322,7 +320,7 @@ export class TitlebarPart extends Part implements ITitleService { super({ id: `workbench.action.focusTitleBar`, title: { value: localize('focusTitleBar', "Focus Title Bar"), original: 'Focus Title Bar' }, - category: CATEGORIES.View, + category: Categories.View, f1: true, }); } @@ -382,16 +380,11 @@ export class TitlebarPart extends Part implements ITitleService { const event = new StandardMouseEvent(e); const anchor = { x: event.posx, y: event.posy }; - // Fill in contributed actions - const menu = this.menuService.createMenu(menuId, this.contextKeyService); - const actions: IAction[] = []; - createAndFillInContextMenuActions(menu, undefined, actions); - menu.dispose(); - // Show it this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => actions, + menuId, + contextKeyService: this.contextKeyService, domForShadowRoot: isMacintosh && isNative ? event.target : undefined }); } diff --git a/src/vs/workbench/browser/parts/views/media/views.css b/src/vs/workbench/browser/parts/views/media/views.css index 9ed1f853826..df9b41cafaf 100644 --- a/src/vs/workbench/browser/parts/views/media/views.css +++ b/src/vs/workbench/browser/parts/views/media/views.css @@ -201,3 +201,76 @@ .customview-tree .monaco-list .monaco-list-row.focused .custom-view-tree-node-item .actions { display: block; } + +/* filter view pane */ + +.viewpane-filter-container { + cursor: default; + display: flex; +} + +.viewpane-filter-container.grow { + flex: 1; +} + +.viewpane-filter-container > .viewpane-filter { + display: flex; + align-items: center; + flex: 1; + position: relative; +} + +.viewpane-filter-container > .viewpane-filter .monaco-inputbox { + height: 24px; + font-size: 12px; + flex: 1; +} + +.pane-header .viewpane-filter-container > .viewpane-filter .monaco-inputbox .monaco-inputbox { + height: 20px; + line-height: 18px; +} + +.monaco-workbench.vs .viewpane-filter-container > .viewpane-filter .monaco-inputbox { + height: 25px; +} + +.viewpane-filter-container > .viewpane-filter > .viewpane-filter-controls { + position: absolute; + top: 0px; + bottom: 0; + right: 0px; + display: flex; + align-items: center; +} + +.viewpane-filter-container > .viewpane-filter > .viewpane-filter-controls > .viewpane-filter-badge { + margin: 4px 0px; + padding: 0px 8px; + border-radius: 2px; +} + +.viewpane-filter > .viewpane-filter-controls > .viewpane-filter-badge.hidden, +.viewpane-filter.small > .viewpane-filter-controls > .viewpane-filter-badge { + display: none; +} + +.viewpane-filter > .viewpane-filter-controls > .monaco-action-bar .action-item .action-label.codicon.filter { + padding: 2px; +} + +.panel > .title .monaco-action-bar .action-item.viewpane-filter-container { + max-width: 400px; + min-width: 300px; + margin-right: 10px; +} + +.pane-body .viewpane-filter-container:not(:empty) { + flex: 1; + margin: 10px 20px; + height: initial; +} + +.pane-body .viewpane-filter-container > .viewpane-filter > .viewpane-filter-controls .monaco-action-bar .action-item { + margin-right: 4px; +} diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index c4ef9dc916c..bf75f767367 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -1024,13 +1024,13 @@ registerThemingParticipant((theme, collector) => { }); interface ITreeExplorerTemplateData { - elementDisposable: IDisposable; - container: HTMLElement; - resourceLabel: IResourceLabel; - icon: HTMLElement; - checkboxContainer: HTMLElement; + readonly elementDisposable: DisposableStore; + readonly container: HTMLElement; + readonly resourceLabel: IResourceLabel; + readonly icon: HTMLElement; + readonly checkboxContainer: HTMLElement; checkbox?: TreeItemCheckbox; - actionBar: ActionBar; + readonly actionBar: ActionBar; } class TreeRenderer extends Disposable implements ITreeRenderer { @@ -1084,7 +1084,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer, index: number, templateData: ITreeExplorerTemplateData): void { - templateData.elementDisposable.dispose(); const node = element.element; const resource = node.resourceUri ? URI.revive(node.resourceUri) : null; const treeItemLabel: ITreeItemLabel | undefined = node.label ? node.label : (resource ? { label: basename(resource) } : undefined); @@ -1150,10 +1149,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer('explorer.decorations'); @@ -1205,7 +1201,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer this.treeViewsService.removeRenderedTreeItemElement(node))); // remember rendered element this._renderedElements.set(element, templateData); - disposableStore.add({ dispose: () => this._renderedElements.delete(element) }); } private rerender() { @@ -1234,7 +1228,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer { checkbox.dispose(); templateData.checkbox = undefined; } }); } templateData.checkbox.render(node); } @@ -1299,8 +1292,13 @@ class TreeRenderer extends Disposable implements ITreeRenderer, index: number, templateData: ITreeExplorerTemplateData): void { - templateData.elementDisposable.dispose(); + templateData.elementDisposable.clear(); + this._renderedElements.delete(resource); + this.treeViewsService.removeRenderedTreeItemElement(resource.element); + + templateData.checkbox?.dispose(); + templateData.checkbox = undefined; } disposeTemplate(templateData: ITreeExplorerTemplateData): void { diff --git a/src/vs/workbench/browser/parts/views/viewFilter.ts b/src/vs/workbench/browser/parts/views/viewFilter.ts new file mode 100644 index 00000000000..60bcde06cd2 --- /dev/null +++ b/src/vs/workbench/browser/parts/views/viewFilter.ts @@ -0,0 +1,257 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Delayer } from 'vs/base/common/async'; +import * as DOM from 'vs/base/browser/dom'; +import { IAction } from 'vs/base/common/actions'; +import { HistoryInputBox } from 'vs/base/browser/ui/inputbox/inputBox'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IThemeService, registerThemingParticipant, ICssStyleCollector, IColorTheme } from 'vs/platform/theme/common/themeService'; +import { attachInputBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; +import { toDisposable } from 'vs/base/common/lifecycle'; +import { badgeBackground, badgeForeground, contrastBorder, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground } from 'vs/platform/theme/common/colorRegistry'; +import { localize } from 'vs/nls'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ContextScopedHistoryInputBox } from 'vs/platform/history/browser/contextScopedHistoryWidget'; +import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { Codicon } from 'vs/base/common/codicons'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { showHistoryKeybindingHint } from 'vs/platform/history/browser/historyWidgetKeybindingHint'; +import { MenuId, MenuRegistry, SubmenuItemAction } from 'vs/platform/actions/common/actions'; +import { HiddenItemStrategy, MenuWorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; +import { SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { Widget } from 'vs/base/browser/ui/widget'; +import { Emitter } from 'vs/base/common/event'; + +const viewFilterMenu = new MenuId('menu.view.filter'); +export const viewFilterSubmenu = new MenuId('submenu.view.filter'); +MenuRegistry.appendMenuItem(viewFilterMenu, { + submenu: viewFilterSubmenu, + title: localize('more filters', "More Filters..."), + group: 'navigation', + icon: Codicon.filter, +}); + +class MoreFiltersActionViewItem extends SubmenuEntryActionViewItem { + + private _checked: boolean = false; + set checked(checked: boolean) { + if (this._checked !== checked) { + this._checked = checked; + this.updateChecked(); + } + } + + protected override updateChecked(): void { + if (this.element) { + this.element.classList.toggle('checked', this._checked); + } + } + + override render(container: HTMLElement): void { + super.render(container); + this.updateChecked(); + } + +} + +export interface IFilterWidgetOptions { + readonly text?: string; + readonly placeholder?: string; + readonly ariaLabel?: string; + readonly history?: string[]; + readonly focusContextKey?: string; +} + +export class FilterWidget extends Widget { + + readonly element: HTMLElement; + private readonly delayedFilterUpdate: Delayer; + private readonly filterInputBox: HistoryInputBox; + private readonly filterBadge: HTMLElement; + private readonly toolbar: MenuWorkbenchToolBar; + private readonly focusContextKey: IContextKey | undefined; + + private readonly _onDidChangeFilterText = this._register(new Emitter()); + readonly onDidChangeFilterText = this._onDidChangeFilterText.event; + + private moreFiltersActionViewItem: MoreFiltersActionViewItem | undefined; + private isMoreFiltersChecked: boolean = false; + + constructor( + private readonly options: IFilterWidgetOptions, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IContextViewService private readonly contextViewService: IContextViewService, + @IThemeService private readonly themeService: IThemeService, + @IContextKeyService contextKeyService: IContextKeyService, + @IKeybindingService private readonly keybindingService: IKeybindingService + ) { + super(); + this.delayedFilterUpdate = new Delayer(400); + this._register(toDisposable(() => this.delayedFilterUpdate.cancel())); + + if (options.focusContextKey) { + this.focusContextKey = new RawContextKey(options.focusContextKey, false).bindTo(contextKeyService); + } + + this.element = DOM.$('.viewpane-filter'); + this.filterInputBox = this.createInput(this.element); + + const controlsContainer = DOM.append(this.element, DOM.$('.viewpane-filter-controls')); + this.filterBadge = this.createBadge(controlsContainer); + this.toolbar = this._register(this.createToolBar(controlsContainer)); + + this.adjustInputBox(); + } + + focus(): void { + this.filterInputBox.focus(); + } + + blur(): void { + this.filterInputBox.blur(); + } + + updateBadge(message: string | undefined): void { + this.filterBadge.classList.toggle('hidden', !message); + this.filterBadge.textContent = message || ''; + this.adjustInputBox(); + } + + setFilterText(filterText: string): void { + this.filterInputBox.value = filterText; + } + + getFilterText(): string { + return this.filterInputBox.value; + } + + getHistory(): string[] { + return this.filterInputBox.getHistory(); + } + + layout(width: number): void { + this.element.parentElement?.classList.toggle('grow', width > 700); + this.element.classList.toggle('small', width < 400); + this.adjustInputBox(); + } + + checkMoreFilters(checked: boolean): void { + this.isMoreFiltersChecked = checked; + if (this.moreFiltersActionViewItem) { + this.moreFiltersActionViewItem.checked = checked; + } + } + + private createInput(container: HTMLElement): ContextScopedHistoryInputBox { + const inputBox = this._register(this.instantiationService.createInstance(ContextScopedHistoryInputBox, container, this.contextViewService, { + placeholder: this.options.placeholder, + ariaLabel: this.options.ariaLabel, + history: this.options.history || [], + showHistoryHint: () => showHistoryKeybindingHint(this.keybindingService) + })); + this._register(attachInputBoxStyler(inputBox, this.themeService)); + if (this.options.text) { + inputBox.value = this.options.text; + } + this._register(inputBox.onDidChange(filter => this.delayedFilterUpdate.trigger(() => this.onDidInputChange(inputBox!)))); + this._register(DOM.addStandardDisposableListener(inputBox.inputElement, DOM.EventType.KEY_DOWN, (e: any) => this.onInputKeyDown(e, inputBox!))); + this._register(DOM.addStandardDisposableListener(container, DOM.EventType.KEY_DOWN, this.handleKeyboardEvent)); + this._register(DOM.addStandardDisposableListener(container, DOM.EventType.KEY_UP, this.handleKeyboardEvent)); + this._register(DOM.addStandardDisposableListener(inputBox.inputElement, DOM.EventType.CLICK, (e) => { + e.stopPropagation(); + e.preventDefault(); + })); + + const focusTracker = this._register(DOM.trackFocus(inputBox.inputElement)); + if (this.focusContextKey) { + this._register(focusTracker.onDidFocus(() => this.focusContextKey!.set(true))); + this._register(focusTracker.onDidBlur(() => this.focusContextKey!.set(false))); + this._register(toDisposable(() => this.focusContextKey!.reset())); + } + return inputBox; + } + + private createBadge(container: HTMLElement): HTMLElement { + const filterBadge = DOM.append(container, DOM.$('.viewpane-filter-badge.hidden')); + this._register(attachStylerCallback(this.themeService, { badgeBackground, badgeForeground, contrastBorder }, colors => { + const background = colors.badgeBackground ? colors.badgeBackground.toString() : ''; + const foreground = colors.badgeForeground ? colors.badgeForeground.toString() : ''; + const border = colors.contrastBorder ? colors.contrastBorder.toString() : ''; + + filterBadge.style.backgroundColor = background; + + filterBadge.style.borderWidth = border ? '1px' : ''; + filterBadge.style.borderStyle = border ? 'solid' : ''; + filterBadge.style.borderColor = border; + filterBadge.style.color = foreground; + })); + return filterBadge; + } + + private createToolBar(container: HTMLElement): MenuWorkbenchToolBar { + return this.instantiationService.createInstance(MenuWorkbenchToolBar, container, viewFilterMenu, + { + hiddenItemStrategy: HiddenItemStrategy.NoHide, + actionViewItemProvider: (action: IAction) => { + if (action instanceof SubmenuItemAction && action.item.submenu.id === viewFilterSubmenu.id) { + this.moreFiltersActionViewItem = this.instantiationService.createInstance(MoreFiltersActionViewItem, action, undefined); + this.moreFiltersActionViewItem.checked = this.isMoreFiltersChecked; + return this.moreFiltersActionViewItem; + } + return undefined; + } + }); + } + + private onDidInputChange(inputbox: HistoryInputBox) { + inputbox.addToHistory(); + this._onDidChangeFilterText.fire(inputbox.value); + } + + private adjustInputBox(): void { + this.filterInputBox.inputElement.style.paddingRight = this.element.classList.contains('small') || this.filterBadge.classList.contains('hidden') ? '25px' : '150px'; + } + + // Action toolbar is swallowing some keys for action items which should not be for an input box + private handleKeyboardEvent(event: StandardKeyboardEvent) { + if (event.equals(KeyCode.Space) + || event.equals(KeyCode.LeftArrow) + || event.equals(KeyCode.RightArrow) + ) { + event.stopPropagation(); + } + } + + private onInputKeyDown(event: StandardKeyboardEvent, filterInputBox: HistoryInputBox) { + let handled = false; + if (event.equals(KeyCode.Tab)) { + this.toolbar.focus(); + handled = true; + } + if (handled) { + event.stopPropagation(); + event.preventDefault(); + } + } + +} + +registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { + const inputActiveOptionBorderColor = theme.getColor(inputActiveOptionBorder); + if (inputActiveOptionBorderColor) { + collector.addRule(`.viewpane-filter > .viewpane-filter-controls .monaco-action-bar .action-label.codicon.codicon-filter.checked { border-color: ${inputActiveOptionBorderColor}; }`); + } + const inputActiveOptionForegroundColor = theme.getColor(inputActiveOptionForeground); + if (inputActiveOptionForegroundColor) { + collector.addRule(`.viewpane-filter > .viewpane-filter-controls .monaco-action-bar .action-label.codicon.codicon-filter.checked { color: ${inputActiveOptionForegroundColor}; }`); + } + const inputActiveOptionBackgroundColor = theme.getColor(inputActiveOptionBackground); + if (inputActiveOptionBackgroundColor) { + collector.addRule(`.viewpane-filter > .viewpane-filter-controls .monaco-action-bar .action-label.codicon.codicon-filter.checked { background-color: ${inputActiveOptionBackgroundColor}; }`); + } +}); diff --git a/src/vs/workbench/browser/parts/views/viewPane.ts b/src/vs/workbench/browser/parts/views/viewPane.ts index feba32556f0..f5b42f9b8a1 100644 --- a/src/vs/workbench/browser/parts/views/viewPane.ts +++ b/src/vs/workbench/browser/parts/views/viewPane.ts @@ -9,9 +9,9 @@ import { Event, Emitter } from 'vs/base/common/event'; import { foreground } from 'vs/platform/theme/common/colorRegistry'; import { attachButtonStyler, attachProgressBarStyler } from 'vs/platform/theme/common/styler'; import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; -import { after, append, $, trackFocus, EventType, addDisposableListener, createCSSRule, asCSSUrl } from 'vs/base/browser/dom'; +import { after, append, $, trackFocus, EventType, addDisposableListener, createCSSRule, asCSSUrl, Dimension, reset } from 'vs/base/browser/dom'; import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { IAction, IActionRunner } from 'vs/base/common/actions'; +import { Action, IAction, IActionRunner } from 'vs/base/common/actions'; import { ActionsOrientation, IActionViewItem, prepareActions } from 'vs/base/browser/ui/actionbar/actionbar'; import { Registry } from 'vs/platform/registry/common/platform'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -42,6 +42,9 @@ import { Codicon } from 'vs/base/common/codicons'; import { CompositeMenuActions } from 'vs/workbench/browser/actions'; import { IDropdownMenuActionViewItemOptions } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; import { WorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; +import { FilterWidget, IFilterWidgetOptions } from 'vs/workbench/browser/parts/views/viewFilter'; +import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; export interface IViewPaneOptions extends IPaneOptions { id: string; @@ -50,6 +53,12 @@ export interface IViewPaneOptions extends IPaneOptions { donotForwardArgs?: boolean; } +export interface IFilterViewPaneOptions extends IViewPaneOptions { + filterOptions: IFilterWidgetOptions; +} + +export const VIEWPANE_FILTER_ACTION = new Action('viewpane.action.filter'); + type WelcomeActionClassification = { owner: 'joaomoreno'; viewId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The view ID in which the welcome view button was clicked.' }; @@ -501,7 +510,11 @@ export abstract class ViewPane extends Pane implements IView { private setActions(): void { if (this.toolbar) { - this.toolbar.setActions(prepareActions(this.menuActions.getPrimaryActions()), prepareActions(this.menuActions.getSecondaryActions())); + const primaryActions = [...this.menuActions.getPrimaryActions()]; + if (this.shouldShowFilterInHeader()) { + primaryActions.unshift(VIEWPANE_FILTER_ACTION); + } + this.toolbar.setActions(prepareActions(primaryActions), prepareActions(this.menuActions.getSecondaryActions())); this.toolbar.context = this.getActionsContext(); } } @@ -520,6 +533,18 @@ export abstract class ViewPane extends Pane implements IView { } getActionViewItem(action: IAction, options?: IDropdownMenuActionViewItemOptions): IActionViewItem | undefined { + if (action.id === VIEWPANE_FILTER_ACTION.id) { + const that = this; + return new class extends BaseActionViewItem { + constructor() { super(null, action); } + override setFocusable(): void { /* noop input elements are focusable by default */ } + override get trapsArrowNavigation(): boolean { return true; } + override render(container: HTMLElement): void { + container.classList.add('viewpane-filter-container'); + append(container, that.getFilterWidget()!.element); + } + }; + } return createActionViewItem(this.instantiationService, action, { ...options, ...{ menuAsChild: action instanceof SubmenuItemAction } }); } @@ -626,6 +651,75 @@ export abstract class ViewPane extends Pane implements IView { shouldShowWelcome(): boolean { return false; } + + getFilterWidget(): FilterWidget | undefined { + return undefined; + } + + shouldShowFilterInHeader(): boolean { + return false; + } +} + +export abstract class FilterViewPane extends ViewPane { + + readonly filterWidget: FilterWidget; + private dimension: Dimension | undefined; + private filterContainer: HTMLElement | undefined; + + constructor( + options: IFilterViewPaneOptions, + @IKeybindingService keybindingService: IKeybindingService, + @IContextMenuService contextMenuService: IContextMenuService, + @IConfigurationService configurationService: IConfigurationService, + @IContextKeyService contextKeyService: IContextKeyService, + @IViewDescriptorService viewDescriptorService: IViewDescriptorService, + @IInstantiationService instantiationService: IInstantiationService, + @IOpenerService openerService: IOpenerService, + @IThemeService themeService: IThemeService, + @ITelemetryService telemetryService: ITelemetryService, + ) { + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); + const scopedContextKeyService = this._register(contextKeyService.createScoped(this.element)); + scopedContextKeyService.createKey('view', options.id); + this.filterWidget = this._register(instantiationService.createChild(new ServiceCollection([IContextKeyService, scopedContextKeyService])).createInstance(FilterWidget, options.filterOptions)); + } + + override getFilterWidget(): FilterWidget { + return this.filterWidget; + } + + protected override renderBody(container: HTMLElement): void { + super.renderBody(container); + this.filterContainer = append(container, $('.viewpane-filter-container')); + } + + protected override layoutBody(height: number, width: number): void { + super.layoutBody(height, width); + + this.dimension = new Dimension(width, height); + const wasFilterShownInHeader = !this.filterContainer?.hasChildNodes(); + const shouldShowFilterInHeader = this.shouldShowFilterInHeader(); + if (wasFilterShownInHeader !== shouldShowFilterInHeader) { + if (shouldShowFilterInHeader) { + reset(this.filterContainer!); + } + this.updateActions(); + if (!shouldShowFilterInHeader) { + append(this.filterContainer!, this.filterWidget.element); + height = height - 44; + } + } + this.filterWidget.layout(width); + this.layoutBodyContent(height, width); + } + + override shouldShowFilterInHeader(): boolean { + return !(this.dimension && this.dimension.width < 600 && this.dimension.height > 100); + } + + protected abstract layoutBodyContent(height: number, width: number): void; + } export abstract class ViewAction extends Action2 { diff --git a/src/vs/workbench/browser/parts/views/viewsService.ts b/src/vs/workbench/browser/parts/views/viewsService.ts index 18670e883ad..4e2b8c64478 100644 --- a/src/vs/workbench/browser/parts/views/viewsService.ts +++ b/src/vs/workbench/browser/parts/views/viewsService.ts @@ -27,7 +27,7 @@ import { PaneCompositeDescriptor, PaneCompositeRegistry, Extensions as PaneCompo import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { URI } from 'vs/base/common/uri'; import { IProgressIndicator } from 'vs/platform/progress/common/progress'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { FilterViewPaneContainer } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; @@ -139,7 +139,7 @@ export class ViewsService extends Disposable implements IViewsService { for (const viewDescriptor of views) { const disposables = new DisposableStore(); disposables.add(this.registerOpenViewAction(viewDescriptor)); - disposables.add(this.registerFocusViewAction(viewDescriptor, composite?.name && composite.name !== composite.id ? composite.name : CATEGORIES.View)); + disposables.add(this.registerFocusViewAction(viewDescriptor, composite?.name && composite.name !== composite.id ? composite.name : Categories.View)); disposables.add(this.registerResetViewLocationAction(viewDescriptor)); this.viewDisposable.set(viewDescriptor, disposables); } @@ -354,7 +354,7 @@ export class ViewsService extends Disposable implements IViewsService { return { value: localize('toggle view', "Toggle {0}", localizedTitle), original: `Toggle ${originalTitle}` }; } }, - category: CATEGORIES.View, + category: Categories.View, precondition: ContextKeyExpr.has(getEnabledViewContainerContextKey(viewContainer.id)), keybinding: keybindings ? { ...keybindings, weight: KeybindingWeight.WorkbenchContrib } : undefined, f1: true @@ -422,7 +422,7 @@ export class ViewsService extends Disposable implements IViewsService { return { value: localize('toggle view', "Toggle {0}", localizedTitle), original: `Toggle ${originalTitle}` }; } }, - category: CATEGORIES.View, + category: Categories.View, precondition: ContextKeyExpr.has(`${viewDescriptor.id}.active`), keybinding: viewDescriptor.openCommandActionDescriptor!.keybindings ? { ...viewDescriptor.openCommandActionDescriptor!.keybindings, weight: KeybindingWeight.WorkbenchContrib } : undefined, f1: true diff --git a/src/vs/workbench/browser/parts/views/viewsViewlet.ts b/src/vs/workbench/browser/parts/views/viewsViewlet.ts index 77ec32db719..b99ea671d92 100644 --- a/src/vs/workbench/browser/parts/views/viewsViewlet.ts +++ b/src/vs/workbench/browser/parts/views/viewsViewlet.ts @@ -19,7 +19,7 @@ import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/la import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; export interface IViewletViewOptions extends IViewPaneOptions { - fromExtensionId?: ExtensionIdentifier; + readonly fromExtensionId?: ExtensionIdentifier; } export abstract class FilterViewPaneContainer extends ViewPaneContainer { diff --git a/src/vs/workbench/browser/web.api.ts b/src/vs/workbench/browser/web.api.ts index 6b8360ae153..3b530fdbf83 100644 --- a/src/vs/workbench/browser/web.api.ts +++ b/src/vs/workbench/browser/web.api.ts @@ -697,6 +697,11 @@ export interface IDevelopmentOptions { */ readonly logLevel?: LogLevel; + /** + * Extension log level. + */ + readonly extensionLogLevel?: [string, LogLevel][]; + /** * Location of a module containing extension tests to run once the workbench is open. */ diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index e50d4867a52..b96c21e86d2 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -6,7 +6,7 @@ import { mark } from 'vs/base/common/performance'; import { domContentLoaded, detectFullscreen, getCookieValue } from 'vs/base/browser/dom'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { ILogService, ConsoleLogger, MultiplexLogService, getLogLevel } from 'vs/platform/log/common/log'; +import { ILogService, ConsoleLogger, MultiplexLogService, getLogLevel, ILoggerService } from 'vs/platform/log/common/log'; import { ConsoleLogInAutomationLogger } from 'vs/platform/log/browser/log'; import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { BrowserWorkbenchEnvironmentService, IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; @@ -35,7 +35,7 @@ import { IWorkbenchConstructionOptions, IWorkbench, ITunnel } from 'vs/workbench import { BrowserStorageService } from 'vs/workbench/services/storage/browser/storageService'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { BufferLogService } from 'vs/platform/log/common/bufferLog'; -import { FileLogger } from 'vs/platform/log/common/fileLog'; +import { FileLoggerService } from 'vs/platform/log/common/fileLog'; import { toLocalISOString } from 'vs/base/common/date'; import { isWorkspaceToOpen, isFolderToOpen } from 'vs/platform/window/common/window'; import { getSingleFolderWorkspaceIdentifier, getWorkspaceIdentifier } from 'vs/workbench/services/workspaces/browser/workspaces'; @@ -52,7 +52,7 @@ import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecy import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { localize } from 'vs/nls'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; @@ -268,7 +268,12 @@ export class BrowserMain extends Disposable { // Files const fileService = this._register(new FileService(logService)); serviceCollection.set(IWorkbenchFileService, fileService); - await this.registerFileSystemProviders(environmentService, fileService, remoteAgentService, logService, logsPath); + + // Logger + const loggerService = new FileLoggerService(logService, fileService); + serviceCollection.set(ILoggerService, loggerService); + + await this.registerFileSystemProviders(environmentService, fileService, remoteAgentService, logService, loggerService, logsPath); // URI Identity const uriIdentityService = new UriIdentityService(fileService); @@ -304,7 +309,7 @@ export class BrowserMain extends Disposable { }) ]); - userDataProfilesService.setEnablement(productService.quality !== 'stable' || configurationService.getValue(PROFILES_ENABLEMENT_CONFIG)); + userDataProfilesService.setEnablement(!environmentService.remoteAuthority && (productService.quality !== 'stable' || configurationService.getValue(PROFILES_ENABLEMENT_CONFIG))); this._register(configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(PROFILES_ENABLEMENT_CONFIG)) { userDataProfilesService.setEnablement(!!configurationService.getValue(PROFILES_ENABLEMENT_CONFIG)); @@ -374,7 +379,7 @@ export class BrowserMain extends Disposable { return { serviceCollection, configurationService, logService }; } - private async registerFileSystemProviders(environmentService: IWorkbenchEnvironmentService, fileService: IWorkbenchFileService, remoteAgentService: IRemoteAgentService, logService: BufferLogService, logsPath: URI): Promise { + private async registerFileSystemProviders(environmentService: IWorkbenchEnvironmentService, fileService: IWorkbenchFileService, remoteAgentService: IRemoteAgentService, logService: BufferLogService, loggerService: ILoggerService, logsPath: URI): Promise { // IndexedDB is used for logging and user data let indexedDB: IndexedDB | undefined; @@ -401,7 +406,7 @@ export class BrowserMain extends Disposable { logService.logger = new MultiplexLogService(coalesce([ new ConsoleLogger(logService.getLevel()), - new FileLogger('window', environmentService.logFile, logService.getLevel(), false, fileService), + loggerService.createLogger(environmentService.logFile, { name: 'window' }), // Extension development test CLI: forward everything to test runner environmentService.isExtensionDevelopment && !!environmentService.extensionTestsLocationURI ? new ConsoleLogInAutomationLogger(logService.getLevel()) : undefined ])); @@ -436,7 +441,7 @@ export class BrowserMain extends Disposable { super({ id: 'workbench.action.resetUserData', title: { original: 'Reset User Data', value: localize('reset', "Reset User Data") }, - category: CATEGORIES.Developer, + category: Categories.Developer, menu: { id: MenuId.CommandPalette } diff --git a/src/vs/workbench/browser/window.ts b/src/vs/workbench/browser/window.ts index 52dcef61e86..724e130fcd0 100644 --- a/src/vs/workbench/browser/window.ts +++ b/src/vs/workbench/browser/window.ts @@ -26,6 +26,7 @@ import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/envir import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { BrowserLifecycleService } from 'vs/workbench/services/lifecycle/browser/lifecycleService'; import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { IHostService } from 'vs/workbench/services/host/browser/host'; export class BrowserWindow extends Disposable { @@ -36,7 +37,8 @@ export class BrowserWindow extends Disposable { @ILabelService private readonly labelService: ILabelService, @IProductService private readonly productService: IProductService, @IBrowserWorkbenchEnvironmentService private readonly environmentService: IBrowserWorkbenchEnvironmentService, - @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService + @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, + @IHostService private readonly hostService: IHostService ) { super(); @@ -204,29 +206,45 @@ export class BrowserWindow extends Disposable { invokeProtocolHandler(); const showProtocolUrlOpenedDialog = async () => { + const { downloadUrl } = this.productService; + let detail = localize( + 'openExternalDialogDetail.v2', + "We launched {0} on your computer.\n\nIf {1} did not launch, try again or install it below.", + this.productService.nameLong, + this.productService.nameLong + ); + const options = [ + localize('openExternalDialogButtonClose', "Close tab"), + localize('openExternalDialogButtonRetry', "Try again"), + localize('openExternalDialogButtonInstall.v3', "Install"), + localize('openExternalDialogButtonCancel', "Cancel") + ]; + if (downloadUrl === undefined) { + options.splice(2, 1); + detail = localize( + 'openExternalDialogDetailNoInstall', + "We launched {0} on your computer.\n\nIf {1} did not launch, try again below.", + this.productService.nameLong, + this.productService.nameLong + ); + } + const showResult = await this.dialogService.show( Severity.Info, localize('openExternalDialogTitle', "All done. You can close this tab now."), - [ - localize('openExternalDialogButtonRetry', "Try again"), - localize('openExternalDialogButtonInstall', "Install {0}", this.productService.nameLong), - localize('openExternalDialogButtonContinue', "Continue here") - ], + options, { - cancelId: 2, - detail: localize('openExternalDialogDetail', "We tried opening {0} on your computer.", this.productService.nameLong) + cancelId: downloadUrl === undefined ? 2 : 3, + detail }, ); if (showResult.choice === 0) { - invokeProtocolHandler(); + this.hostService.close(); } else if (showResult.choice === 1) { - // Route the user to the appropriate install link - await this.openerService.open(URI.parse( - this.productService.quality === 'stable' - ? `http://aka.ms/vscode-install` - : `http://aka.ms/vscode-install-insiders` - )); + invokeProtocolHandler(); + } else if (showResult.choice === 2 && downloadUrl !== undefined) { + await this.openerService.open(URI.parse(downloadUrl)); // Re-show the dialog so that the user can come back after installing and try again showProtocolUrlOpenedDialog(); diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 4ebd6498e72..4cbc72bc837 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -18,7 +18,7 @@ const registry = Registry.as(ConfigurationExtensions.Con (function registerConfiguration(): void { // Migration support - Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ConfigurationMigrationWorkbenchContribution, 'ConfigurationMigrationWorkbenchContribution', LifecyclePhase.Eventually); + Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ConfigurationMigrationWorkbenchContribution, LifecyclePhase.Eventually); // Workbench registry.registerConfiguration({ @@ -448,6 +448,7 @@ const registry = Registry.as(ConfigurationExtensions.Con localize('workbench.reduceMotion.auto', "Render with reduced motion based on OS configuration."), ], default: 'auto', + tags: ['accessibility'], enum: ['on', 'off', 'auto'] }, 'workbench.layoutControl.enabled': { diff --git a/src/vs/workbench/common/actions.ts b/src/vs/workbench/common/actions.ts index 64742f9555f..3b2148055a5 100644 --- a/src/vs/workbench/common/actions.ts +++ b/src/vs/workbench/common/actions.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { localize } from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands'; @@ -121,11 +120,3 @@ Registry.add(Extensions.WorkbenchActions, new class implements IWorkbenchActionR } } }); - -export const CATEGORIES = { - View: { value: localize('view', "View"), original: 'View' }, - Help: { value: localize('help', "Help"), original: 'Help' }, - Test: { value: localize('test', "Test"), original: 'Test' }, - Preferences: { value: localize('preferences', "Preferences"), original: 'Preferences' }, - Developer: { value: localize({ key: 'developer', comment: ['A developer on Code itself or someone diagnosing issues in Code'] }, "Developer"), original: 'Developer' } -}; diff --git a/src/vs/workbench/common/contributions.ts b/src/vs/workbench/common/contributions.ts index afa24265570..592f64c9f89 100644 --- a/src/vs/workbench/common/contributions.ts +++ b/src/vs/workbench/common/contributions.ts @@ -9,6 +9,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { runWhenIdle, IdleDeadline } from 'vs/base/common/async'; import { mark } from 'vs/base/common/performance'; import { ILogService } from 'vs/platform/log/common/log'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; /** * A workbench contribution that will be loaded when the workbench starts and disposed when the workbench shuts down. @@ -23,21 +24,15 @@ export namespace Extensions { type IWorkbenchContributionSignature = new (...services: Service) => IWorkbenchContribution; -interface IWorkbenchContributionRegistration { - readonly id: string; - readonly ctor: IConstructorSignature; -} - export interface IWorkbenchContributionsRegistry { /** * Registers a workbench contribution to the platform that will be loaded when the workbench starts and disposed when * the workbench shuts down. * - * @param id the identifier of the contribution. * @param phase the lifecycle phase when to instantiate the contribution. */ - registerWorkbenchContribution(contribution: IWorkbenchContributionSignature, id: string, phase: LifecyclePhase): void; + registerWorkbenchContribution(contribution: IWorkbenchContributionSignature, phase: LifecyclePhase): void; /** * Starts the registry by providing the required services. @@ -50,15 +45,15 @@ class WorkbenchContributionsRegistry implements IWorkbenchContributionsRegistry private instantiationService: IInstantiationService | undefined; private lifecycleService: ILifecycleService | undefined; private logService: ILogService | undefined; + private environmentService: IEnvironmentService | undefined; - private readonly toBeInstantiated = new Map(); + private readonly toBeInstantiated = new Map[]>(); - registerWorkbenchContribution(ctor: IConstructorSignature, id: string, phase: LifecyclePhase = LifecyclePhase.Starting): void { - const contribution = { id, ctor }; + registerWorkbenchContribution(ctor: IConstructorSignature, phase: LifecyclePhase = LifecyclePhase.Starting): void { // Instantiate directly if we are already matching the provided phase - if (this.instantiationService && this.lifecycleService && this.logService && this.lifecycleService.phase >= phase) { - this.safeCreateInstance(this.instantiationService, this.logService, contribution, phase); + if (this.instantiationService && this.lifecycleService && this.logService && this.environmentService && this.lifecycleService.phase >= phase) { + this.safeCreateInstance(this.instantiationService, this.logService, this.environmentService, ctor, phase); } // Otherwise keep contributions by lifecycle phase @@ -69,7 +64,7 @@ class WorkbenchContributionsRegistry implements IWorkbenchContributionsRegistry this.toBeInstantiated.set(phase, toBeInstantiated); } - toBeInstantiated.push(contribution); + toBeInstantiated.push(ctor as IConstructorSignature); } } @@ -77,26 +72,27 @@ class WorkbenchContributionsRegistry implements IWorkbenchContributionsRegistry const instantiationService = this.instantiationService = accessor.get(IInstantiationService); const lifecycleService = this.lifecycleService = accessor.get(ILifecycleService); const logService = this.logService = accessor.get(ILogService); + const environmentService = this.environmentService = accessor.get(IEnvironmentService); for (const phase of [LifecyclePhase.Starting, LifecyclePhase.Ready, LifecyclePhase.Restored, LifecyclePhase.Eventually]) { - this.instantiateByPhase(instantiationService, lifecycleService, logService, phase); + this.instantiateByPhase(instantiationService, lifecycleService, logService, environmentService, phase); } } - private instantiateByPhase(instantiationService: IInstantiationService, lifecycleService: ILifecycleService, logService: ILogService, phase: LifecyclePhase): void { + private instantiateByPhase(instantiationService: IInstantiationService, lifecycleService: ILifecycleService, logService: ILogService, environmentService: IEnvironmentService, phase: LifecyclePhase): void { // Instantiate contributions directly when phase is already reached if (lifecycleService.phase >= phase) { - this.doInstantiateByPhase(instantiationService, logService, phase); + this.doInstantiateByPhase(instantiationService, logService, environmentService, phase); } // Otherwise wait for phase to be reached else { - lifecycleService.when(phase).then(() => this.doInstantiateByPhase(instantiationService, logService, phase)); + lifecycleService.when(phase).then(() => this.doInstantiateByPhase(instantiationService, logService, environmentService, phase)); } } - private doInstantiateByPhase(instantiationService: IInstantiationService, logService: ILogService, phase: LifecyclePhase): void { + private doInstantiateByPhase(instantiationService: IInstantiationService, logService: ILogService, environmentService: IEnvironmentService, phase: LifecyclePhase): void { const toBeInstantiated = this.toBeInstantiated.get(phase); if (toBeInstantiated) { this.toBeInstantiated.delete(phase); @@ -108,7 +104,7 @@ class WorkbenchContributionsRegistry implements IWorkbenchContributionsRegistry mark(`code/willCreateWorkbenchContributions/${phase}`); for (const ctor of toBeInstantiated) { - this.safeCreateInstance(instantiationService, logService, ctor, phase); // catch error so that other contributions are still considered + this.safeCreateInstance(instantiationService, logService, environmentService, ctor, phase); // catch error so that other contributions are still considered } mark(`code/didCreateWorkbenchContributions/${phase}`); @@ -123,7 +119,7 @@ class WorkbenchContributionsRegistry implements IWorkbenchContributionsRegistry const instantiateSome = (idle: IdleDeadline) => { while (i < toBeInstantiated.length) { const ctor = toBeInstantiated[i++]; - this.safeCreateInstance(instantiationService, logService, ctor, phase); // catch error so that other contributions are still considered + this.safeCreateInstance(instantiationService, logService, environmentService, ctor, phase); // catch error so that other contributions are still considered if (idle.timeRemaining() < 1) { // time is up -> reschedule runWhenIdle(instantiateSome, forcedTimeout); @@ -136,19 +132,19 @@ class WorkbenchContributionsRegistry implements IWorkbenchContributionsRegistry } } - private safeCreateInstance(instantiationService: IInstantiationService, logService: ILogService, contribution: IWorkbenchContributionRegistration, phase: LifecyclePhase): void { + private safeCreateInstance(instantiationService: IInstantiationService, logService: ILogService, environmentService: IEnvironmentService, ctor: IConstructorSignature, phase: LifecyclePhase): void { const now: number | undefined = phase < LifecyclePhase.Restored ? Date.now() : undefined; try { - instantiationService.createInstance(contribution.ctor); + instantiationService.createInstance(ctor); } catch (error) { - logService.error(`Unable to instantiate workbench contribution ${contribution.id}.`, error); + logService.error(`Unable to instantiate workbench contribution ${ctor.name}.`, error); } - if (typeof now === 'number') { + if (typeof now === 'number' && !environmentService.isBuilt /* only log out of sources where we have good ctor names */) { const time = Date.now() - now; - if (time > 5) { - logService.warn(`Workbench contribution ${contribution.id} blocked restore phase by ${time}ms.`); + if (time > 20) { + logService.warn(`Workbench contribution ${ctor.name} blocked restore phase by ${time}ms.`); } } } diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index b89e7d795d6..8c49d1bf96b 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -639,6 +639,13 @@ export const ACTIVITY_BAR_SETTINGS_PROFILE_HOVER_FOREGROUND = registerColor('act hcLight: ACTIVITY_BAR_FOREGROUND }, localize('activityBarItem.settingsProfilesHoverForeground', "Foreground color for the settings profile entry on the activity bar when hovering.")); +export const ACTIVITY_BAR_SETTINGS_PROFILE_BACKGROUND = registerColor('activityBarItem.settingsProfilesBackground', { + dark: lighten(ACTIVITY_BAR_BACKGROUND, 0.5), + light: darken(ACTIVITY_BAR_BACKGROUND, 0.12), + hcDark: null, + hcLight: null +}, localize('statusBarItemSettingsProfileBackground', "Background color for the settings profile entry on the activity bar.")); + // < --- Remote --- > export const STATUS_BAR_HOST_NAME_BACKGROUND = registerColor('statusBarItem.remoteBackground', { diff --git a/src/vs/workbench/contrib/audioCues/browser/audioCueService.ts b/src/vs/workbench/contrib/audioCues/browser/audioCueService.ts index 1c26424410f..ce7a4d8a1b0 100644 --- a/src/vs/workbench/contrib/audioCues/browser/audioCueService.ts +++ b/src/vs/workbench/contrib/audioCues/browser/audioCueService.ts @@ -152,7 +152,8 @@ export class Sound { public static readonly foldedArea = Sound.register({ fileName: 'foldedAreas.mp3' }); public static readonly break = Sound.register({ fileName: 'break.mp3' }); public static readonly quickFixes = Sound.register({ fileName: 'quickFixes.mp3' }); - public static readonly taskEnded = Sound.register({ fileName: 'taskEnded.mp3' }); + public static readonly taskCompleted = Sound.register({ fileName: 'taskCompleted.mp3' }); + public static readonly taskFailed = Sound.register({ fileName: 'taskFailed.mp3' }); public static readonly terminalBell = Sound.register({ fileName: 'terminalBell.mp3' }); private constructor(public readonly fileName: string) { } @@ -201,6 +202,12 @@ export class AudioCue { settingsKey: 'audioCues.lineHasInlineSuggestion', }); + public static readonly terminalQuickFix = AudioCue.register({ + name: localize('audioCues.terminalQuickFix.name', 'Terminal Quick Fix'), + sound: Sound.quickFixes, + settingsKey: 'audioCues.terminalQuickFix', + }); + public static readonly onDebugBreak = AudioCue.register({ name: localize('audioCues.onDebugBreak.name', 'Debugger Stopped on Breakpoint'), sound: Sound.break, @@ -213,10 +220,16 @@ export class AudioCue { settingsKey: 'audioCues.noInlayHints' }); - public static readonly taskEnded = AudioCue.register({ - name: localize('audioCues.taskEnded', 'Task Ended'), - sound: Sound.taskEnded, - settingsKey: 'audioCues.taskEnded' + public static readonly taskCompleted = AudioCue.register({ + name: localize('audioCues.taskCompleted', 'Task Completed'), + sound: Sound.taskCompleted, + settingsKey: 'audioCues.taskCompleted' + }); + + public static readonly taskFailed = AudioCue.register({ + name: localize('audioCues.taskFailed', 'Task Failed'), + sound: Sound.taskFailed, + settingsKey: 'audioCues.taskFailed' }); public static readonly terminalBell = AudioCue.register({ diff --git a/src/vs/workbench/contrib/audioCues/browser/audioCues.contribution.ts b/src/vs/workbench/contrib/audioCues/browser/audioCues.contribution.ts index facb74870e4..dc4693613a8 100644 --- a/src/vs/workbench/contrib/audioCues/browser/audioCues.contribution.ts +++ b/src/vs/workbench/contrib/audioCues/browser/audioCues.contribution.ts @@ -6,7 +6,7 @@ import { localize } from 'vs/nls'; import { registerAction2 } from 'vs/platform/actions/common/actions'; import { Extensions as ConfigurationExtensions, IConfigurationPropertySchema, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { AudioCueLineDebuggerContribution } from 'vs/workbench/contrib/audioCues/browser/audioCueDebuggerContribution'; @@ -15,10 +15,10 @@ import { AudioCueService, IAudioCueService } from 'vs/workbench/contrib/audioCue import { ShowAudioCueHelp } from 'vs/workbench/contrib/audioCues/browser/commands'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -registerSingleton(IAudioCueService, AudioCueService, false); +registerSingleton(IAudioCueService, AudioCueService, InstantiationType.Delayed); -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(AudioCueLineFeatureContribution, 'AudioCueLineFeatureContribution', LifecyclePhase.Restored); -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(AudioCueLineDebuggerContribution, 'AudioCueLineDebuggerContribution', LifecyclePhase.Restored); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(AudioCueLineFeatureContribution, LifecyclePhase.Restored); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(AudioCueLineDebuggerContribution, LifecyclePhase.Restored); const audioCueFeatureBase: IConfigurationPropertySchema = { 'type': 'string', @@ -29,27 +29,30 @@ const audioCueFeatureBase: IConfigurationPropertySchema = { localize('audioCues.enabled.on', "Enable audio cue."), localize('audioCues.enabled.off', "Disable audio cue.") ], + tags: ['accessibility'] }; Registry.as(ConfigurationExtensions.Configuration).registerConfiguration({ 'properties': { 'audioCues.enabled': { markdownDeprecationMessage: 'Deprecated. Use the specific setting for each audio cue instead (`audioCues.*`).', + tags: ['accessibility'] }, 'audioCues.volume': { 'description': localize('audioCues.volume', "The volume of the audio cues in percent (0-100)."), 'type': 'number', 'minimum': 0, 'maximum': 100, - 'default': 70 + 'default': 70, + tags: ['accessibility'] }, 'audioCues.lineHasBreakpoint': { 'description': localize('audioCues.lineHasBreakpoint', "Plays a sound when the active line has a breakpoint."), - ...audioCueFeatureBase, + ...audioCueFeatureBase }, 'audioCues.lineHasInlineSuggestion': { 'description': localize('audioCues.lineHasInlineSuggestion', "Plays a sound when the active line has an inline suggestion."), - ...audioCueFeatureBase, + ...audioCueFeatureBase }, 'audioCues.lineHasError': { 'description': localize('audioCues.lineHasError', "Plays a sound when the active line has an error."), @@ -72,8 +75,16 @@ Registry.as(ConfigurationExtensions.Configuration).regis 'description': localize('audioCues.noInlayHints', "Plays a sound when trying to read a line with inlay hints that has no inlay hints."), ...audioCueFeatureBase, }, - 'audioCues.taskEnded': { - 'description': localize('audioCues.taskEnded', "Plays a sound when a task ends."), + 'audioCues.taskCompleted': { + 'description': localize('audioCues.taskCompleted', "Plays a sound when a task completed."), + ...audioCueFeatureBase, + }, + 'audioCues.taskFailed': { + 'description': localize('audioCues.taskFailed', "Plays a sound when a task fails (non-zero exit code)."), + ...audioCueFeatureBase, + }, + 'audioCues.terminalQuickFix': { + 'description': localize('audioCues.terminalQuickFix', "Plays a sound when a terminal quick fixes are available"), ...audioCueFeatureBase, }, } diff --git a/src/vs/workbench/contrib/audioCues/browser/media/taskCompleted.mp3 b/src/vs/workbench/contrib/audioCues/browser/media/taskCompleted.mp3 new file mode 100644 index 00000000000..dd3fecbef57 Binary files /dev/null and b/src/vs/workbench/contrib/audioCues/browser/media/taskCompleted.mp3 differ diff --git a/src/vs/workbench/contrib/audioCues/browser/media/taskEnded.mp3 b/src/vs/workbench/contrib/audioCues/browser/media/taskEnded.mp3 deleted file mode 100644 index 37b832b02b5..00000000000 Binary files a/src/vs/workbench/contrib/audioCues/browser/media/taskEnded.mp3 and /dev/null differ diff --git a/src/vs/workbench/contrib/audioCues/browser/media/taskFailed.mp3 b/src/vs/workbench/contrib/audioCues/browser/media/taskFailed.mp3 new file mode 100644 index 00000000000..7a155fa67d3 Binary files /dev/null and b/src/vs/workbench/contrib/audioCues/browser/media/taskFailed.mp3 differ diff --git a/src/vs/workbench/contrib/bracketPairColorizer2Telemetry/browser/bracketPairColorizer2Telemetry.contribution.ts b/src/vs/workbench/contrib/bracketPairColorizer2Telemetry/browser/bracketPairColorizer2Telemetry.contribution.ts index af4f63a1d65..c1051e9d4cf 100644 --- a/src/vs/workbench/contrib/bracketPairColorizer2Telemetry/browser/bracketPairColorizer2Telemetry.contribution.ts +++ b/src/vs/workbench/contrib/bracketPairColorizer2Telemetry/browser/bracketPairColorizer2Telemetry.contribution.ts @@ -51,5 +51,5 @@ class BracketPairColorizer2TelemetryContribution { } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(BracketPairColorizer2TelemetryContribution, 'BracketPairColorizer2TelemetryContribution', LifecyclePhase.Restored); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(BracketPairColorizer2TelemetryContribution, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts index ae0212db0c5..00a833511b0 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts @@ -16,7 +16,7 @@ import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { IProgress, IProgressStep, Progress } from 'vs/platform/progress/common/progress'; @@ -184,7 +184,7 @@ export class BulkEditService implements IBulkEditService { let edits = liftEdits(Array.isArray(editsIn) ? editsIn : editsIn.edits); if (edits.length === 0) { - return { ariaSummary: localize('nothing', "Made no edits") }; + return { ariaSummary: localize('nothing', "Made no edits"), isApplied: false }; } if (this._previewHandler && (options?.showPreview || edits.some(value => value.metadata?.needsConfirmation))) { @@ -244,11 +244,11 @@ export class BulkEditService implements IBulkEditService { // when enabled (option AND setting) loop over all dirty working copies and trigger save // for those that were involved in this bulk edit operation. - if (options?.respectAutoSaveConfig && this._configService.getValue(autoSaveSetting) === true && resources.length > 1) { + if (options?.respectAutoSaveConfig && this._configService.getValue(autoSaveSetting) === true) { await this._saveAll(resources); } - return { ariaSummary: bulkEdit.ariaMessage() }; + return { ariaSummary: bulkEdit.ariaMessage(), isApplied: edits.length > 0 }; } catch (err) { // console.log('apply FAILED'); // console.log(err); @@ -289,7 +289,7 @@ export class BulkEditService implements IBulkEditService { } } -registerSingleton(IBulkEditService, BulkEditService, true); +registerSingleton(IBulkEditService, BulkEditService, InstantiationType.Delayed); const autoSaveSetting = 'files.refactoring.autoSave'; diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts index 19164716538..4d51be59e2f 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts @@ -323,9 +323,7 @@ registerAction2(class ToggleGrouping extends Action2 { }); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( - BulkEditPreviewContribution, - 'BulkEditPreviewContribution', - LifecyclePhase.Ready + BulkEditPreviewContribution, LifecyclePhase.Ready ); const refactorPreviewViewIcon = registerIcon('refactor-preview-view-icon', Codicon.lightbulb, localize('refactorPreviewViewIcon', 'View icon of the refactor preview view.')); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts index cb829e3eb0b..33aae4de46f 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts @@ -26,9 +26,7 @@ import { ResourceLabels, IResourceLabelsContainer } from 'vs/workbench/browser/l import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import Severity from 'vs/base/common/severity'; import { basename, dirname } from 'vs/base/common/resources'; -import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; -import { IAction } from 'vs/base/common/actions'; -import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { MenuId } from 'vs/platform/actions/common/actions'; import { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; @@ -78,10 +76,9 @@ export class BulkEditPane extends ViewPane { @ILabelService private readonly _labelService: ILabelService, @ITextModelService private readonly _textModelService: ITextModelService, @IDialogService private readonly _dialogService: IDialogService, - @IMenuService private readonly _menuService: IMenuService, @IContextMenuService private readonly _contextMenuService: IContextMenuService, - @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IStorageService private readonly _storageService: IStorageService, + @IContextKeyService contextKeyService: IContextKeyService, @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IKeybindingService keybindingService: IKeybindingService, @IContextMenuService contextMenuService: IContextMenuService, @@ -92,13 +89,13 @@ export class BulkEditPane extends ViewPane { ) { super( { ...options, titleMenuId: MenuId.BulkEditTitle }, - keybindingService, contextMenuService, configurationService, _contextKeyService, viewDescriptorService, _instaService, openerService, themeService, telemetryService + keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, _instaService, openerService, themeService, telemetryService ); this.element.classList.add('bulk-edit-panel', 'show-file-icons'); - this._ctxHasCategories = BulkEditPane.ctxHasCategories.bindTo(_contextKeyService); - this._ctxGroupByFile = BulkEditPane.ctxGroupByFile.bindTo(_contextKeyService); - this._ctxHasCheckedChanges = BulkEditPane.ctxHasCheckedChanges.bindTo(_contextKeyService); + this._ctxHasCategories = BulkEditPane.ctxHasCategories.bindTo(contextKeyService); + this._ctxGroupByFile = BulkEditPane.ctxGroupByFile.bindTo(contextKeyService); + this._ctxHasCheckedChanges = BulkEditPane.ctxHasCheckedChanges.bindTo(contextKeyService); } override dispose(): void { @@ -380,16 +377,11 @@ export class BulkEditPane extends ViewPane { } private _onContextMenu(e: ITreeContextMenuEvent): void { - const menu = this._menuService.createMenu(MenuId.BulkEditContext, this._contextKeyService); - const actions: IAction[] = []; - createAndFillInContextMenuActions(menu, undefined, actions); this._contextMenuService.showContextMenu({ - getActions: () => actions, - getAnchor: () => e.anchor, - onHide: () => { - menu.dispose(); - } + menuId: MenuId.BulkEditContext, + contextKeyService: this.contextKeyService, + getAnchor: () => e.anchor }); } } diff --git a/src/vs/workbench/contrib/codeActions/browser/codeActions.contribution.ts b/src/vs/workbench/contrib/codeActions/browser/codeActions.contribution.ts index f3d4de633ba..afcf9915643 100644 --- a/src/vs/workbench/contrib/codeActions/browser/codeActions.contribution.ts +++ b/src/vs/workbench/contrib/codeActions/browser/codeActions.contribution.ts @@ -30,4 +30,4 @@ class WorkbenchConfigurationContribution { } Registry.as(WorkbenchExtensions.Workbench) - .registerWorkbenchContribution(WorkbenchConfigurationContribution, 'WorkbenchConfigurationContribution', LifecyclePhase.Eventually); + .registerWorkbenchContribution(WorkbenchConfigurationContribution, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.css b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.css index 50fde909c9f..74b3d08aebd 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.css +++ b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.css @@ -38,6 +38,10 @@ visibility: visible; } +.monaco-workbench .simple-find-part.suppress-transition { + transition: none; +} + .monaco-workbench .simple-find-part.visible-transition { top: 0; } diff --git a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts index fbb881c8f56..10d1756462d 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts @@ -254,7 +254,7 @@ export abstract class SimpleFindWidget extends Widget { return this._domNode; } - public reveal(initialInput?: string): void { + public reveal(initialInput?: string, animated = true): void { if (initialInput) { this._findInput.setValue(initialInput); } @@ -269,9 +269,16 @@ export abstract class SimpleFindWidget extends Widget { this.layout(); setTimeout(() => { + this._innerDomNode.classList.toggle('suppress-transition', !animated); this._innerDomNode.classList.add('visible', 'visible-transition'); this._innerDomNode.setAttribute('aria-hidden', 'false'); this._findInput.select(); + + if (!animated) { + setTimeout(() => { + this._innerDomNode.classList.remove('suppress-transition'); + }, 0); + } }, 0); } @@ -285,20 +292,22 @@ export abstract class SimpleFindWidget extends Widget { setTimeout(() => { this._innerDomNode.classList.add('visible', 'visible-transition'); + this._innerDomNode.setAttribute('aria-hidden', 'false'); }, 0); } - public hide(): void { + public hide(animated = true): void { if (this._isVisible) { + this._innerDomNode.classList.toggle('suppress-transition', !animated); this._innerDomNode.classList.remove('visible-transition'); this._innerDomNode.setAttribute('aria-hidden', 'true'); // Need to delay toggling visibility until after Transition, then visibility hidden - removes from tabIndex list setTimeout(() => { this._isVisible = false; this.updateButtons(this._foundMatch); - this._innerDomNode.classList.remove('visible'); - }, 200); + this._innerDomNode.classList.remove('visible', 'suppress-transition'); + }, animated ? 200 : 0); } } diff --git a/src/vs/workbench/contrib/codeEditor/browser/inspectKeybindings.ts b/src/vs/workbench/contrib/codeEditor/browser/inspectKeybindings.ts index 73808153cd7..dc493bf295c 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/inspectKeybindings.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/inspectKeybindings.ts @@ -8,7 +8,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; class InspectKeyMap extends Action2 { @@ -17,7 +17,7 @@ class InspectKeyMap extends Action2 { super({ id: 'workbench.action.inspectKeyMappings', title: { value: localize('workbench.action.inspectKeyMap', "Inspect Key Mappings"), original: 'Inspect Key Mappings' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true }); } @@ -38,7 +38,7 @@ class InspectKeyMapJSON extends Action2 { super({ id: 'workbench.action.inspectKeyMappingsJSON', title: { value: localize('workbench.action.inspectKeyMapJSON', "Inspect Key Mappings (JSON)"), original: 'Inspect Key Mappings (JSON)' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true }); } diff --git a/src/vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint.ts b/src/vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint.ts index dd288894ba7..6e524e57ce8 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint.ts @@ -16,7 +16,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ITextMateService } from 'vs/workbench/services/textMate/browser/textMate'; import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages'; -import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; +import { IExtensionResourceLoaderService } from 'vs/platform/extensionResourceLoader/common/extensionResourceLoader'; import { hash } from 'vs/base/common/hash'; import { Disposable } from 'vs/base/common/lifecycle'; diff --git a/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsOutline.ts b/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsOutline.ts index 3cbf02252d0..2651e59faff 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsOutline.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsOutline.ts @@ -103,6 +103,11 @@ class DocumentSymbolBreadcrumbsSource implements IBreadcrumbsDataSource { private readonly _disposables = new DisposableStore(); @@ -156,8 +161,9 @@ class DocumentSymbolsOutline implements IOutline { } }; const comparator = new DocumentSymbolComparator(); + const initialState = textResourceConfigurationService.getValue(_editor.getModel()?.uri, OutlineConfigKeys.collapseItems); const options = { - collapseByDefault: target === OutlineTarget.Breadcrumbs, + collapseByDefault: target === OutlineTarget.Breadcrumbs || (target === OutlineTarget.OutlinePane && initialState === DocumentSymbolsOutlineInitialState.Collapsed), expandOnlyOnTwistieClick: true, multipleSelectionSupport: false, identityProvider: new DocumentSymbolIdentityProvider(), @@ -432,4 +438,4 @@ class DocumentSymbolsOutlineCreator implements IOutlineCreator(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DocumentSymbolsOutlineCreator, 'DocumentSymbolsOutlineCreator', LifecyclePhase.Eventually); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DocumentSymbolsOutlineCreator, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts index 2049f12a728..4b966ec9bd1 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts @@ -402,4 +402,4 @@ export class SaveParticipantsContribution extends Disposable implements IWorkben } const workbenchContributionsRegistry = Registry.as(WorkbenchContributionsExtensions.Workbench); -workbenchContributionsRegistry.registerWorkbenchContribution(SaveParticipantsContribution, 'SaveParticipantsContribution', LifecyclePhase.Restored); +workbenchContributionsRegistry.registerWorkbenchContribution(SaveParticipantsContribution, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.css b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.css index b025d4b2528..299f2e37e3a 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.css +++ b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.css @@ -4,7 +4,8 @@ *--------------------------------------------------------------------------------------------*/ .suggest-input-container { - padding: 2px 4px; + padding: 2px 6px; + border-radius: 2px; } .suggest-input-container .monaco-editor-background, diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleMinimap.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleMinimap.ts index f0ea2a89e33..6703497db2d 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleMinimap.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleMinimap.ts @@ -7,7 +7,7 @@ import { localize } from 'vs/nls'; import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; export class ToggleMinimapAction extends Action2 { @@ -22,12 +22,11 @@ export class ToggleMinimapAction extends Action2 { original: 'Toggle Minimap', mnemonicTitle: localize({ key: 'miMinimap', comment: ['&& denotes a mnemonic'] }, "&&Minimap") }, - category: CATEGORIES.View, + category: Categories.View, f1: true, toggled: ContextKeyExpr.equals('config.editor.minimap.enabled', true), menu: { - id: MenuId.MenubarViewMenu, - group: '5_editor', + id: MenuId.MenubarEditorFeaturesMenu, order: 2 } }); diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts index 58424f5f37c..fea60d0515a 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts @@ -64,7 +64,7 @@ class MultiCursorModifierContextKeyController implements IWorkbenchContribution } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(MultiCursorModifierContextKeyController, 'MultiCursorModifierContextKeyController', LifecyclePhase.Restored); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(MultiCursorModifierContextKeyController, LifecyclePhase.Restored); registerAction2(ToggleMultiCursorModifierAction); diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleRenderControlCharacter.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleRenderControlCharacter.ts index 0990dd2f454..9e3029dd0d8 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleRenderControlCharacter.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleRenderControlCharacter.ts @@ -7,7 +7,7 @@ import { localize } from 'vs/nls'; import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { CATEGORIES, } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; export class ToggleRenderControlCharacterAction extends Action2 { @@ -22,12 +22,11 @@ export class ToggleRenderControlCharacterAction extends Action2 { mnemonicTitle: localize({ key: 'miToggleRenderControlCharacters', comment: ['&& denotes a mnemonic'] }, "Render &&Control Characters"), original: 'Toggle Control Characters' }, - category: CATEGORIES.View, + category: Categories.View, f1: true, toggled: ContextKeyExpr.equals('config.editor.renderControlCharacters', true), menu: { - id: MenuId.MenubarViewMenu, - group: '5_editor', + id: MenuId.MenubarEditorFeaturesMenu, order: 5 } }); diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace.ts index c8bab9f321f..88e06bc7547 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace.ts @@ -7,7 +7,7 @@ import { localize } from 'vs/nls'; import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { CATEGORIES, } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; class ToggleRenderWhitespaceAction extends Action2 { @@ -22,12 +22,11 @@ class ToggleRenderWhitespaceAction extends Action2 { mnemonicTitle: localize({ key: 'miToggleRenderWhitespace', comment: ['&& denotes a mnemonic'] }, "&&Render Whitespace"), original: 'Toggle Render Whitespace' }, - category: CATEGORIES.View, + category: Categories.View, f1: true, toggled: ContextKeyExpr.notEquals('config.editor.renderWhitespace', 'none'), menu: { - id: MenuId.MenubarViewMenu, - group: '5_editor', + id: MenuId.MenubarEditorFeaturesMenu, order: 4 } }); diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts index 947a3d1cfb4..d2774c7642d 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts @@ -6,11 +6,11 @@ import * as nls from 'vs/nls'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { IActiveCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { IActiveCodeEditor, ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution, registerDiffEditorContribution } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; -import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { IDiffEditorContribution, IEditorContribution } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; @@ -93,30 +93,37 @@ class ToggleWordWrapAction extends EditorAction { writeTransientState(model, newState, codeEditorService); // if we are in a diff editor, update the other editor (if possible) - if (editor.getOption(EditorOption.inDiffEditor)) { - // this editor belongs to a diff editor - for (const diffEditor of codeEditorService.listDiffEditors()) { - const originalEditor = diffEditor.getOriginalEditor(); - const modifiedEditor = diffEditor.getModifiedEditor(); - if (originalEditor === editor) { - if (canToggleWordWrap(codeEditorService, modifiedEditor)) { - writeTransientState(modifiedEditor.getModel(), newState, codeEditorService); - diffEditor.updateOptions({}); - } - break; - } - if (modifiedEditor === editor) { - if (canToggleWordWrap(codeEditorService, originalEditor)) { - writeTransientState(originalEditor.getModel(), newState, codeEditorService); - diffEditor.updateOptions({}); - } - break; - } + const diffEditor = findDiffEditorContainingCodeEditor(editor, codeEditorService); + if (diffEditor) { + const originalEditor = diffEditor.getOriginalEditor(); + const modifiedEditor = diffEditor.getModifiedEditor(); + const otherEditor = (originalEditor === editor ? modifiedEditor : originalEditor); + if (canToggleWordWrap(codeEditorService, otherEditor)) { + writeTransientState(otherEditor.getModel(), newState, codeEditorService); + diffEditor.updateOptions({}); } } } } +/** + * If `editor` is the original or modified editor of a diff editor, it returns it. + * It returns null otherwise. + */ +function findDiffEditorContainingCodeEditor(editor: ICodeEditor, codeEditorService: ICodeEditorService): IDiffEditor | null { + if (!editor.getOption(EditorOption.inDiffEditor)) { + return null; + } + for (const diffEditor of codeEditorService.listDiffEditors()) { + const originalEditor = diffEditor.getOriginalEditor(); + const modifiedEditor = diffEditor.getModifiedEditor(); + if (originalEditor === editor || modifiedEditor === editor) { + return diffEditor; + } + } + return null; +} + class ToggleWordWrapController extends Disposable implements IEditorContribution { public static readonly ID = 'editor.contrib.toggleWordWrapController'; @@ -181,6 +188,45 @@ class ToggleWordWrapController extends Disposable implements IEditorContribution } } +class DiffToggleWordWrapController extends Disposable implements IDiffEditorContribution { + + public static readonly ID = 'diffeditor.contrib.toggleWordWrapController'; + + constructor( + private readonly _diffEditor: IDiffEditor, + @ICodeEditorService private readonly _codeEditorService: ICodeEditorService + ) { + super(); + + this._register(this._diffEditor.onDidChangeModel(() => { + this._ensureSyncedWordWrapToggle(); + })); + } + + private _ensureSyncedWordWrapToggle(): void { + const originalEditor = this._diffEditor.getOriginalEditor(); + const modifiedEditor = this._diffEditor.getModifiedEditor(); + + if (!originalEditor.hasModel() || !modifiedEditor.hasModel()) { + return; + } + + const originalTransientState = readTransientState(originalEditor.getModel(), this._codeEditorService); + const modifiedTransientState = readTransientState(modifiedEditor.getModel(), this._codeEditorService); + + console.log(originalTransientState, modifiedTransientState); + + if (originalTransientState && !modifiedTransientState && canToggleWordWrap(this._codeEditorService, originalEditor)) { + writeTransientState(modifiedEditor.getModel(), originalTransientState, this._codeEditorService); + this._diffEditor.updateOptions({}); + } + if (!originalTransientState && modifiedTransientState && canToggleWordWrap(this._codeEditorService, modifiedEditor)) { + writeTransientState(originalEditor.getModel(), modifiedTransientState, this._codeEditorService); + this._diffEditor.updateOptions({}); + } + } +} + function canToggleWordWrap(codeEditorService: ICodeEditorService, editor: ICodeEditor | null): editor is IActiveCodeEditor { if (!editor) { return false; @@ -269,10 +315,10 @@ class EditorWordWrapContextKeyTracker implements IWorkbenchContribution { } const workbenchRegistry = Registry.as(Extensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(EditorWordWrapContextKeyTracker, 'EditorWordWrapContextKeyTracker', LifecyclePhase.Ready); +workbenchRegistry.registerWorkbenchContribution(EditorWordWrapContextKeyTracker, LifecyclePhase.Ready); registerEditorContribution(ToggleWordWrapController.ID, ToggleWordWrapController); - +registerDiffEditorContribution(DiffToggleWordWrapController.ID, DiffToggleWordWrapController); registerEditorAction(ToggleWordWrapAction); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { @@ -305,8 +351,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { // View menu -MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '5_editor', +MenuRegistry.appendMenuItem(MenuId.MenubarEditorFeaturesMenu, { command: { id: TOGGLE_WORD_WRAP_ID, title: nls.localize({ key: 'miToggleWordWrap', comment: ['&& denotes a mnemonic'] }, "&&Word Wrap"), diff --git a/src/vs/workbench/contrib/codeEditor/electron-sandbox/displayChangeRemeasureFonts.ts b/src/vs/workbench/contrib/codeEditor/electron-sandbox/displayChangeRemeasureFonts.ts index 4a2368a13a1..4d53ac15383 100644 --- a/src/vs/workbench/contrib/codeEditor/electron-sandbox/displayChangeRemeasureFonts.ts +++ b/src/vs/workbench/contrib/codeEditor/electron-sandbox/displayChangeRemeasureFonts.ts @@ -23,4 +23,4 @@ class DisplayChangeRemeasureFonts extends Disposable implements IWorkbenchContri } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DisplayChangeRemeasureFonts, 'DisplayChangeRemeasureFonts', LifecyclePhase.Eventually); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DisplayChangeRemeasureFonts, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/codeEditor/electron-sandbox/selectionClipboard.ts b/src/vs/workbench/contrib/codeEditor/electron-sandbox/selectionClipboard.ts index 2801c0cb4c0..b0c66b9acb9 100644 --- a/src/vs/workbench/contrib/codeEditor/electron-sandbox/selectionClipboard.ts +++ b/src/vs/workbench/contrib/codeEditor/electron-sandbox/selectionClipboard.ts @@ -135,7 +135,7 @@ class PasteSelectionClipboardAction extends EditorAction { } registerEditorContribution(SelectionClipboardContributionID, SelectionClipboard); -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(SelectionClipboardPastePreventer, 'SelectionClipboardPastePreventer', LifecyclePhase.Ready); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(SelectionClipboardPastePreventer, LifecyclePhase.Ready); if (platform.isLinux) { registerEditorAction(PasteSelectionClipboardAction); } diff --git a/src/vs/workbench/contrib/codeEditor/electron-sandbox/sleepResumeRepaintMinimap.ts b/src/vs/workbench/contrib/codeEditor/electron-sandbox/sleepResumeRepaintMinimap.ts index 78219ebf9ef..f0e5225baa5 100644 --- a/src/vs/workbench/contrib/codeEditor/electron-sandbox/sleepResumeRepaintMinimap.ts +++ b/src/vs/workbench/contrib/codeEditor/electron-sandbox/sleepResumeRepaintMinimap.ts @@ -24,4 +24,4 @@ class SleepResumeRepaintMinimap extends Disposable implements IWorkbenchContribu } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(SleepResumeRepaintMinimap, 'SleepResumeRepaintMinimap', LifecyclePhase.Eventually); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(SleepResumeRepaintMinimap, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/codeEditor/electron-sandbox/startDebugTextMate.ts b/src/vs/workbench/contrib/codeEditor/electron-sandbox/startDebugTextMate.ts index 5d6f3d31359..032f263903d 100644 --- a/src/vs/workbench/contrib/codeEditor/electron-sandbox/startDebugTextMate.ts +++ b/src/vs/workbench/contrib/codeEditor/electron-sandbox/startDebugTextMate.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { Range } from 'vs/editor/common/core/range'; import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { ITextMateService } from 'vs/workbench/services/textMate/browser/textMate'; import { IModelService } from 'vs/editor/common/services/model'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -30,7 +30,7 @@ class StartDebugTextMate extends Action2 { super({ id: 'editor.action.startDebugTextMate', title: { value: nls.localize('startDebugTextMate', "Start Text Mate Syntax Grammar Logging"), original: 'Start Text Mate Syntax Grammar Logging' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true }); } diff --git a/src/vs/workbench/contrib/comments/browser/commentMenus.ts b/src/vs/workbench/contrib/comments/browser/commentMenus.ts index 79e647f9121..dae9f44956f 100644 --- a/src/vs/workbench/contrib/comments/browser/commentMenus.ts +++ b/src/vs/workbench/contrib/comments/browser/commentMenus.ts @@ -35,10 +35,6 @@ export class CommentMenus implements IDisposable { return this.getMenu(MenuId.CommentThreadTitleContext, contextKeyService); } - getCommentThreadCommentContextActions(contextKeyService: IContextKeyService): IMenu { - return this.getMenu(MenuId.CommentThreadCommentContext, contextKeyService); - } - private getMenu(menuId: MenuId, contextKeyService: IContextKeyService): IMenu { const menu = this.menuService.createMenu(menuId, contextKeyService); diff --git a/src/vs/workbench/contrib/comments/browser/commentNode.ts b/src/vs/workbench/contrib/comments/browser/commentNode.ts index df7f71d462a..8ca985d8f43 100644 --- a/src/vs/workbench/contrib/comments/browser/commentNode.ts +++ b/src/vs/workbench/contrib/comments/browser/commentNode.ts @@ -26,7 +26,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { ToggleReactionsAction, ReactionAction, ReactionActionViewItem } from './reactionsAction'; import { ICommentThreadWidget } from 'vs/workbench/contrib/comments/common/commentThreadWidget'; -import { MenuItemAction, SubmenuItemAction, IMenu } from 'vs/platform/actions/common/actions'; +import { MenuItemAction, SubmenuItemAction, IMenu, MenuId } from 'vs/platform/actions/common/actions'; import { MenuEntryActionViewItem, SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { CommentFormActions } from 'vs/workbench/contrib/comments/browser/commentFormActions'; @@ -574,13 +574,11 @@ export class CommentNode extends Disposable { private onContextMenu(e: MouseEvent) { - const actions = this._commentMenus.getCommentThreadCommentContextActions(this._contextKeyService).getActions({ shouldForwardArgs: true }).map((value) => value[1]).flat(); - if (!actions.length) { - return; - } this.contextMenuService.showContextMenu({ getAnchor: () => e, - getActions: () => actions, + menuId: MenuId.CommentThreadCommentContext, + menuActionOptions: { shouldForwardArgs: true }, + contextKeyService: this._contextKeyService, actionRunner: new ActionRunner(), getActionsContext: () => { return this.commentNodeContext; diff --git a/src/vs/workbench/contrib/comments/browser/comments.ts b/src/vs/workbench/contrib/comments/browser/comments.ts index 82c3e009faa..7ccfc5706e4 100644 --- a/src/vs/workbench/contrib/comments/browser/comments.ts +++ b/src/vs/workbench/contrib/comments/browser/comments.ts @@ -3,20 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event } from 'vs/base/common/event'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IView } from 'vs/workbench/common/views'; import { CommentsFilters } from 'vs/workbench/contrib/comments/browser/commentsViewActions'; export const CommentsViewFilterFocusContextKey = new RawContextKey('commentsFilterFocus', false); -export const CommentsViewSmallLayoutContextKey = new RawContextKey(`commentsView.smallLayout`, false); export interface ICommentsView extends IView { - readonly onDidFocusFilter: Event; - readonly onDidClearFilterText: Event; readonly filters: CommentsFilters; - readonly onDidChangeFilterStats: Event<{ total: number; filtered: number }>; focusFilter(): void; clearFilterText(): void; getFilterStats(): { total: number; filtered: number }; diff --git a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts index 81de2638436..af59bb1d79c 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts @@ -50,7 +50,7 @@ import { CommentThreadRangeDecorator } from 'vs/workbench/contrib/comments/brows import { commentThreadRangeActiveBackground, commentThreadRangeActiveBorder, commentThreadRangeBackground, commentThreadRangeBorder } from 'vs/workbench/contrib/comments/browser/commentColors'; import { ICursorSelectionChangedEvent } from 'vs/editor/common/cursorEvents'; import { CommentsPanel } from 'vs/workbench/contrib/comments/browser/commentsView'; -import { withUndefinedAsNull } from 'vs/base/common/types'; +import { withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types'; export const ID = 'editor.contrib.review'; @@ -180,11 +180,11 @@ class CommentingRangeDecorator { } } - public update(editor: ICodeEditor | undefined, commentInfos: ICommentInfo[]) { + public update(editor: ICodeEditor | undefined, commentInfos: ICommentInfo[], cursorLine?: number, range?: Range) { if (editor) { this._editor = editor; this._infos = commentInfos; - this._doUpdate(editor, commentInfos); + this._doUpdate(editor, commentInfos, cursorLine, range); } } @@ -493,7 +493,7 @@ export class CommentController implements IEditorContribution { }).then(commentInfos => { if (this.commentService.isCommentingEnabled) { const meaningfulCommentInfos = coalesce(commentInfos); - this._commentingRangeDecorator.update(this.editor, meaningfulCommentInfos); + this._commentingRangeDecorator.update(this.editor, meaningfulCommentInfos, this.editor?.getPosition()?.lineNumber, withNullAsUndefined(this.editor?.getSelection())); } }, (err) => { onUnexpectedError(err); diff --git a/src/vs/workbench/contrib/comments/browser/commentsView.ts b/src/vs/workbench/contrib/comments/browser/commentsView.ts index d597a84eef2..4f31b2605e4 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsView.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsView.ts @@ -18,7 +18,7 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { textLinkForeground, textLinkActiveForeground, focusBorder, textPreformatForeground } from 'vs/platform/theme/common/colorRegistry'; import { ResourceLabels } from 'vs/workbench/browser/labels'; import { CommentsList, COMMENTS_VIEW_ID, COMMENTS_VIEW_TITLE, Filter } from 'vs/workbench/contrib/comments/browser/commentsTreeViewer'; -import { ViewPane, IViewPaneOptions, ViewAction } from 'vs/workbench/browser/parts/views/viewPane'; +import { IViewPaneOptions, ViewAction, FilterViewPane } from 'vs/workbench/browser/parts/views/viewPane'; import { IViewDescriptorService, IViewsService } from 'vs/workbench/common/views'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; @@ -31,11 +31,8 @@ import { MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { Codicon } from 'vs/base/common/codicons'; import { IEditor } from 'vs/editor/common/editorCommon'; import { TextModel } from 'vs/editor/common/model/textModel'; -import { Action, IAction } from 'vs/base/common/actions'; -import { ActionBar, IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; -import { CommentsViewSmallLayoutContextKey, ICommentsView } from 'vs/workbench/contrib/comments/browser/comments'; -import { CommentsFilterActionViewItem, CommentsFilters, CommentsFiltersChangeEvent } from 'vs/workbench/contrib/comments/browser/commentsViewActions'; -import { Event, Emitter } from 'vs/base/common/event'; +import { CommentsViewFilterFocusContextKey, ICommentsView } from 'vs/workbench/contrib/comments/browser/comments'; +import { CommentsFilters, CommentsFiltersChangeEvent } from 'vs/workbench/contrib/comments/browser/commentsViewActions'; import { Memento, MementoObject } from 'vs/workbench/common/memento'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { FilterOptions } from 'vs/workbench/contrib/comments/browser/commentsFilterOptions'; @@ -43,7 +40,7 @@ import { FilterOptions } from 'vs/workbench/contrib/comments/browser/commentsFil const CONTEXT_KEY_HAS_COMMENTS = new RawContextKey('commentsView.hasComments', false); const VIEW_STORAGE_ID = 'commentsViewState'; -export class CommentsPanel extends ViewPane implements ICommentsView { +export class CommentsPanel extends FilterViewPane implements ICommentsView { private treeLabels!: ResourceLabels; private tree: CommentsList | undefined; private treeContainer!: HTMLElement; @@ -51,10 +48,8 @@ export class CommentsPanel extends ViewPane implements ICommentsView { private commentsModel!: CommentsModel; private totalComments: number = 0; private readonly hasCommentsContextKey: IContextKey; - private readonly smallLayoutContextKey: IContextKey; private readonly filter: Filter; readonly filters: CommentsFilters; - private filterActionBar: ActionBar | undefined; private currentHeight = 0; private currentWidth = 0; @@ -64,13 +59,6 @@ export class CommentsPanel extends ViewPane implements ICommentsView { readonly onDidChangeVisibility = this.onDidChangeBodyVisibility; - private readonly _onDidFocusFilter: Emitter = this._register(new Emitter()); - readonly onDidFocusFilter: Event = this._onDidFocusFilter.event; - private readonly _onDidClearFilterText: Emitter = this._register(new Emitter()); - readonly onDidClearFilterText: Event = this._onDidClearFilterText.event; - private _onDidChangeFilterStats = this._register(new Emitter<{ total: number; filtered: number }>()); - readonly onDidChangeFilterStats: Event<{ total: number; filtered: number }> = this._onDidChangeFilterStats.event; - constructor( options: IViewPaneOptions, @IInstantiationService instantiationService: IInstantiationService, @@ -87,20 +75,27 @@ export class CommentsPanel extends ViewPane implements ICommentsView { @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, @IStorageService readonly storageService: IStorageService ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); + const stateMemento = new Memento(VIEW_STORAGE_ID, storageService); + const viewState = stateMemento.getMemento(StorageScope.WORKSPACE, StorageTarget.USER); + super({ + ...options, + filterOptions: { + placeholder: nls.localize('comments.filter.placeholder', "Filter (e.g. text, author)"), + ariaLabel: nls.localize('comments.filter.ariaLabel', "Filter comments"), + history: viewState['filterHistory'] || [], + text: viewState['filter'] || '', + focusContextKey: CommentsViewFilterFocusContextKey.key + } + }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); this.hasCommentsContextKey = CONTEXT_KEY_HAS_COMMENTS.bindTo(contextKeyService); - this.smallLayoutContextKey = CommentsViewSmallLayoutContextKey.bindTo(this.contextKeyService); - this.stateMemento = new Memento(VIEW_STORAGE_ID, storageService); - this.viewState = this.stateMemento.getMemento(StorageScope.WORKSPACE, StorageTarget.USER); + this.stateMemento = stateMemento; + this.viewState = viewState; this.filters = this._register(new CommentsFilters({ - filterText: this.viewState['filter'] || '', - filterHistory: this.viewState['filterHistory'] || [], showResolved: this.viewState['showResolved'] !== false, showUnresolved: this.viewState['showUnresolved'] !== false, - layout: new dom.Dimension(0, 0) - })); - this.filter = new Filter(new FilterOptions(this.filters.filterText, this.filters.showResolved, this.filters.showUnresolved)); + }, this.contextKeyService)); + this.filter = new Filter(new FilterOptions(this.filterWidget.getFilterText(), this.filters.showResolved, this.filters.showUnresolved)); this._register(this.commentService.onDidSetAllCommentThreads(e => { this.totalComments = e.commentThreads.length; @@ -112,15 +107,16 @@ export class CommentsPanel extends ViewPane implements ICommentsView { })); this._register(this.filters.onDidChange((event: CommentsFiltersChangeEvent) => { - if (event.filterText || event.showResolved || event.showUnresolved) { + if (event.showResolved || event.showUnresolved) { this.updateFilter(); } })); + this._register(this.filterWidget.onDidChangeFilterText(() => this.updateFilter())); } override saveState(): void { - this.viewState['filter'] = this.filters.filterText; - this.viewState['filterHistory'] = this.filters.filterHistory; + this.viewState['filter'] = this.filterWidget.getFilterText(); + this.viewState['filterHistory'] = this.filterWidget.getHistory(); this.viewState['showResolved'] = this.filters.showResolved; this.viewState['showUnresolved'] = this.filters.showUnresolved; this.stateMemento.saveMemento(); @@ -128,11 +124,11 @@ export class CommentsPanel extends ViewPane implements ICommentsView { } public focusFilter(): void { - this._onDidFocusFilter.fire(); + this.filterWidget.focus(); } public clearFilterText(): void { - this._onDidClearFilterText.fire(); + this.filterWidget.setFilterText(''); } public getFilterStats(): { total: number; filtered: number } { @@ -147,30 +143,21 @@ export class CommentsPanel extends ViewPane implements ICommentsView { } private updateFilter() { - this.filter.options = new FilterOptions(this.filters.filterText, this.filters.showResolved, this.filters.showUnresolved); + this.filter.options = new FilterOptions(this.filterWidget.getFilterText(), this.filters.showResolved, this.filters.showUnresolved); this.tree?.filterComments(); this.cachedFilterStats = undefined; - this._onDidChangeFilterStats.fire(this.getFilterStats()); + const { total, filtered } = this.getFilterStats(); + this.filterWidget.updateBadge(total === filtered || total === 0 ? undefined : nls.localize('showing filtered results', "Showing {0} of {1}", filtered, total)); + this.filterWidget.checkMoreFilters(!this.filters.showResolved || !this.filters.showUnresolved); } - private createFilterActionBar(parent: HTMLElement): void { - this.filterActionBar = this._register(new ActionBar(parent, { actionViewItemProvider: action => this.getActionViewItem(action) })); - this.filterActionBar.getContainer().classList.add('comments-panel-filter-container'); - this.filterActionBar.getContainer().classList.toggle('hide', !this.smallLayout); - } - - private get smallLayout(): boolean { return !!this.smallLayoutContextKey.get(); } - private set smallLayout(smallLayout: boolean) { this.smallLayoutContextKey.set(smallLayout); } - public override renderBody(container: HTMLElement): void { super.renderBody(container); container.classList.add('comments-panel'); const domContainer = dom.append(container, dom.$('.comments-panel-container')); - this.createFilterActionBar(domContainer); - this.filterActionBar!.push(new Action(`workbench.actions.treeView.${this.id}.filter`)); this.treeContainer = dom.append(domContainer, dom.$('.tree-container')); this.treeContainer.classList.add('file-icon-themable-tree', 'show-file-icons'); @@ -255,20 +242,11 @@ export class CommentsPanel extends ViewPane implements ICommentsView { return !!this.tree; } - public override layoutBody(height: number = this.currentHeight, width: number = this.currentWidth): void { - super.layoutBody(height, width); - const wasSmallLayout = this.smallLayout; - this.smallLayout = width < 600 && height > 100; - if (this.smallLayout !== wasSmallLayout) { - this.filterActionBar?.getContainer().classList.toggle('hide', !this.smallLayout); - } - const contentHeight = this.smallLayout ? height - 44 : height; + public override layoutBodyContent(height: number = this.currentHeight, width: number = this.currentWidth): void { if (this.messageBoxContainer) { - this.messageBoxContainer.style.height = `${contentHeight}px`; + this.messageBoxContainer.style.height = `${height}px`; } - this.tree?.layout(contentHeight, width); - this.filters.layout = new dom.Dimension(this.smallLayout ? width : width - 200, height); - + this.tree?.layout(height, width); this.currentHeight = height; this.currentWidth = width; } @@ -421,12 +399,6 @@ export class CommentsPanel extends ViewPane implements ICommentsView { } } - public override getActionViewItem(action: IAction): IActionViewItem | undefined { - if (action.id === `workbench.actions.treeView.${this.id}.filter`) { - return this.instantiationService.createInstance(CommentsFilterActionViewItem, action, this); - } - return super.getActionViewItem(action); - } } CommandsRegistry.registerCommand({ diff --git a/src/vs/workbench/contrib/comments/browser/commentsViewActions.ts b/src/vs/workbench/contrib/comments/browser/commentsViewActions.ts index 4445e3a05b0..7720d37c9e3 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsViewActions.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsViewActions.ts @@ -3,50 +3,31 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Delayer } from 'vs/base/common/async'; -import * as DOM from 'vs/base/browser/dom'; -import { Action, IAction, IActionRunner } from 'vs/base/common/actions'; -import { HistoryInputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IThemeService, registerThemingParticipant, ICssStyleCollector, IColorTheme, ThemeIcon } from 'vs/platform/theme/common/themeService'; -import { attachInputBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; -import { toDisposable, Disposable } from 'vs/base/common/lifecycle'; -import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { badgeBackground, badgeForeground, contrastBorder, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground } from 'vs/platform/theme/common/colorRegistry'; +import { Disposable } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; -import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { ContextScopedHistoryInputBox } from 'vs/platform/history/browser/contextScopedHistoryWidget'; -import { ContextKeyExpr, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { ContextKeyExpr, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { Event, Emitter } from 'vs/base/common/event'; -import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; -import { Codicon } from 'vs/base/common/codicons'; -import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; -import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; -import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { showHistoryKeybindingHint } from 'vs/platform/history/browser/historyWidgetKeybindingHint'; -import { CommentsViewFilterFocusContextKey, CommentsViewSmallLayoutContextKey, ICommentsView } from 'vs/workbench/contrib/comments/browser/comments'; -import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; +import { CommentsViewFilterFocusContextKey, ICommentsView } from 'vs/workbench/contrib/comments/browser/comments'; +import { registerAction2 } from 'vs/platform/actions/common/actions'; import { ViewAction } from 'vs/workbench/browser/parts/views/viewPane'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { COMMENTS_VIEW_ID } from 'vs/workbench/contrib/comments/browser/commentsTreeViewer'; import { FocusedViewContext } from 'vs/workbench/common/contextkeys'; +import { viewFilterSubmenu } from 'vs/workbench/browser/parts/views/viewFilter'; + +const CONTEXT_KEY_SHOW_RESOLVED = new RawContextKey('commentsView.showResolvedFilter', true); +const CONTEXT_KEY_SHOW_UNRESOLVED = new RawContextKey('commentsView.showUnResolvedFilter', true); export interface CommentsFiltersChangeEvent { - filterText?: boolean; showResolved?: boolean; showUnresolved?: boolean; - layout?: boolean; } export interface CommentsFiltersOptions { - filterText: string; - filterHistory: string[]; showResolved: boolean; showUnresolved: boolean; - layout: DOM.Dimension; } export class CommentsFilters extends Disposable { @@ -54,323 +35,34 @@ export class CommentsFilters extends Disposable { private readonly _onDidChange: Emitter = this._register(new Emitter()); readonly onDidChange: Event = this._onDidChange.event; - constructor(options: CommentsFiltersOptions) { + constructor(options: CommentsFiltersOptions, private readonly contextKeyService: IContextKeyService) { super(); - this._filterText = options.filterText; - this._showResolved = options.showResolved; - this._showUnresolved = options.showUnresolved; - this.filterHistory = options.filterHistory; - this._layout = options.layout; + this._showResolved.set(options.showResolved); + this._showUnresolved.set(options.showUnresolved); } - private _filterText: string; - get filterText(): string { - return this._filterText; - } - set filterText(filterText: string) { - if (this._filterText !== filterText) { - this._filterText = filterText; - this._onDidChange.fire({ filterText: true }); - } - } - - filterHistory: string[]; - - private _showUnresolved: boolean = true; + private readonly _showUnresolved = CONTEXT_KEY_SHOW_UNRESOLVED.bindTo(this.contextKeyService); get showUnresolved(): boolean { - return this._showUnresolved; + return !!this._showUnresolved.get(); } set showUnresolved(showUnresolved: boolean) { - if (this._showUnresolved !== showUnresolved) { - this._showUnresolved = showUnresolved; + if (this._showUnresolved.get() !== showUnresolved) { + this._showUnresolved.set(showUnresolved); this._onDidChange.fire({ showUnresolved: true }); } } - private _showResolved: boolean = true; + private _showResolved = CONTEXT_KEY_SHOW_RESOLVED.bindTo(this.contextKeyService); get showResolved(): boolean { - return this._showResolved; + return !!this._showResolved.get(); } set showResolved(showResolved: boolean) { - if (this._showResolved !== showResolved) { - this._showResolved = showResolved; + if (this._showResolved.get() !== showResolved) { + this._showResolved.set(showResolved); this._onDidChange.fire({ showResolved: true }); } } - private _layout: DOM.Dimension = new DOM.Dimension(0, 0); - get layout(): DOM.Dimension { - return this._layout; - } - set layout(layout: DOM.Dimension) { - if (this._layout.width !== layout.width || this._layout.height !== layout.height) { - this._layout = layout; - this._onDidChange.fire({ layout: true }); - } - } -} - -class FiltersDropdownMenuActionViewItem extends DropdownMenuActionViewItem { - - constructor( - action: IAction, private filters: CommentsFilters, actionRunner: IActionRunner, - @IContextMenuService contextMenuService: IContextMenuService - ) { - super(action, - { getActions: () => this.getActions() }, - contextMenuService, - { - actionRunner, - classNames: action.class, - anchorAlignmentProvider: () => AnchorAlignment.RIGHT, - menuAsChild: true - } - ); - } - - override render(container: HTMLElement): void { - super.render(container); - this.updateChecked(); - } - - private getActions(): IAction[] { - return [ - { - checked: this.filters.showResolved, - class: undefined, - enabled: true, - id: 'showResolved', - label: localize('showResolved', "Show Resolved"), - run: async () => this.filters.showResolved = !this.filters.showResolved, - tooltip: '' - }, - { - checked: this.filters.showUnresolved, - class: undefined, - enabled: true, - id: 'showUnresolved', - label: localize('showUnresolved', "Show Unresolved"), - run: async () => this.filters.showUnresolved = !this.filters.showUnresolved, - tooltip: '' - } - ]; - } - - override updateChecked(): void { - this.element!.classList.toggle('checked', this._action.checked); - } -} - - -const filterIcon = registerIcon('comments-view-filter', Codicon.filter, localize('comments.filterIcon', 'Icon for the filter configuration in the Comments view.')); - -export class CommentsFilterActionViewItem extends BaseActionViewItem { - - private delayedFilterUpdate: Delayer; - private container: HTMLElement | null = null; - private filterInputBox: HistoryInputBox | null = null; - private filterBadge: HTMLElement | null = null; - private focusContextKey: IContextKey; - private readonly filtersAction: IAction; - private actionbar: ActionBar | null = null; - private keybindingService; - - constructor( - action: IAction, - private commentsView: ICommentsView, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IContextViewService private readonly contextViewService: IContextViewService, - @IThemeService private readonly themeService: IThemeService, - @IContextKeyService contextKeyService: IContextKeyService, - @IKeybindingService keybindingService: IKeybindingService - ) { - super(null, action); - this.keybindingService = keybindingService; - this.focusContextKey = CommentsViewFilterFocusContextKey.bindTo(contextKeyService); - this.delayedFilterUpdate = new Delayer(400); - this._register(toDisposable(() => this.delayedFilterUpdate.cancel())); - this._register(commentsView.onDidFocusFilter(() => this.focus())); - this._register(commentsView.onDidClearFilterText(() => this.clearFilterText())); - this.filtersAction = new Action('commentsFiltersAction', localize('commentsFiltersAction', "More Filters..."), 'comments-filters ' + ThemeIcon.asClassName(filterIcon)); - this.filtersAction.checked = this.hasFiltersChanged(); - this._register(commentsView.filters.onDidChange(e => this.onDidFiltersChange(e))); - } - - override render(container: HTMLElement): void { - this.container = container; - this.container.classList.add('comments-panel-action-filter-container'); - - this.element = DOM.append(this.container, DOM.$('')); - this.element.className = this.class; - this.createInput(this.element); - this.createControls(this.element); - this.updateClass(); - - this.adjustInputBox(); - } - - override focus(): void { - if (this.filterInputBox) { - this.filterInputBox.focus(); - } - } - - override blur(): void { - if (this.filterInputBox) { - this.filterInputBox.blur(); - } - } - - override setFocusable(): void { - // noop input elements are focusable by default - } - - override get trapsArrowNavigation(): boolean { - return true; - } - - private clearFilterText(): void { - if (this.filterInputBox) { - this.filterInputBox.value = ''; - } - } - - private onDidFiltersChange(e: CommentsFiltersChangeEvent): void { - this.filtersAction.checked = this.hasFiltersChanged(); - if (e.layout) { - this.updateClass(); - } - } - - private hasFiltersChanged(): boolean { - return !this.commentsView.filters.showResolved || !this.commentsView.filters.showUnresolved; - } - - private createInput(container: HTMLElement): void { - this.filterInputBox = this._register(this.instantiationService.createInstance(ContextScopedHistoryInputBox, container, this.contextViewService, { - placeholder: localize('comments.filter.placeholder', "Filter (e.g. text, author)"), - ariaLabel: localize('comments.filter.ariaLabel', "Filter comments"), - history: this.commentsView.filters.filterHistory, - showHistoryHint: () => showHistoryKeybindingHint(this.keybindingService) - })); - this._register(attachInputBoxStyler(this.filterInputBox, this.themeService)); - this.filterInputBox.value = this.commentsView.filters.filterText; - this._register(this.filterInputBox.onDidChange(filter => this.delayedFilterUpdate.trigger(() => this.onDidInputChange(this.filterInputBox!)))); - this._register(this.commentsView.filters.onDidChange((event: CommentsFiltersChangeEvent) => { - if (event.filterText) { - this.filterInputBox!.value = this.commentsView.filters.filterText; - } - })); - this._register(DOM.addStandardDisposableListener(this.filterInputBox.inputElement, DOM.EventType.KEY_DOWN, (e: any) => this.onInputKeyDown(e, this.filterInputBox!))); - this._register(DOM.addStandardDisposableListener(container, DOM.EventType.KEY_DOWN, this.handleKeyboardEvent)); - this._register(DOM.addStandardDisposableListener(container, DOM.EventType.KEY_UP, this.handleKeyboardEvent)); - this._register(DOM.addStandardDisposableListener(this.filterInputBox.inputElement, DOM.EventType.CLICK, (e) => { - e.stopPropagation(); - e.preventDefault(); - })); - - const focusTracker = this._register(DOM.trackFocus(this.filterInputBox.inputElement)); - this._register(focusTracker.onDidFocus(() => this.focusContextKey.set(true))); - this._register(focusTracker.onDidBlur(() => this.focusContextKey.set(false))); - this._register(toDisposable(() => this.focusContextKey.reset())); - } - - private createControls(container: HTMLElement): void { - const controlsContainer = DOM.append(container, DOM.$('.comments-panel-filter-controls')); - this.createBadge(controlsContainer); - this.createFilters(controlsContainer); - } - - private createBadge(container: HTMLElement): void { - const filterBadge = this.filterBadge = DOM.append(container, DOM.$('.comments-panel-filter-badge')); - this._register(attachStylerCallback(this.themeService, { badgeBackground, badgeForeground, contrastBorder }, colors => { - const background = colors.badgeBackground ? colors.badgeBackground.toString() : ''; - const foreground = colors.badgeForeground ? colors.badgeForeground.toString() : ''; - const border = colors.contrastBorder ? colors.contrastBorder.toString() : ''; - - filterBadge.style.backgroundColor = background; - - filterBadge.style.borderWidth = border ? '1px' : ''; - filterBadge.style.borderStyle = border ? 'solid' : ''; - filterBadge.style.borderColor = border; - filterBadge.style.color = foreground; - })); - this.updateBadge(); - this._register(this.commentsView.onDidChangeFilterStats(() => this.updateBadge())); - } - - private createFilters(container: HTMLElement): void { - this.actionbar = this._register(new ActionBar(container, { - actionViewItemProvider: action => { - if (action.id === this.filtersAction.id) { - return this.instantiationService.createInstance(FiltersDropdownMenuActionViewItem, action, this.commentsView.filters, this.actionRunner); - } - return undefined; - } - })); - this.actionbar.push(this.filtersAction, { icon: true, label: false }); - } - - private onDidInputChange(inputbox: HistoryInputBox) { - inputbox.addToHistory(); - this.commentsView.filters.filterText = inputbox.value; - this.commentsView.filters.filterHistory = inputbox.getHistory(); - } - - private updateBadge(): void { - if (this.filterBadge) { - const { total, filtered } = this.commentsView.getFilterStats(); - this.filterBadge.classList.toggle('hidden', (total === filtered && !this.filterInputBox?.value) || total === 0); - this.filterBadge.textContent = localize('showing filtered comments', "Showing {0} of {1}", filtered, total); - this.adjustInputBox(); - } - } - - private adjustInputBox(): void { - if (this.element && this.filterInputBox && this.filterBadge) { - this.filterInputBox.inputElement.style.paddingRight = this.element.classList.contains('small') || this.filterBadge.classList.contains('hidden') ? '25px' : '150px'; - } - } - - // Action toolbar is swallowing some keys for action items which should not be for an input box - private handleKeyboardEvent(event: StandardKeyboardEvent) { - if (event.equals(KeyCode.Space) - || event.equals(KeyCode.LeftArrow) - || event.equals(KeyCode.RightArrow) - ) { - event.stopPropagation(); - } - } - - private onInputKeyDown(event: StandardKeyboardEvent, filterInputBox: HistoryInputBox) { - let handled = false; - if (event.equals(KeyCode.Tab)) { - this.actionbar?.focus(); - handled = true; - } - if (handled) { - event.stopPropagation(); - event.preventDefault(); - } - } - - protected override updateClass(): void { - if (this.element && this.container) { - this.element.className = this.class; - this.container.classList.toggle('grow', this.element.classList.contains('grow')); - this.adjustInputBox(); - } - } - - protected get class(): string { - if (this.commentsView.filters.layout.width > 600) { - return 'comments-panel-action-filter grow'; - } else if (this.commentsView.filters.layout.width < 400) { - return 'comments-panel-action-filter small'; - } else { - return 'comments-panel-action-filter'; - } - } } registerAction2(class extends ViewAction { @@ -409,22 +101,6 @@ registerAction2(class extends ViewAction { } }); -registerAction2(class extends Action2 { - constructor() { - super({ - id: `workbench.actions.treeView.${COMMENTS_VIEW_ID}.filter`, - title: localize('filter', "Filter"), - menu: { - id: MenuId.ViewTitle, - when: ContextKeyExpr.and(ContextKeyExpr.equals('view', COMMENTS_VIEW_ID), CommentsViewSmallLayoutContextKey.negate()), - group: 'navigation', - order: 1, - }, - }); - } - async run(): Promise { } -}); - registerAction2(class extends ViewAction { constructor() { super({ @@ -443,17 +119,52 @@ registerAction2(class extends ViewAction { } }); -registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { - const inputActiveOptionBorderColor = theme.getColor(inputActiveOptionBorder); - if (inputActiveOptionBorderColor) { - collector.addRule(`.comments-panel-action-filter > .comments-panel-filter-controls > .monaco-action-bar .action-label.comments-filters.checked { border-color: ${inputActiveOptionBorderColor}; }`); +registerAction2(class extends ViewAction { + constructor() { + super({ + id: `workbench.actions.${COMMENTS_VIEW_ID}.toggleUnResolvedComments`, + title: localize('toggle unresolved', "Toggle Unresolved Comments"), + category: localize('comments', "Comments"), + toggled: { + condition: CONTEXT_KEY_SHOW_UNRESOLVED, + title: localize('unresolved', "Show Unresolved"), + }, + menu: { + id: viewFilterSubmenu, + group: '1_filter', + when: ContextKeyExpr.equals('view', COMMENTS_VIEW_ID), + order: 1 + }, + viewId: COMMENTS_VIEW_ID + }); } - const inputActiveOptionForegroundColor = theme.getColor(inputActiveOptionForeground); - if (inputActiveOptionForegroundColor) { - collector.addRule(`.comments-panel-action-filter > .comments-panel-filter-controls > .monaco-action-bar .action-label.comments-filters.checked { color: ${inputActiveOptionForegroundColor}; }`); - } - const inputActiveOptionBackgroundColor = theme.getColor(inputActiveOptionBackground); - if (inputActiveOptionBackgroundColor) { - collector.addRule(`.comments-panel-action-filter > .comments-panel-filter-controls > .monaco-action-bar .action-label.comments-filters.checked { background-color: ${inputActiveOptionBackgroundColor}; }`); + + async runInView(serviceAccessor: ServicesAccessor, view: ICommentsView): Promise { + view.filters.showUnresolved = !view.filters.showUnresolved; + } +}); + +registerAction2(class extends ViewAction { + constructor() { + super({ + id: `workbench.actions.${COMMENTS_VIEW_ID}.toggleResolvedComments`, + title: localize('toggle resolved', "Toggle Resolved Comments"), + category: localize('comments', "Comments"), + toggled: { + condition: CONTEXT_KEY_SHOW_RESOLVED, + title: localize('resolved', "Show Resolved"), + }, + menu: { + id: viewFilterSubmenu, + group: '1_filter', + when: ContextKeyExpr.equals('view', COMMENTS_VIEW_ID), + order: 1 + }, + viewId: COMMENTS_VIEW_ID + }); + } + + async runInView(serviceAccessor: ServicesAccessor, view: ICommentsView): Promise { + view.filters.showResolved = !view.filters.showResolved; } }); diff --git a/src/vs/workbench/contrib/comments/browser/media/panel.css b/src/vs/workbench/contrib/comments/browser/media/panel.css index db53571f495..1492f1dfee4 100644 --- a/src/vs/workbench/contrib/comments/browser/media/panel.css +++ b/src/vs/workbench/contrib/comments/browser/media/panel.css @@ -109,61 +109,6 @@ padding-left: 16px; } -.comments-panel-container .monaco-action-bar.comments-panel-filter-container .action-item.comments-panel-action-filter-container, -.panel > .title .monaco-action-bar .action-item.comments-panel-action-filter-container.grow { - flex: 1; -} - -.comments-panel-container .monaco-action-bar.comments-panel-filter-container { - margin: 10px 20px; - height: initial; -} - -.comments-panel-action-filter > .comments-panel-filter-controls { - position: absolute; - top: 0px; - bottom: 0; - right: 0px; - display: flex; - align-items: center; -} - -.comments-panel-action-filter > .comments-panel-filter-controls > .comments-panel-filter-badge { - margin: 4px 0px; - padding: 0px 8px; - border-radius: 2px; -} - -.comments-panel-action-filter > .comments-panel-filter-controls > .comments-panel-filter-badge.hidden, -.comments-panel-action-filter.small > .comments-panel-filter-controls > .comments-panel-filter-badge { - display: none; -} - -.comments-panel-action-filter > .comments-panel-filter-controls > .monaco-action-bar .action-item .action-label.codicon.comments-filters { - padding: 2px; -} - -.panel > .title .monaco-action-bar .action-item.comments-panel-action-filter-container { - max-width: 400px; - min-width: 300px; - margin-right: 10px; -} - -.monaco-action-bar .comments-panel-action-filter .monaco-inputbox { - height: 24px; - font-size: 12px; - flex: 1; -} - -.pane-header .monaco-action-bar .comments-panel-action-filter .monaco-inputbox { - height: 20px; - line-height: 18px; -} - -.monaco-workbench.vs .monaco-action-bar .comments-panel-action-filter .monaco-inputbox { - height: 25px; -} - .comments-panel .hide { display: none; } diff --git a/src/vs/workbench/contrib/comments/browser/media/review.css b/src/vs/workbench/contrib/comments/browser/media/review.css index b92ceed75a1..bbbd5f93da9 100644 --- a/src/vs/workbench/contrib/comments/browser/media/review.css +++ b/src/vs/workbench/contrib/comments/browser/media/review.css @@ -326,6 +326,9 @@ font-size: 13px; margin-left: 20px; cursor: default; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .review-widget .head .review-title .dirname:not(:empty) { diff --git a/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts b/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts index c4ec1bbf5e3..c94bdc405fa 100644 --- a/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts +++ b/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts @@ -76,7 +76,7 @@ export class SimpleCommentEditor extends CodeEditorWidget { return this._parentThread; } - protected _getActions(): EditorAction[] { + protected _getActions(): Iterable { return EditorExtensionsRegistry.getEditorActions(); } diff --git a/src/vs/workbench/contrib/configExporter/electron-sandbox/configurationExportHelper.contribution.ts b/src/vs/workbench/contrib/configExporter/electron-sandbox/configurationExportHelper.contribution.ts index 9a0c2be015a..127283add02 100644 --- a/src/vs/workbench/contrib/configExporter/electron-sandbox/configurationExportHelper.contribution.ts +++ b/src/vs/workbench/contrib/configExporter/electron-sandbox/configurationExportHelper.contribution.ts @@ -23,4 +23,4 @@ export class ExtensionPoints implements IWorkbenchContribution { } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ExtensionPoints, 'ExtensionPoints', LifecyclePhase.Restored); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ExtensionPoints, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/contextmenu/browser/contextmenu.contribution.ts b/src/vs/workbench/contrib/contextmenu/browser/contextmenu.contribution.ts index 79cf442038e..ee27b0aefa9 100644 --- a/src/vs/workbench/contrib/contextmenu/browser/contextmenu.contribution.ts +++ b/src/vs/workbench/contrib/contextmenu/browser/contextmenu.contribution.ts @@ -25,4 +25,4 @@ class ContextMenuContribution implements IWorkbenchContribution { } Registry.as(WorkbenchExtensions.Workbench) - .registerWorkbenchContribution(ContextMenuContribution, 'ContextMenuContribution', LifecyclePhase.Eventually); + .registerWorkbenchContribution(ContextMenuContribution, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditor.contribution.ts b/src/vs/workbench/contrib/customEditor/browser/customEditor.contribution.ts index f7ce07c9213..f8673805e33 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditor.contribution.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditor.contribution.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Registry } from 'vs/platform/registry/common/platform'; import { EditorPaneDescriptor, IEditorPaneRegistry } from 'vs/workbench/browser/editor'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; @@ -16,7 +16,7 @@ import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle import { CustomEditorInput } from './customEditorInput'; import { CustomEditorService } from './customEditors'; -registerSingleton(ICustomEditorService, CustomEditorService, false); +registerSingleton(ICustomEditorService, CustomEditorService, InstantiationType.Delayed); Registry.as(EditorExtensions.EditorPane) .registerEditorPane( @@ -34,4 +34,4 @@ Registry.as(EditorExtensions.EditorFactory) CustomEditorInputSerializer); Registry.as(WorkbenchExtensions.Workbench) - .registerWorkbenchContribution(ComplexCustomWorkingCopyEditorHandler, 'ComplexCustomWorkingCopyEditorHandler', LifecyclePhase.Starting); + .registerWorkbenchContribution(ComplexCustomWorkingCopyEditorHandler, LifecyclePhase.Starting); diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts index e98fbb5a058..37fe6a28c75 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts @@ -15,7 +15,6 @@ import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebo import { IWebviewService, WebviewContentOptions, WebviewContentPurpose, WebviewExtensionDescription, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; import { DeserializedWebview, restoreWebviewContentOptions, restoreWebviewOptions, reviveWebviewExtensionDescription, SerializedWebview, SerializedWebviewOptions, WebviewEditorInputSerializer } from 'vs/workbench/contrib/webviewPanel/browser/webviewEditorInputSerializer'; import { IWebviewWorkbenchService } from 'vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService'; -import { IEditorResolverService } from 'vs/workbench/services/editor/common/editorResolverService'; import { IWorkingCopyBackupMeta } from 'vs/workbench/services/workingCopy/common/workingCopy'; import { IWorkingCopyBackupService } from 'vs/workbench/services/workingCopy/common/workingCopyBackup'; import { IWorkingCopyEditorService } from 'vs/workbench/services/workingCopy/common/workingCopyEditorService'; @@ -58,7 +57,6 @@ export class CustomEditorInputSerializer extends WebviewEditorInputSerializer { @IWebviewWorkbenchService webviewWorkbenchService: IWebviewWorkbenchService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IWebviewService private readonly _webviewService: IWebviewService, - @IEditorResolverService private readonly _editorResolverService: IEditorResolverService ) { super(webviewWorkbenchService); } @@ -92,12 +90,6 @@ export class CustomEditorInputSerializer extends WebviewEditorInputSerializer { serializedEditorInput: string ): CustomEditorInput { const data = this.fromJson(JSON.parse(serializedEditorInput)); - if (data.viewType === 'jupyter.notebook.ipynb') { - const editorAssociation = this._editorResolverService.getAssociationsForResource(data.editorResource); - if (!editorAssociation.find(association => association.viewType === 'jupyter.notebook.ipynb')) { - return NotebookEditorInput.create(this._instantiationService, data.editorResource, 'jupyter-notebook', { _backupId: data.backupId, startDirty: data.dirty }) as any; - } - } const webview = reviveWebview(this._webviewService, data); const customInput = this._instantiationService.createInstance(CustomEditorInput, { resource: data.editorResource, viewType: data.viewType, id: data.id }, webview, { startsDirty: data.dirty, backupId: data.backupId }); @@ -131,7 +123,6 @@ export class ComplexCustomWorkingCopyEditorHandler extends Disposable implements @IInstantiationService private readonly _instantiationService: IInstantiationService, @IWorkingCopyEditorService private readonly _workingCopyEditorService: IWorkingCopyEditorService, @IWorkingCopyBackupService private readonly _workingCopyBackupService: IWorkingCopyBackupService, - @IEditorResolverService private readonly _editorResolverService: IEditorResolverService, @IWebviewService private readonly _webviewService: IWebviewService, @ICustomEditorService _customEditorService: ICustomEditorService // DO NOT REMOVE (needed on startup to register overrides properly) ) { @@ -177,13 +168,6 @@ export class ComplexCustomWorkingCopyEditorHandler extends Disposable implements } const backupData = backup.meta; - if (backupData.viewType === 'jupyter.notebook.ipynb') { - const editorAssociation = this._editorResolverService.getAssociationsForResource(URI.revive(backupData.editorResource)); - if (!editorAssociation.find(association => association.viewType === 'jupyter.notebook.ipynb')) { - return NotebookEditorInput.create(this._instantiationService, URI.revive(backupData.editorResource), 'jupyter-notebook', { startDirty: !!backupData.backupId, _backupId: backupData.backupId, _workingCopy: workingCopy }) as any; - } - } - const id = backupData.webview.id; const extension = reviveWebviewExtensionDescription(backupData.extension?.id, backupData.extension?.location); const webview = reviveWebview(this._webviewService, { diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index 12adb973d5f..f4245a1995b 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -26,7 +26,7 @@ import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { CONTEXT_ACTIVE_CUSTOM_EDITOR_ID, CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE, CustomEditorCapabilities, CustomEditorInfo, CustomEditorInfoCollection, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { CustomEditorModelManager } from 'vs/workbench/contrib/customEditor/common/customEditorModelManager'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { DiffEditorInputFactoryFunction, EditorInputFactoryFunction, IEditorResolverService, IEditorType, RegisteredEditorPriority, UntitledEditorInputFactoryFunction } from 'vs/workbench/services/editor/common/editorResolverService'; +import { IEditorResolverService, IEditorType, RegisteredEditorPriority } from 'vs/workbench/services/editor/common/editorResolverService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ContributedCustomEditors } from '../common/contributedCustomEditors'; import { CustomEditorInput } from './customEditorInput'; @@ -119,16 +119,6 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ continue; } - const editorInputFactory: EditorInputFactoryFunction = ({ resource }, group) => { - return { editor: CustomEditorInput.create(this.instantiationService, resource, contributedEditor.id, group.id) }; - }; - const untitledEditorInputFactory: UntitledEditorInputFactoryFunction = ({ resource }, group) => { - return { editor: CustomEditorInput.create(this.instantiationService, resource ?? URI.from({ scheme: Schemas.untitled, authority: `Untitled-${this._untitledCounter++}` }), contributedEditor.id, group.id) }; - }; - const diffEditorInputFactory: DiffEditorInputFactoryFunction = (diffEditorInput, group) => { - return { editor: this.createDiffEditorInput(diffEditorInput, contributedEditor.id, group) }; - }; - this._editorResolverDisposables.add(this.editorResolverService.registerEditor( globPattern.filenamePattern, { @@ -141,9 +131,15 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ singlePerResource: () => !this.getCustomEditorCapabilities(contributedEditor.id)?.supportsMultipleEditorsPerDocument ?? true }, { - createEditorInput: editorInputFactory, - createUntitledEditorInput: untitledEditorInputFactory, - createDiffEditorInput: diffEditorInputFactory, + createEditorInput: ({ resource }, group) => { + return { editor: CustomEditorInput.create(this.instantiationService, resource, contributedEditor.id, group.id) }; + }, + createUntitledEditorInput: ({ resource }, group) => { + return { editor: CustomEditorInput.create(this.instantiationService, resource ?? URI.from({ scheme: Schemas.untitled, authority: `Untitled-${this._untitledCounter++}` }), contributedEditor.id, group.id) }; + }, + createDiffEditorInput: (diffEditorInput, group) => { + return { editor: this.createDiffEditorInput(diffEditorInput, contributedEditor.id, group) }; + }, } )); } diff --git a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts index b900d70c0d9..e1eb88b7dfe 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts @@ -3,41 +3,41 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; -import * as env from 'vs/base/common/platform'; +import { isSafari } from 'vs/base/browser/browser'; +import { BrowserFeatures } from 'vs/base/browser/canIUse'; import * as dom from 'vs/base/browser/dom'; -import { URI } from 'vs/base/common/uri'; -import severity from 'vs/base/common/severity'; -import { IAction, Action, SubmenuAction, Separator } from 'vs/base/common/actions'; -import { Range } from 'vs/editor/common/core/range'; -import { ICodeEditor, IEditorMouseEvent, MouseTargetType, IContentWidget, IActiveCodeEditor, IContentWidgetPosition, ContentWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; -import { IModelDecorationOptions, TrackedRangeStickiness, ITextModel, OverviewRulerLane, IModelDecorationOverviewRulerOptions } from 'vs/editor/common/model'; -import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IDebugService, IBreakpoint, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, BreakpointWidgetContext, IBreakpointEditorContribution, IBreakpointUpdateData, IDebugConfiguration, State, IDebugSession, DebuggerString } from 'vs/workbench/contrib/debug/common/debug'; -import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { BreakpointWidget } from 'vs/workbench/contrib/debug/browser/breakpointWidget'; -import { IDisposable, dispose, disposeIfDisposable } from 'vs/base/common/lifecycle'; -import { MarkdownString } from 'vs/base/common/htmlContent'; -import { getBreakpointMessageAndIcon } from 'vs/workbench/contrib/debug/browser/breakpointsView'; -import { generateUuid } from 'vs/base/common/uuid'; -import { memoize } from 'vs/base/common/decorators'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; +import { Action, IAction, Separator, SubmenuAction } from 'vs/base/common/actions'; import { distinct } from 'vs/base/common/arrays'; import { RunOnceScheduler } from 'vs/base/common/async'; -import { EditorOption } from 'vs/editor/common/config/editorOptions'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { BrowserFeatures } from 'vs/base/browser/canIUse'; -import { isSafari } from 'vs/base/browser/browser'; -import { registerThemingParticipant, themeColorFromId, ThemeIcon } from 'vs/platform/theme/common/themeService'; -import { registerColor } from 'vs/platform/theme/common/colorRegistry'; -import { ILabelService } from 'vs/platform/label/common/label'; -import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons'; +import { memoize } from 'vs/base/common/decorators'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { MarkdownString } from 'vs/base/common/htmlContent'; +import { Disposable, dispose, disposeIfDisposable, IDisposable } from 'vs/base/common/lifecycle'; +import * as env from 'vs/base/common/platform'; +import severity from 'vs/base/common/severity'; import { noBreakWhitespace } from 'vs/base/common/strings'; -import { ILanguageService } from 'vs/editor/common/languages/language'; import { withNullAsUndefined } from 'vs/base/common/types'; +import { URI } from 'vs/base/common/uri'; +import { generateUuid } from 'vs/base/common/uuid'; +import { ContentWidgetPositionPreference, IActiveCodeEditor, ICodeEditor, IContentWidget, IContentWidgetPosition, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { Range } from 'vs/editor/common/core/range'; +import { ILanguageService } from 'vs/editor/common/languages/language'; +import { IModelDecorationOptions, IModelDecorationOverviewRulerOptions, ITextModel, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; +import * as nls from 'vs/nls'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { registerColor } from 'vs/platform/theme/common/colorRegistry'; +import { registerThemingParticipant, themeColorFromId, ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { getBreakpointMessageAndIcon } from 'vs/workbench/contrib/debug/browser/breakpointsView'; +import { BreakpointWidget } from 'vs/workbench/contrib/debug/browser/breakpointWidget'; +import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons'; +import { BreakpointWidgetContext, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, DebuggerString, IBreakpoint, IBreakpointEditorContribution, IBreakpointUpdateData, IDebugConfiguration, IDebugService, IDebugSession, State } from 'vs/workbench/contrib/debug/common/debug'; const $ = dom.$; @@ -188,17 +188,43 @@ async function createCandidateDecorations(model: ITextModel, breakpointDecoratio return result; } -export class BreakpointEditorContribution implements IBreakpointEditorContribution { +export class LazyBreakpointEditorContribution extends Disposable implements IBreakpointEditorContribution { + private _contrib: IBreakpointEditorContribution | undefined; + + constructor(editor: ICodeEditor, @IInstantiationService instantiationService: IInstantiationService) { + super(); + + const listener = editor.onDidChangeModel(() => { + if (editor.hasModel()) { + listener.dispose(); + this._contrib = this._register(instantiationService.createInstance(BreakpointEditorContribution, editor)); + } + }); + } + showBreakpointWidget(lineNumber: number, column: number | undefined, context?: BreakpointWidgetContext | undefined): void { + this._contrib?.showBreakpointWidget(lineNumber, column, context); + } + + closeBreakpointWidget(): void { + this._contrib?.closeBreakpointWidget(); + } + + getContextMenuActionsAtPosition(lineNumber: number, model: ITextModel): IAction[] { + return this._contrib?.getContextMenuActionsAtPosition(lineNumber, model) ?? []; + } +} + +class BreakpointEditorContribution implements IBreakpointEditorContribution { private breakpointHintDecoration: string | null = null; private breakpointWidget: BreakpointWidget | undefined; - private breakpointWidgetVisible: IContextKey; + private breakpointWidgetVisible!: IContextKey; private toDispose: IDisposable[] = []; private ignoreDecorationsChangedEvent = false; private ignoreBreakpointsChangeEvent = false; private breakpointDecorations: IBreakpointDecoration[] = []; private candidateDecorations: { decorationId: string; inlineWidget: InlineBreakpointWidget }[] = []; - private setDecorationsScheduler: RunOnceScheduler; + private setDecorationsScheduler!: RunOnceScheduler; constructor( private readonly editor: ICodeEditor, @@ -212,8 +238,8 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi ) { this.breakpointWidgetVisible = CONTEXT_BREAKPOINT_WIDGET_VISIBLE.bindTo(contextKeyService); this.setDecorationsScheduler = new RunOnceScheduler(() => this.setDecorations(), 30); - this.registerListeners(); this.setDecorationsScheduler.schedule(); + this.registerListeners(); } /** diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index 414b1b2494e..42b8cbf0f94 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -294,7 +294,7 @@ export class BreakpointsView extends ViewPane { return langId && dbg.interestedInLanguage(langId); }); - if (message && debuggerHasUnverifiedBps?.length) { + if (message && debuggerHasUnverifiedBps?.length && this.debugService.getModel().areBreakpointsActivated()) { if (delayed) { const mdown = new MarkdownString(undefined, { isTrusted: true }).appendMarkdown(message); this.hintContainer.setLabel('$(warning)', undefined, { title: { markdown: mdown, markdownNotSupportedFallback: message } }); diff --git a/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts index b09f8c1157c..79cc37f580f 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts @@ -3,21 +3,22 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Constants } from 'vs/base/common/uint'; -import { Range } from 'vs/editor/common/core/range'; -import { TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationOptions, OverviewRulerLane } from 'vs/editor/common/model'; -import { IDebugService, IStackFrame } from 'vs/workbench/contrib/debug/common/debug'; -import { registerThemingParticipant, themeColorFromId, ThemeIcon } from 'vs/platform/theme/common/themeService'; -import { registerColor } from 'vs/platform/theme/common/colorRegistry'; -import { localize } from 'vs/nls'; -import { Event } from 'vs/base/common/event'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IEditorContribution } from 'vs/editor/common/editorCommon'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { distinct } from 'vs/base/common/arrays'; +import { Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { Constants } from 'vs/base/common/uint'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { Range } from 'vs/editor/common/core/range'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { IModelDecorationOptions, IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; +import { localize } from 'vs/nls'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ILogService } from 'vs/platform/log/common/log'; +import { registerColor } from 'vs/platform/theme/common/colorRegistry'; +import { registerThemingParticipant, themeColorFromId, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { debugStackframe, debugStackframeFocused } from 'vs/workbench/contrib/debug/browser/debugIcons'; -import { ILogService } from 'vs/platform/log/common/log'; +import { IDebugService, IStackFrame } from 'vs/workbench/contrib/debug/common/debug'; export const topStackFrameColor = registerColor('editor.stackFrameHighlightBackground', { dark: '#ffff0033', light: '#ffff6673', hcDark: '#ffff0033', hcLight: '#ffff6673' }, localize('topStackFrameLineHighlight', 'Background color for the highlight of line at the top stack frame position.')); export const focusedStackFrameColor = registerColor('editor.focusedStackFrameHighlightBackground', { dark: '#7abd7a4d', light: '#cee7ce73', hcDark: '#7abd7a4d', hcLight: '#cee7ce73' }, localize('focusedStackFrameLineHighlight', 'Background color for the highlight of line at focused stack frame position.')); @@ -107,8 +108,20 @@ export function createDecorationsForStackFrame(stackFrame: IStackFrame, isFocuse return result; } -export class CallStackEditorContribution implements IEditorContribution { - private toDispose: IDisposable[] = []; +export class LazyCallStackEditorContribution extends Disposable { + constructor(editor: ICodeEditor, @IInstantiationService instantiationService: IInstantiationService) { + super(); + + const listener = editor.onDidChangeModel(() => { + if (editor.hasModel()) { + listener.dispose(); + this._register(instantiationService.createInstance(CallStackEditorContribution, editor)); + } + }); + } +} + +class CallStackEditorContribution extends Disposable implements IEditorContribution { private decorations = this.editor.createDecorationsCollection(); constructor( @@ -117,15 +130,18 @@ export class CallStackEditorContribution implements IEditorContribution { @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, @ILogService private readonly logService: ILogService, ) { + super(); + const setDecorations = () => this.decorations.set(this.createCallStackDecorations()); - this.toDispose.push(Event.any(this.debugService.getViewModel().onDidFocusStackFrame, this.debugService.getModel().onDidChangeCallStack)(() => { + this._register(Event.any(this.debugService.getViewModel().onDidFocusStackFrame, this.debugService.getModel().onDidChangeCallStack)(() => { setDecorations(); })); - this.toDispose.push(this.editor.onDidChangeModel(e => { + this._register(this.editor.onDidChangeModel(e => { if (e.newModelUrl) { setDecorations(); } })); + setDecorations(); } private createCallStackDecorations(): IModelDeltaDecoration[] { @@ -169,9 +185,9 @@ export class CallStackEditorContribution implements IEditorContribution { return distinct(decorations, d => `${d.options.className} ${d.options.glyphMarginClassName} ${d.range.startLineNumber} ${d.range.startColumn}`); } - dispose(): void { + override dispose(): void { + super.dispose(); this.decorations.clear(); - this.toDispose = dispose(this.toDispose); } } diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index 7643f1a8057..fe90489df17 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -229,7 +229,7 @@ export class CallStackView extends ViewPane { this.instantiationService.createInstance(ThreadsRenderer), this.instantiationService.createInstance(StackFramesRenderer), new ErrorsRenderer(), - new LoadAllRenderer(this.themeService), + new LoadMoreRenderer(this.themeService), new ShowMoreRenderer(this.themeService) ], this.dataSource, { accessibilityProvider: new CallStackAccessibilityProvider(), @@ -259,7 +259,7 @@ export class CallStackView extends ViewPane { return e; } if (e instanceof ThreadAndSessionIds) { - return LoadAllRenderer.LABEL; + return LoadMoreRenderer.LABEL; } return localize('showMoreStackFrames2', "Show More Stack Frames"); @@ -621,6 +621,10 @@ class SessionsRenderer implements ICompressibleTreeRenderer, _: number, templateData: ISessionTemplateData): void { templateData.elementDisposable.clear(); } + + disposeCompressedElements(node: ITreeNode, FuzzyScore>, index: number, templateData: ISessionTemplateData, height: number | undefined): void { + templateData.elementDisposable.clear(); + } } function getThreadContextOverlay(thread: IThread): [string, any][] { @@ -804,14 +808,14 @@ class ErrorsRenderer implements ICompressibleTreeRenderer { - static readonly ID = 'loadAll'; - static readonly LABEL = localize('loadAllStackFrames', "Load All Stack Frames"); +class LoadMoreRenderer implements ICompressibleTreeRenderer { + static readonly ID = 'loadMore'; + static readonly LABEL = localize('loadAllStackFrames', "Load More Stack Frames"); constructor(private readonly themeService: IThemeService) { } get templateId(): string { - return LoadAllRenderer.ID; + return LoadMoreRenderer.ID; } renderTemplate(container: HTMLElement): ILabelTemplateData { @@ -826,7 +830,7 @@ class LoadAllRenderer implements ICompressibleTreeRenderer, index: number, data: ILabelTemplateData): void { - data.label.textContent = LoadAllRenderer.LABEL; + data.label.textContent = LoadMoreRenderer.LABEL; } renderCompressedElements(node: ITreeNode, FuzzyScore>, index: number, templateData: ILabelTemplateData, height: number | undefined): void { @@ -904,7 +908,7 @@ class CallStackDelegate implements IListVirtualDelegate { return ErrorsRenderer.ID; } if (element instanceof ThreadAndSessionIds) { - return LoadAllRenderer.ID; + return LoadMoreRenderer.ID; } // element instanceof Array @@ -1067,7 +1071,7 @@ class CallStackAccessibilityProvider implements IListAccessibilityProvider(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugStatusContribution, 'DebugStatusContribution', LifecyclePhase.Eventually); -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugProgressContribution, 'DebugProgressContribution', LifecyclePhase.Eventually); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugStatusContribution, LifecyclePhase.Eventually); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugProgressContribution, LifecyclePhase.Eventually); if (isWeb) { - Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugTitleContribution, 'DebugTitleContribution', LifecyclePhase.Eventually); + Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugTitleContribution, LifecyclePhase.Eventually); } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugToolBar, 'DebugToolBar', LifecyclePhase.Restored); -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugContentProvider, 'DebugContentProvider', LifecyclePhase.Eventually); -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(StatusBarColorProvider, 'StatusBarColorProvider', LifecyclePhase.Eventually); -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DisassemblyViewContribution, 'DisassemblyViewContribution', LifecyclePhase.Eventually); -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugLifecycle, 'DebugLifecycle', LifecyclePhase.Eventually); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugToolBar, LifecyclePhase.Restored); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugContentProvider, LifecyclePhase.Eventually); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(StatusBarColorProvider, LifecyclePhase.Eventually); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DisassemblyViewContribution, LifecyclePhase.Eventually); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugLifecycle, LifecyclePhase.Eventually); // Register Quick Access Registry.as(QuickAccessExtensions.Quickaccess).registerQuickAccessProvider({ @@ -93,9 +90,9 @@ Registry.as(QuickAccessExtensions.Quickaccess).registerQui helpEntries: [{ description: nls.localize('tasksQuickAccessHelp', "Show All Debug Consoles"), commandId: SELECT_DEBUG_CONSOLE_ID }] }); -registerEditorContribution('editor.contrib.callStack', CallStackEditorContribution); -registerEditorContribution(BREAKPOINT_EDITOR_CONTRIBUTION_ID, BreakpointEditorContribution); -registerEditorContribution(EDITOR_CONTRIBUTION_ID, DebugEditorContribution); +registerEditorContribution('editor.contrib.callStack', LazyCallStackEditorContribution); +registerEditorContribution(BREAKPOINT_EDITOR_CONTRIBUTION_ID, LazyBreakpointEditorContribution); +registerEditorContribution(EDITOR_CONTRIBUTION_ID, LazyDebugEditorContribution); const registerDebugCommandPaletteItem = (id: string, title: ICommandActionTitle, when?: ContextKeyExpression, precondition?: ContextKeyExpression) => { MenuRegistry.appendMenuItem(MenuId.CommandPalette, { diff --git a/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts b/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts index 7f08c57a578..8754713b3b1 100644 --- a/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts @@ -43,8 +43,8 @@ export class AdapterManager extends Disposable implements IAdapterManager { private debuggers: Debugger[]; private adapterDescriptorFactories: IDebugAdapterDescriptorFactory[]; private debugAdapterFactories = new Map(); - private debuggersAvailable: IContextKey; - private debugExtensionsAvailable: IContextKey; + private debuggersAvailable!: IContextKey; + private debugExtensionsAvailable!: IContextKey; private readonly _onDidRegisterDebugger = new Emitter(); private readonly _onDidDebuggersExtPointRead = new Emitter(); private breakpointContributions: Breakpoints[] = []; @@ -72,15 +72,16 @@ export class AdapterManager extends Disposable implements IAdapterManager { this.adapterDescriptorFactories = []; this.debuggers = []; this.registerListeners(); - this.debuggersAvailable = CONTEXT_DEBUGGERS_AVAILABLE.bindTo(contextKeyService); + this.contextKeyService.bufferChangeEvents(() => { + this.debuggersAvailable = CONTEXT_DEBUGGERS_AVAILABLE.bindTo(contextKeyService); + this.debugExtensionsAvailable = CONTEXT_DEBUG_EXTENSION_AVAILABLE.bindTo(contextKeyService); + }); this._register(this.contextKeyService.onDidChangeContext(e => { if (e.affectsSome(this.debuggerWhenKeys)) { this.debuggersAvailable.set(this.hasEnabledDebuggers()); this.updateDebugAdapterSchema(); } })); - this.debugExtensionsAvailable = CONTEXT_DEBUG_EXTENSION_AVAILABLE.bindTo(contextKeyService); - this.debugExtensionsAvailable.set(true); // Avoid a flash of the default message before extensions load. this._register(this.onDidDebuggersExtPointRead(() => { this.debugExtensionsAvailable.set(this.debuggers.length > 0); })); diff --git a/src/vs/workbench/contrib/debug/browser/debugCommands.ts b/src/vs/workbench/contrib/debug/browser/debugCommands.ts index f04f2889942..b33e1b7a7a4 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCommands.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCommands.ts @@ -12,7 +12,7 @@ import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_ import { Expression, Variable, Breakpoint, FunctionBreakpoint, DataBreakpoint, Thread } from 'vs/workbench/contrib/debug/common/debugModel'; import { IExtensionsViewPaneContainer, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; +import { MenuRegistry, MenuId, Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -20,7 +20,7 @@ import { openBreakpointSource } from 'vs/workbench/contrib/debug/browser/breakpo import { INotificationService } from 'vs/platform/notification/common/notification'; import { InputFocusedContext } from 'vs/platform/contextkey/common/contextkeys'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; -import { PanelFocusContext } from 'vs/workbench/common/contextkeys'; +import { ActiveEditorContext, PanelFocusContext, ResourceContextKey } from 'vs/workbench/common/contextkeys'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfiguration'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; @@ -33,6 +33,8 @@ import { saveAllBeforeDebugStart } from 'vs/workbench/contrib/debug/common/debug import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { showLoadedScriptMenu } from 'vs/workbench/contrib/debug/common/loadedScriptsPicker'; import { showDebugSessionMenu } from 'vs/workbench/contrib/debug/browser/debugSessionPicker'; +import { TEXT_FILE_EDITOR_ID } from 'vs/workbench/contrib/files/common/files'; +import { ILocalizedString } from 'vs/platform/action/common/action'; export const ADD_CONFIGURATION_ID = 'debug.addConfiguration'; export const TOGGLE_INLINE_BREAKPOINT_ID = 'editor.debug.action.toggleInlineBreakpoint'; @@ -71,7 +73,7 @@ export const CALLSTACK_BOTTOM_ID = 'workbench.action.debug.callStackBottom'; export const CALLSTACK_UP_ID = 'workbench.action.debug.callStackUp'; export const CALLSTACK_DOWN_ID = 'workbench.action.debug.callStackDown'; -export const DEBUG_COMMAND_CATEGORY = 'Debug'; +export const DEBUG_COMMAND_CATEGORY: ILocalizedString = { original: 'Debug', value: nls.localize('debug', 'Debug') }; export const RESTART_LABEL = { value: nls.localize('restartDebug', "Restart"), original: 'Restart' }; export const STEP_OVER_LABEL = { value: nls.localize('stepOverDebug', "Step Over"), original: 'Step Over' }; export const STEP_INTO_LABEL = { value: nls.localize('stepIntoDebug', "Step Into"), original: 'Step Into' }; @@ -406,7 +408,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorContext, { command: { id: JUMP_TO_CURSOR_ID, title: nls.localize('jumpToCursor', "Jump to Cursor"), - category: { value: nls.localize('debug', "Debug"), original: 'Debug' } + category: DEBUG_COMMAND_CATEGORY }, when: ContextKeyExpr.and(CONTEXT_JUMP_TO_CURSOR_SUPPORTED, EditorContextKeys.editorTextFocus), group: 'debug', @@ -915,12 +917,23 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } }); -KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: ADD_CONFIGURATION_ID, - weight: KeybindingWeight.WorkbenchContrib, - when: undefined, - primary: undefined, - handler: async (accessor, launchUri: string) => { +registerAction2(class AddConfigurationAction extends Action2 { + constructor() { + super({ + id: ADD_CONFIGURATION_ID, + title: { value: nls.localize('addConfiguration', "Add Configuration..."), original: 'Add Configuration...' }, + category: DEBUG_COMMAND_CATEGORY, + f1: true, + menu: { + id: MenuId.EditorContent, + when: ContextKeyExpr.and( + ContextKeyExpr.regex(ResourceContextKey.Path.key, /\.vscode[/\\]launch\.json$/), + ActiveEditorContext.isEqualTo(TEXT_FILE_EDITOR_ID)) + } + }); + } + + async run(accessor: ServicesAccessor, launchUri: string): Promise { const manager = accessor.get(IDebugService).getConfigurationManager(); const launch = manager.getLaunches().find(l => l.uri.toString() === launchUri) || manager.selectedConfiguration.launch; @@ -966,7 +979,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorContext, { command: { id: TOGGLE_INLINE_BREAKPOINT_ID, title: nls.localize('addInlineBreakpoint', "Add Inline Breakpoint"), - category: { value: nls.localize('debug', "Debug"), original: 'Debug' } + category: DEBUG_COMMAND_CATEGORY }, when: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, PanelFocusContext.toNegated(), EditorContextKeys.editorTextFocus), group: 'debug', diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index 3c2568989db..fe3d0e29842 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -3,41 +3,41 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; -import { dispose, IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { Event, Emitter } from 'vs/base/common/event'; -import * as objects from 'vs/base/common/objects'; -import * as json from 'vs/base/common/json'; -import { URI as uri } from 'vs/base/common/uri'; -import * as resources from 'vs/base/common/resources'; -import { IJSONSchema } from 'vs/base/common/jsonSchema'; -import { IEditorPane } from 'vs/workbench/common/editor'; -import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { IFileService } from 'vs/platform/files/common/files'; -import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IDebugConfigurationProvider, ICompound, IConfig, IGlobalConfig, IConfigurationManager, ILaunch, CONTEXT_DEBUG_CONFIGURATION_TYPE, IConfigPresentation, DebugConfigurationProviderTriggerKind } from 'vs/workbench/contrib/debug/common/debug'; -import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; -import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration'; -import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; -import { launchSchema } from 'vs/workbench/contrib/debug/common/debugSchemas'; -import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; -import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; -import { withUndefinedAsNull } from 'vs/base/common/types'; +import { distinct, flatten } from 'vs/base/common/arrays'; import { sequence } from 'vs/base/common/async'; -import { IHistoryService } from 'vs/workbench/services/history/common/history'; -import { flatten, distinct } from 'vs/base/common/arrays'; -import { getVisibleAndSorted } from 'vs/workbench/contrib/debug/common/debugUtils'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { Emitter, Event } from 'vs/base/common/event'; +import * as json from 'vs/base/common/json'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import * as objects from 'vs/base/common/objects'; +import * as resources from 'vs/base/common/resources'; +import { withUndefinedAsNull } from 'vs/base/common/types'; +import { URI as uri } from 'vs/base/common/uri'; +import * as nls from 'vs/nls'; +import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { Extensions as JSONExtensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { IWorkspaceContextService, IWorkspaceFolder, IWorkspaceFoldersChangeEvent, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IEditorPane } from 'vs/workbench/common/editor'; import { AdapterManager } from 'vs/workbench/contrib/debug/browser/debugAdapterManager'; import { debugConfigure } from 'vs/workbench/contrib/debug/browser/debugIcons'; -import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { CONTEXT_DEBUG_CONFIGURATION_TYPE, DebugConfigurationProviderTriggerKind, ICompound, IConfig, IConfigPresentation, IConfigurationManager, IDebugConfigurationProvider, IGlobalConfig, ILaunch } from 'vs/workbench/contrib/debug/common/debug'; +import { launchSchema } from 'vs/workbench/contrib/debug/common/debugSchemas'; +import { getVisibleAndSorted } from 'vs/workbench/contrib/debug/common/debugUtils'; +import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration'; +import { ACTIVE_GROUP, IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IHistoryService } from 'vs/workbench/services/history/common/history'; +import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; const jsonRegistry = Registry.as(JSONExtensions.JSONContribution); jsonRegistry.registerSchema(launchSchemaId, launchSchema); @@ -56,6 +56,7 @@ export class ConfigurationManager implements IConfigurationManager { private selectedLaunch: ILaunch | undefined; private getSelectedConfig: () => Promise = () => Promise.resolve(undefined); private selectedType: string | undefined; + private selectedDynamic = false; private toDispose: IDisposable[]; private readonly _onDidSelectConfigurationName = new Emitter(); private configProviders: IDebugConfigurationProvider[]; @@ -279,7 +280,7 @@ export class ConfigurationManager implements IConfigurationManager { removeRecentDynamicConfigurations(name: string, type: string) { const remaining = this.getRecentDynamicConfigurations().filter(c => c.name !== name || c.type !== type); this.storageService.store(DEBUG_RECENT_DYNAMIC_CONFIGURATIONS, JSON.stringify(remaining), StorageScope.WORKSPACE, StorageTarget.USER); - if (this.selectedConfiguration.name === name && this.selectedType === type) { + if (this.selectedConfiguration.name === name && this.selectedType === type && this.selectedDynamic) { this.selectConfiguration(undefined, undefined); } else { this._onDidSelectConfigurationName.fire(); @@ -377,6 +378,7 @@ export class ConfigurationManager implements IConfigurationManager { const previousLaunch = this.selectedLaunch; const previousName = this.selectedName; + const previousSelectedDynamic = this.selectedDynamic; this.selectedLaunch = launch; if (this.selectedLaunch) { @@ -436,6 +438,7 @@ export class ConfigurationManager implements IConfigurationManager { } this.selectedType = dynamicConfig?.type || config?.type; + this.selectedDynamic = !!dynamicConfig; // Only store the selected type if we are having a dynamic configuration. Otherwise restoring this configuration from storage might be misindentified as a dynamic configuration this.storageService.store(DEBUG_SELECTED_TYPE, dynamicConfig ? this.selectedType : undefined, StorageScope.WORKSPACE, StorageTarget.MACHINE); @@ -445,7 +448,7 @@ export class ConfigurationManager implements IConfigurationManager { this.debugConfigurationTypeContext.reset(); } - if (this.selectedLaunch !== previousLaunch || this.selectedName !== previousName) { + if (this.selectedLaunch !== previousLaunch || this.selectedName !== previousName || previousSelectedDynamic !== this.selectedDynamic) { this._onDidSelectConfigurationName.fire(); } } diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index 9687e574a1a..021e9b52295 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -3,29 +3,28 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; -import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; -import { Range } from 'vs/editor/common/core/range'; -import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { registerEditorAction, EditorAction, IActionOptions, EditorAction2 } from 'vs/editor/browser/editorExtensions'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution, REPL_VIEW_ID, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, WATCH_VIEW_ID, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_EXCEPTION_WIDGET_VISIBLE, CONTEXT_DISASSEMBLE_REQUEST_SUPPORTED, CONTEXT_LANGUAGE_SUPPORTS_DISASSEMBLE_REQUEST, CONTEXT_FOCUSED_STACK_FRAME_HAS_INSTRUCTION_POINTER_REFERENCE, CONTEXT_CALLSTACK_ITEM_TYPE, IDebugConfiguration } from 'vs/workbench/contrib/debug/common/debug'; +import { getDomNodePagePosition } from 'vs/base/browser/dom'; +import { Action } from 'vs/base/common/actions'; +import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { openBreakpointSource } from 'vs/workbench/contrib/debug/browser/breakpointsView'; +import { EditorAction, EditorAction2, IActionOptions, registerEditorAction } from 'vs/editor/browser/editorExtensions'; +import { Position } from 'vs/editor/common/core/position'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { MessageController } from 'vs/editor/contrib/message/browser/messageController'; +import * as nls from 'vs/nls'; +import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { PanelFocusContext } from 'vs/workbench/common/contextkeys'; import { IViewsService } from 'vs/workbench/common/views'; -import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; -import { registerAction2, MenuId, Action2 } from 'vs/platform/actions/common/actions'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { openBreakpointSource } from 'vs/workbench/contrib/debug/browser/breakpointsView'; +import { BreakpointWidgetContext, BREAKPOINT_EDITOR_CONTRIBUTION_ID, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE, CONTEXT_DISASSEMBLE_REQUEST_SUPPORTED, CONTEXT_EXCEPTION_WIDGET_VISIBLE, CONTEXT_FOCUSED_STACK_FRAME_HAS_INSTRUCTION_POINTER_REFERENCE, CONTEXT_IN_DEBUG_MODE, CONTEXT_LANGUAGE_SUPPORTS_DISASSEMBLE_REQUEST, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution, IDebugConfiguration, IDebugEditorContribution, IDebugService, REPL_VIEW_ID, WATCH_VIEW_ID } from 'vs/workbench/contrib/debug/common/debug'; import { DisassemblyViewInput } from 'vs/workbench/contrib/debug/common/disassemblyViewInput'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { MessageController } from 'vs/editor/contrib/message/browser/messageController'; -import { getDomNodePagePosition } from 'vs/base/browser/dom'; -import { Position } from 'vs/editor/common/core/position'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { Action } from 'vs/base/common/actions'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; class ToggleBreakpointAction extends EditorAction { constructor() { @@ -321,13 +320,8 @@ class ShowDebugHoverAction extends EditorAction { if (!position || !editor.hasModel()) { return; } - const word = editor.getModel().getWordAtPosition(position); - if (!word) { - return; - } - const range = new Range(position.lineNumber, position.column, position.lineNumber, word.endColumn); - return editor.getContribution(EDITOR_CONTRIBUTION_ID)?.showHover(range, true); + return editor.getContribution(EDITOR_CONTRIBUTION_ID)?.showHover(position, true); } } diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts index 48a002966c6..423a4d430e6 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts @@ -32,7 +32,7 @@ import { memoize } from 'vs/base/common/decorators'; import { IEditorHoverOptions, EditorOption } from 'vs/editor/common/config/editorOptions'; import { DebugHoverWidget } from 'vs/workbench/contrib/debug/browser/debugHover'; import { IModelDeltaDecoration, InjectedTextCursorStops, ITextModel } from 'vs/editor/common/model'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { basename } from 'vs/base/common/path'; import { ModesHoverController } from 'vs/editor/contrib/hover/browser/hover'; @@ -48,7 +48,6 @@ import { DomEmitter } from 'vs/base/browser/event'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce'; -const LAUNCH_JSON_REGEX = /\.vscode\/launch\.json$/; const MAX_NUM_INLINE_VALUES = 100; // JS Global scope can have 700+ entries. We want to limit ourselves for perf reasons const MAX_INLINE_DECORATOR_LENGTH = 150; // Max string length of each inline decorator when debugging. If exceeded ... is added const MAX_TOKENIZATION_LINE_LEN = 500; // If line is too long, then inline values for the line are skipped @@ -210,11 +209,38 @@ function getWordToLineNumbersMap(model: ITextModel | null): Map { + if (editor.hasModel()) { + listener.dispose(); + this._contrib = this._register(instantiationService.createInstance(DebugEditorContribution, editor)); + } + }); + } + + showHover(position: Position, focus: boolean): Promise { + return this._contrib ? this._contrib.showHover(position, focus) : Promise.resolve(); + } + + addLaunchConfiguration(): Promise { + return this._contrib ? this._contrib.addLaunchConfiguration() : Promise.resolve(); + } + + closeExceptionWidget(): void { + this._contrib?.closeExceptionWidget(); + } +} + export class DebugEditorContribution implements IDebugEditorContribution { private toDispose: IDisposable[]; private hoverWidget: DebugHoverWidget; - private hoverRange: Range | null = null; + private hoverPosition: Position | null = null; private mouseDown = false; private exceptionWidgetVisible: IContextKey; @@ -241,7 +267,6 @@ export class DebugEditorContribution implements IDebugEditorContribution { this.hoverWidget = this.instantiationService.createInstance(DebugHoverWidget, this.editor); this.toDispose = []; this.registerListeners(); - this.updateConfigurationWidgetVisibility(); this.exceptionWidgetVisible = CONTEXT_EXCEPTION_WIDGET_VISIBLE.bindTo(contextKeyService); this.toggleExceptionWidget(); } @@ -280,7 +305,6 @@ export class DebugEditorContribution implements IDebugEditorContribution { } this.toggleExceptionWidget(); this.hideHoverWidget(); - this.updateConfigurationWidgetVisibility(); this._wordToLineNumbersMap = undefined; await this.updateInlineValueDecorations(stackFrame); })); @@ -321,10 +345,11 @@ export class DebugEditorContribution implements IDebugEditorContribution { const debugHoverWasVisible = this.hoverWidget.isVisible(); this.hoverWidget.hide(); this.enableEditorHover(); - if (debugHoverWasVisible && this.hoverRange) { + if (debugHoverWasVisible && this.hoverPosition) { // If the debug hover was visible immediately show the editor hover for the alt transition to be smooth const hoverController = this.editor.getContribution(ModesHoverController.ID); - hoverController?.showContentHover(this.hoverRange, HoverStartMode.Immediate, false); + const range = new Range(this.hoverPosition.lineNumber, this.hoverPosition.column, this.hoverPosition.lineNumber, this.hoverPosition.column); + hoverController?.showContentHover(range, HoverStartMode.Immediate, false); } const onKeyUp = new DomEmitter(document, 'keyup'); @@ -368,11 +393,11 @@ export class DebugEditorContribution implements IDebugEditorContribution { } } - async showHover(range: Range, focus: boolean): Promise { + async showHover(position: Position, focus: boolean): Promise { const sf = this.debugService.getViewModel().focusedStackFrame; const model = this.editor.getModel(); if (sf && model && this.uriIdentityService.extUri.isEqual(sf.source.uri, model.uri) && !this.altPressed) { - return this.hoverWidget.showAt(range, focus); + return this.hoverWidget.showAt(position, focus); } } @@ -394,8 +419,8 @@ export class DebugEditorContribution implements IDebugEditorContribution { private get showHoverScheduler(): RunOnceScheduler { const hoverOption = this.editor.getOption(EditorOption.hover); const scheduler = new RunOnceScheduler(() => { - if (this.hoverRange) { - this.showHover(this.hoverRange, false); + if (this.hoverPosition) { + this.showHover(this.hoverPosition, false); } }, hoverOption.delay * 2); this.toDispose.push(scheduler); @@ -446,8 +471,8 @@ export class DebugEditorContribution implements IDebugEditorContribution { return; } if (target.type === MouseTargetType.CONTENT_TEXT) { - if (target.range && !target.range.equalsRange(this.hoverRange)) { - this.hoverRange = target.range; + if (target.position && !Position.equals(target.position, this.hoverPosition)) { + this.hoverPosition = target.position; this.hideHoverScheduler.cancel(); this.showHoverScheduler.schedule(); } @@ -524,19 +549,6 @@ export class DebugEditorContribution implements IDebugEditorContribution { } } - // configuration widget - private updateConfigurationWidgetVisibility(): void { - const model = this.editor.getModel(); - if (this.configurationWidget) { - this.configurationWidget.dispose(); - } - if (model && LAUNCH_JSON_REGEX.test(model.uri.toString()) && !this.editor.getOption(EditorOption.readOnly)) { - this.configurationWidget = this.instantiationService.createInstance(FloatingClickWidget, this.editor, nls.localize('addConfiguration', "Add Configuration..."), null); - this.configurationWidget.render(); - this.toDispose.push(this.configurationWidget.onClick(() => this.addLaunchConfiguration())); - } - } - async addLaunchConfiguration(): Promise { const model = this.editor.getModel(); if (!model) { diff --git a/src/vs/workbench/contrib/debug/browser/debugHover.ts b/src/vs/workbench/contrib/debug/browser/debugHover.ts index 6308c2b6c5b..40afc3e611e 100644 --- a/src/vs/workbench/contrib/debug/browser/debugHover.ts +++ b/src/vs/workbench/contrib/debug/browser/debugHover.ts @@ -3,43 +3,44 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; -import * as lifecycle from 'vs/base/common/lifecycle'; -import { KeyCode } from 'vs/base/common/keyCodes'; -import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import * as dom from 'vs/base/browser/dom'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; +import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; +import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; +import { IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; +import { coalesce } from 'vs/base/common/arrays'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import * as lifecycle from 'vs/base/common/lifecycle'; +import { isMacintosh } from 'vs/base/common/platform'; +import { ScrollbarVisibility } from 'vs/base/common/scrollable'; +import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; -import { Range, IRange } from 'vs/editor/common/core/range'; -import { IContentWidget, ICodeEditor, IContentWidgetPosition, ContentWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import { ITextModel } from 'vs/editor/common/model'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; +import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; +import * as nls from 'vs/nls'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IDebugService, IExpression, IExpressionContainer, IStackFrame } from 'vs/workbench/contrib/debug/common/debug'; -import { Expression, Variable } from 'vs/workbench/contrib/debug/common/debugModel'; -import { renderExpressionValue } from 'vs/workbench/contrib/debug/browser/baseDebugView'; -import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; +import { editorHoverBackground, editorHoverBorder, editorHoverForeground } from 'vs/platform/theme/common/colorRegistry'; import { attachStylerCallback } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { editorHoverBackground, editorHoverBorder, editorHoverForeground } from 'vs/platform/theme/common/colorRegistry'; -import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; -import { getExactExpressionStartAndEnd } from 'vs/workbench/contrib/debug/common/debugUtils'; -import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; -import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; -import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; -import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; -import { coalesce } from 'vs/base/common/arrays'; -import { IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; -import { VariablesRenderer } from 'vs/workbench/contrib/debug/browser/variablesView'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; -import { isMacintosh } from 'vs/base/common/platform'; +import { renderExpressionValue } from 'vs/workbench/contrib/debug/browser/baseDebugView'; import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector'; -import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; +import { VariablesRenderer } from 'vs/workbench/contrib/debug/browser/variablesView'; +import { IDebugService, IDebugSession, IExpression, IExpressionContainer, IStackFrame } from 'vs/workbench/contrib/debug/common/debug'; +import { Expression, Variable } from 'vs/workbench/contrib/debug/common/debugModel'; +import { getExactExpressionStartAndEnd } from 'vs/workbench/contrib/debug/common/debugUtils'; const $ = dom.$; async function doFindExpression(container: IExpressionContainer, namesToFind: string[]): Promise { if (!container) { - return Promise.resolve(null); + return null; } const children = await container.getChildren(); @@ -69,7 +70,7 @@ export class DebugHoverWidget implements IContentWidget { static readonly ID = 'debug.hoverWidget'; // editor.IContentWidget.allowEditorOverflow - allowEditorOverflow = true; + readonly allowEditorOverflow = true; private _isVisible: boolean; private showCancellationSource?: CancellationTokenSource; @@ -84,19 +85,20 @@ export class DebugHoverWidget implements IContentWidget { private treeContainer!: HTMLElement; private toDispose: lifecycle.IDisposable[]; private scrollbar!: DomScrollableElement; + private debugHoverComputer: DebugHoverComputer; constructor( private editor: ICodeEditor, @IDebugService private readonly debugService: IDebugService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IThemeService private readonly themeService: IThemeService, - @ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService, + @IThemeService private readonly themeService: IThemeService ) { this.toDispose = []; this._isVisible = false; this.showAtPosition = null; this.positionPreference = [ContentWidgetPositionPreference.ABOVE, ContentWidgetPositionPreference.BELOW]; + this.debugHoverComputer = this.instantiationService.createInstance(DebugHoverComputer, this.editor); } private create(): void { @@ -193,81 +195,38 @@ export class DebugHoverWidget implements IContentWidget { return this.domNode; } - async showAt(range: Range, focus: boolean): Promise { + async showAt(position: Position, focus: boolean): Promise { this.showCancellationSource?.cancel(); const cancellationSource = this.showCancellationSource = new CancellationTokenSource(); const session = this.debugService.getViewModel().focusedSession; if (!session || !this.editor.hasModel()) { - return Promise.resolve(this.hide()); + this.hide(); + return; } - const model = this.editor.getModel(); - const pos = range.getStartPosition(); - - let rng: IRange | undefined = undefined; - let matchingExpression: string | undefined; - - if (this.languageFeaturesService.evaluatableExpressionProvider.has(model)) { - const supports = this.languageFeaturesService.evaluatableExpressionProvider.ordered(model); - - const promises = supports.map(support => { - return Promise.resolve(support.provideEvaluatableExpression(model, pos, cancellationSource.token)).then(expression => { - return expression; - }, err => { - //onUnexpectedExternalError(err); - return undefined; - }); - }); - - const results = await Promise.all(promises).then(coalesce); - if (results.length > 0) { - matchingExpression = results[0].expression; - rng = results[0].range; - - if (!matchingExpression) { - const lineContent = model.getLineContent(pos.lineNumber); - matchingExpression = lineContent.substring(rng.startColumn - 1, rng.endColumn - 1); - } - } - - } else { // old one-size-fits-all strategy - const lineContent = model.getLineContent(pos.lineNumber); - const { start, end } = getExactExpressionStartAndEnd(lineContent, range.startColumn, range.endColumn); - - // use regex to extract the sub-expression #9821 - matchingExpression = lineContent.substring(start - 1, end); - rng = new Range(pos.lineNumber, start, pos.lineNumber, start + matchingExpression.length); + const result = await this.debugHoverComputer.compute(position, cancellationSource.token); + if (this.isVisible() && !result.rangeChanged) { + return; } - if (!matchingExpression) { - return Promise.resolve(this.hide()); - } - - let expression; - if (session.capabilities.supportsEvaluateForHovers) { - expression = new Expression(matchingExpression); - await expression.evaluate(session, this.debugService.getViewModel().focusedStackFrame, 'hover'); - } else { - const focusedStackFrame = this.debugService.getViewModel().focusedStackFrame; - if (focusedStackFrame) { - expression = await findExpressionInStackFrame(focusedStackFrame, coalesce(matchingExpression.split('.').map(word => word.trim()))); - } + if (!result.range || cancellationSource.token.isCancellationRequested) { + this.hide(); + return; } + const expression = await this.debugHoverComputer.evaluate(session); if (cancellationSource.token.isCancellationRequested || !expression || (expression instanceof Expression && !expression.available)) { this.hide(); return; } - if (rng) { - this.highlightDecorations.set([{ - range: rng, - options: DebugHoverWidget._HOVER_HIGHLIGHT_DECORATION_OPTIONS - }]); - } + this.highlightDecorations.set([{ + range: result.range, + options: DebugHoverWidget._HOVER_HIGHLIGHT_DECORATION_OPTIONS + }]); - return this.doShow(pos, expression, focus); + return this.doShow(result.range.getStartPosition(), expression, focus); } private static readonly _HOVER_HIGHLIGHT_DECORATION_OPTIONS = ModelDecorationOptions.register({ @@ -298,7 +257,7 @@ export class DebugHoverWidget implements IContentWidget { this.valueContainer.focus(); } - return Promise.resolve(undefined); + return undefined; } this.valueContainer.hidden = true; @@ -396,3 +355,101 @@ class DebugHoverDelegate implements IListVirtualDelegate { return VariablesRenderer.ID; } } + +interface IDebugHoverComputeResult { + rangeChanged: boolean; + range?: Range; +} + +class DebugHoverComputer { + private _currentRange: Range | undefined; + private _currentExpression: string | undefined; + + constructor( + private editor: ICodeEditor, + @IDebugService private readonly debugService: IDebugService, + @ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService, + ) { } + + public async compute(position: Position, token: CancellationToken): Promise { + const session = this.debugService.getViewModel().focusedSession; + + if (!session || !this.editor.hasModel()) { + return { rangeChanged: false }; + } + + const model = this.editor.getModel(); + + const result = await this.doCompute(model, position, token); + if (!result) { + return { rangeChanged: false }; + } + + const { range, matchingExpression } = result; + const rangeChanged = this._currentRange ? + !this._currentRange.equalsRange(range) : + true; + this._currentExpression = matchingExpression; + this._currentRange = Range.lift(range); + return { rangeChanged, range: this._currentRange }; + } + + private async doCompute(model: ITextModel, position: Position, token: CancellationToken): Promise<{ range: IRange; matchingExpression: string } | null> { + if (this.languageFeaturesService.evaluatableExpressionProvider.has(model)) { + const supports = this.languageFeaturesService.evaluatableExpressionProvider.ordered(model); + + const results = coalesce(await Promise.all(supports.map(async support => { + try { + return await support.provideEvaluatableExpression(model, position, token); + } catch (err) { + return undefined; + } + }))); + + if (results.length > 0) { + let matchingExpression = results[0].expression; + const range = results[0].range; + + if (!matchingExpression) { + const lineContent = model.getLineContent(position.lineNumber); + matchingExpression = lineContent.substring(range.startColumn - 1, range.endColumn - 1); + } + + return { range, matchingExpression }; + } + } else { // old one-size-fits-all strategy + const lineContent = model.getLineContent(position.lineNumber); + const { start, end } = getExactExpressionStartAndEnd(lineContent, position.column, position.column); + + // use regex to extract the sub-expression #9821 + const matchingExpression = lineContent.substring(start - 1, end); + return { + matchingExpression, + range: new Range(position.lineNumber, start, position.lineNumber, start + matchingExpression.length) + }; + } + + return null; + } + + async evaluate(session: IDebugSession): Promise { + if (!this._currentExpression) { + throw new Error('No expression to evaluate'); + } + + if (session.capabilities.supportsEvaluateForHovers) { + const expression = new Expression(this._currentExpression); + await expression.evaluate(session, this.debugService.getViewModel().focusedStackFrame, 'hover'); + return expression; + } else { + const focusedStackFrame = this.debugService.getViewModel().focusedStackFrame; + if (focusedStackFrame) { + return await findExpressionInStackFrame( + focusedStackFrame, + coalesce(this._currentExpression.split('.').map(word => word.trim()))); + } + } + + return undefined; + } +} diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 5d6622a3b7f..99327f8edd6 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -84,6 +84,7 @@ export class DebugService implements IDebugService { private sessionCancellationTokens = new Map(); private activity: IDisposable | undefined; private chosenEnvironments: { [key: string]: string }; + private haveDoneLazySetup = false; constructor( @IEditorService private readonly editorService: IEditorService, @@ -120,27 +121,14 @@ export class DebugService implements IDebugService { this.disposables.add(this.configurationManager); this.debugStorage = this.instantiationService.createInstance(DebugStorage); - contextKeyService.bufferChangeEvents(() => { - this.debugType = CONTEXT_DEBUG_TYPE.bindTo(contextKeyService); - this.debugState = CONTEXT_DEBUG_STATE.bindTo(contextKeyService); - this.inDebugMode = CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService); - this.debugUx = CONTEXT_DEBUG_UX.bindTo(contextKeyService); - this.debugUx.set(this.debugStorage.loadDebugUxState()); - this.breakpointsExist = CONTEXT_BREAKPOINTS_EXIST.bindTo(contextKeyService); - // Need to set disassemblyViewFocus here to make it in the same context as the debug event handlers - this.disassemblyViewFocus = CONTEXT_DISASSEMBLY_VIEW_FOCUS.bindTo(contextKeyService); - }); this.chosenEnvironments = this.debugStorage.loadChosenEnvironments(); this.model = this.instantiationService.createInstance(DebugModel, this.debugStorage); this.telemetry = this.instantiationService.createInstance(DebugTelemetry, this.model); - const setBreakpointsExistContext = () => this.breakpointsExist.set(!!(this.model.getBreakpoints().length || this.model.getDataBreakpoints().length || this.model.getFunctionBreakpoints().length)); - setBreakpointsExistContext(); this.viewModel = new ViewModel(contextKeyService); this.taskRunner = this.instantiationService.createInstance(DebugTaskRunner); - this.disposables.add(this.fileService.registerProvider(DEBUG_MEMORY_SCHEME, new DebugMemoryFileSystemProvider(this))); this.disposables.add(this.fileService.onDidFilesChange(e => this.onFileChanges(e))); this.disposables.add(this.lifecycleService.onWillShutdown(this.dispose, this)); @@ -182,7 +170,6 @@ export class DebugService implements IDebugService { } } })); - this.disposables.add(this.model.onDidChangeBreakpoints(() => setBreakpointsExistContext())); this.disposables.add(editorService.onDidActiveEditorChange(() => { this.contextKeyService.bufferChangeEvents(() => { @@ -202,6 +189,27 @@ export class DebugService implements IDebugService { } } })); + + this.initContextKeys(contextKeyService); + } + + private initContextKeys(contextKeyService: IContextKeyService): void { + queueMicrotask(() => { + contextKeyService.bufferChangeEvents(() => { + this.debugType = CONTEXT_DEBUG_TYPE.bindTo(contextKeyService); + this.debugState = CONTEXT_DEBUG_STATE.bindTo(contextKeyService); + this.inDebugMode = CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService); + this.debugUx = CONTEXT_DEBUG_UX.bindTo(contextKeyService); + this.debugUx.set(this.debugStorage.loadDebugUxState()); + this.breakpointsExist = CONTEXT_BREAKPOINTS_EXIST.bindTo(contextKeyService); + // Need to set disassemblyViewFocus here to make it in the same context as the debug event handlers + this.disassemblyViewFocus = CONTEXT_DISASSEMBLY_VIEW_FOCUS.bindTo(contextKeyService); + }); + + const setBreakpointsExistContext = () => this.breakpointsExist.set(!!(this.model.getBreakpoints().length || this.model.getDataBreakpoints().length || this.model.getFunctionBreakpoints().length)); + setBreakpointsExistContext(); + this.disposables.add(this.model.onDidChangeBreakpoints(() => setBreakpointsExistContext())); + }); } getModel(): IDebugModel { @@ -304,6 +312,15 @@ export class DebugService implements IDebugService { return this._onDidEndSession.event; } + private lazySetup() { + if (!this.haveDoneLazySetup) { + // Registering fs providers is slow + // https://github.com/microsoft/vscode/issues/159886 + this.disposables.add(this.fileService.registerProvider(DEBUG_MEMORY_SCHEME, new DebugMemoryFileSystemProvider(this))); + this.haveDoneLazySetup = true; + } + } + //---- life cycle management /** @@ -316,6 +333,9 @@ export class DebugService implements IDebugService { if (!trust) { return false; } + + this.lazySetup(); + this.startInitializingState(options); try { // make sure to save all files and that the configuration is up to date @@ -559,7 +579,7 @@ export class DebugService implements IDebugService { const openDebug = this.configurationService.getValue('debug').openDebug; // Open debug viewlet based on the visibility of the side bar and openDebug setting. Do not open for 'run without debug' - if (!configuration.resolved.noDebug && (openDebug === 'openOnSessionStart' || (openDebug !== 'neverOpen' && this.viewModel.firstSessionStart)) && !session.isSimpleUI) { + if (!configuration.resolved.noDebug && (openDebug === 'openOnSessionStart' || (openDebug !== 'neverOpen' && this.viewModel.firstSessionStart)) && !session.suppressDebugView) { await this.paneCompositeService.openPaneComposite(VIEWLET_ID, ViewContainerLocation.Sidebar); } @@ -1038,12 +1058,27 @@ export class DebugService implements IDebugService { } async sendAllBreakpoints(session?: IDebugSession): Promise { - await Promise.all(distinct(this.model.getBreakpoints(), bp => bp.uri.toString()).map(bp => this.sendBreakpoints(bp.uri, false, session))); - await this.sendFunctionBreakpoints(session); - await this.sendDataBreakpoints(session); - await this.sendInstructionBreakpoints(session); - // send exception breakpoints at the end since some debug adapters rely on the order - await this.sendExceptionBreakpoints(session); + const setBreakpointsPromises = distinct(this.model.getBreakpoints(), bp => bp.uri.toString()) + .map(bp => this.sendBreakpoints(bp.uri, false, session)); + + // If sending breakpoints to one session which we know supports the configurationDone request, can make all requests in parallel + if (session?.capabilities.supportsConfigurationDoneRequest) { + await Promise.all([ + ...setBreakpointsPromises, + this.sendFunctionBreakpoints(session), + this.sendDataBreakpoints(session), + this.sendInstructionBreakpoints(session), + this.sendExceptionBreakpoints(session), + ]); + } else { + await Promise.all(setBreakpointsPromises); + await this.sendFunctionBreakpoints(session); + await this.sendDataBreakpoints(session); + await this.sendInstructionBreakpoints(session); + // send exception breakpoints at the end since some debug adapters may rely on the order - this was the case before + // the configurationDone request was introduced. + await this.sendExceptionBreakpoints(session); + } } private async sendBreakpoints(modelUri: uri, sourceModified = false, session?: IDebugSession): Promise { diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 2dd3ebf0241..57597ac5458 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -172,10 +172,19 @@ export class DebugSession implements IDebugSession { return this._options.compoundRoot; } - get isSimpleUI(): boolean { - return this._options.debugUI?.simple ?? false; + get suppressDebugStatusbar(): boolean { + return this._options.suppressDebugStatusbar ?? false; } + get suppressDebugToolbar(): boolean { + return this._options.suppressDebugToolbar ?? false; + } + + get suppressDebugView(): boolean { + return this._options.suppressDebugView ?? false; + } + + get autoExpandLazyVariables(): boolean { // This tiny helper avoids converting the entire debug model to use service injection return this.configurationService.getValue('debug').autoExpandLazyVariables; @@ -971,7 +980,7 @@ export class DebugSession implements IDebugSession { } if (thread.stoppedDetails) { - if (thread.stoppedDetails.reason === 'breakpoint' && this.configurationService.getValue('debug').openDebug === 'openOnDebugBreak' && !this.isSimpleUI) { + if (thread.stoppedDetails.reason === 'breakpoint' && this.configurationService.getValue('debug').openDebug === 'openOnDebugBreak' && !this.suppressDebugView) { await this.paneCompositeService.openPaneComposite(VIEWLET_ID, ViewContainerLocation.Sidebar); } diff --git a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts index 8836e317cc9..68a459f09c6 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts @@ -99,7 +99,13 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { this.updateScheduler = this._register(new RunOnceScheduler(() => { const state = this.debugService.state; const toolBarLocation = this.configurationService.getValue('debug').toolBarLocation; - if (state === State.Inactive || toolBarLocation === 'docked' || toolBarLocation === 'hidden' || this.debugService.getViewModel().focusedSession?.isSimpleUI || (state === State.Initializing && this.debugService.initializingOptions?.debugUI?.simple)) { + if ( + state === State.Inactive || + toolBarLocation === 'docked' || + toolBarLocation === 'hidden' || + this.debugService.getModel().getSessions().every(s => s.suppressDebugToolbar) || + (state === State.Initializing && this.debugService.initializingOptions?.suppressDebugToolbar) + ) { return this.hide(); } diff --git a/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts b/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts index a3fda1a7045..435d3fac617 100644 --- a/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts +++ b/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts @@ -9,7 +9,7 @@ import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IExtensionHostDebugService, IOpenExtensionWindowResult } from 'vs/platform/debug/common/extensionHostDebug'; import { ExtensionHostDebugBroadcastChannel, ExtensionHostDebugChannelClient } from 'vs/platform/debug/common/extensionHostDebugIpc'; import { IFileService } from 'vs/platform/files/common/files'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { isFolderToOpen, isWorkspaceToOpen } from 'vs/platform/window/common/window'; @@ -172,4 +172,4 @@ class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient i } } -registerSingleton(IExtensionHostDebugService, BrowserExtensionHostDebugService, true); +registerSingleton(IExtensionHostDebugService, BrowserExtensionHostDebugService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/debug/browser/media/debugHover.css b/src/vs/workbench/contrib/debug/browser/media/debugHover.css index c523256de87..3a862adbe9f 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debugHover.css +++ b/src/vs/workbench/contrib/debug/browser/media/debugHover.css @@ -5,7 +5,6 @@ .monaco-editor .debug-hover-widget { position: absolute; - margin-top: -1px; z-index: 50; animation-duration: 0.15s; animation-name: fadeIn; diff --git a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css index 3af216decd0..c9d7cb9eea3 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css +++ b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css @@ -41,6 +41,12 @@ flex-shrink: 0; } +.monaco-workbench .part > .title > .title-actions .start-debug-action-item .codicon-debug-start { + width: 18px; + height: 21px; + padding-left: 2px; +} + .monaco-workbench .monaco-action-bar .start-debug-action-item .configuration .monaco-select-box { border: none; margin-top: 0px; diff --git a/src/vs/workbench/contrib/debug/browser/media/repl.css b/src/vs/workbench/contrib/debug/browser/media/repl.css index 6803c044720..b8ec701f315 100644 --- a/src/vs/workbench/contrib/debug/browser/media/repl.css +++ b/src/vs/workbench/contrib/debug/browser/media/repl.css @@ -142,53 +142,3 @@ } .monaco-workbench .repl .repl-tree .output.expression .code-subscript { vertical-align: sub; font-size: smaller; line-height: normal; } .monaco-workbench .repl .repl-tree .output.expression .code-superscript { vertical-align: super; font-size: smaller; line-height: normal; } - -.monaco-action-bar .action-item.repl-panel-filter-container { - cursor: default; - display: flex; -} - -.monaco-action-bar .panel-action-tree-filter{ - display: flex; - align-items: center; - flex: 1; -} - -.monaco-action-bar .panel-action-tree-filter .monaco-inputbox { - height: 24px; - font-size: 12px; - flex: 1; -} - -.pane-header .monaco-action-bar .panel-action-tree-filter .monaco-inputbox { - height: 20px; - line-height: 18px; -} - -.monaco-workbench.vs .monaco-action-bar .panel-action-tree-filter .monaco-inputbox { - height: 25px; -} - -.panel > .title .monaco-action-bar .action-item.repl-panel-filter-container { - min-width: 300px; - margin-right: 10px; -} - -.repl-panel-filter-container .repl-panel-filter-controls { - position: absolute; - top: 0px; - bottom: 0; - right: 0px; - display: flex; - align-items: center; -} - -.repl-panel-filter-container .repl-panel-filter-controls > .repl-panel-filter-badge { - margin: 4px; - padding: 0px 8px; - border-radius: 2px; -} - -.repl-panel-filter-container .repl-panel-filter-controls > .repl-panel-filter-badge.hidden { - display: none; -} diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index dfcdd381ef0..a1e6de4c6b7 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -44,7 +44,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { ContextKeyExpr, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { registerAndCreateHistoryNavigationContext } from 'vs/platform/history/browser/contextScopedHistoryWidget'; -import { showHistoryKeybindingHint } from 'vs/platform/history/browser/historyWidgetKeybindingHint'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -55,13 +54,13 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { editorForeground, resolveColorValue } from 'vs/platform/theme/common/colorRegistry'; import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; -import { IViewPaneOptions, ViewAction, ViewPane } from 'vs/workbench/browser/parts/views/viewPane'; +import { FilterViewPane, IViewPaneOptions, ViewAction } from 'vs/workbench/browser/parts/views/viewPane'; import { IViewDescriptorService, IViewsService } from 'vs/workbench/common/views'; import { getSimpleCodeEditorWidgetOptions, getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions'; import { FocusSessionActionViewItem } from 'vs/workbench/contrib/debug/browser/debugActionViewItems'; import { debugConsoleClearAll, debugConsoleEvaluationPrompt } from 'vs/workbench/contrib/debug/browser/debugIcons'; import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector'; -import { ReplFilter, ReplFilterActionViewItem, ReplFilterState } from 'vs/workbench/contrib/debug/browser/replFilter'; +import { ReplFilter } from 'vs/workbench/contrib/debug/browser/replFilter'; import { ReplAccessibilityProvider, ReplDataSource, ReplDelegate, ReplEvaluationInputsRenderer, ReplEvaluationResultsRenderer, ReplGroupRenderer, ReplRawObjectsRenderer, ReplSimpleElementsRenderer, ReplVariablesRenderer } from 'vs/workbench/contrib/debug/browser/replViewer'; import { CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_REPL, CONTEXT_MULTI_SESSION_REPL, DEBUG_SCHEME, getStateLabel, IDebugConfiguration, IDebugService, IDebugSession, IReplConfiguration, IReplElement, IReplOptions, REPL_VIEW_ID, State } from 'vs/workbench/contrib/debug/common/debug'; import { Variable } from 'vs/workbench/contrib/debug/common/debugModel'; @@ -74,7 +73,6 @@ const HISTORY_STORAGE_KEY = 'debug.repl.history'; const FILTER_HISTORY_STORAGE_KEY = 'debug.repl.filterHistory'; const FILTER_VALUE_STORAGE_KEY = 'debug.repl.filterValue'; const DECORATION_KEY = 'replinputdecoration'; -const FILTER_ACTION_ID = `workbench.actions.treeView.repl.filter`; function revealLastElement(tree: WorkbenchAsyncDataTree) { tree.scrollTop = tree.scrollHeight - tree.renderHeight; @@ -84,7 +82,7 @@ function revealLastElement(tree: WorkbenchAsyncDataTree) { const sessionsToIgnore = new Set(); const identityProvider = { getId: (element: IReplElement) => element.getId() }; -export class Repl extends ViewPane implements IHistoryNavigationWidget { +export class Repl extends FilterViewPane implements IHistoryNavigationWidget { declare readonly _serviceBrand: undefined; private static readonly REFRESH_DELAY = 50; // delay in ms to refresh the repl for new elements to show @@ -99,7 +97,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { private treeContainer!: HTMLElement; private replInput!: CodeEditorWidget; private replInputContainer!: HTMLElement; - private dimension!: dom.Dimension; + private bodyContentDimension: dom.Dimension | undefined; private replInputLineCount = 1; private model: ITextModel | undefined; private setHistoryNavigationEnablement!: (enabled: boolean) => void; @@ -109,8 +107,6 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { private completionItemProvider: IDisposable | undefined; private modelChangeListener: IDisposable = Disposable.None; private filter: ReplFilter; - private filterState: ReplFilterState; - private filterActionViewItem: ReplFilterActionViewItem | undefined; private multiSessionRepl: IContextKey; private menu: IMenu; @@ -134,14 +130,21 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { @IMenuService menuService: IMenuService, @ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService, ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); + const filterText = storageService.get(FILTER_VALUE_STORAGE_KEY, StorageScope.WORKSPACE, ''); + super({ + ...options, + filterOptions: { + placeholder: localize({ key: 'workbench.debug.filter.placeholder', comment: ['Text in the brackets after e.g. is not localizable'] }, "Filter (e.g. text, !exclude)"), + text: filterText, + history: JSON.parse(storageService.get(FILTER_HISTORY_STORAGE_KEY, StorageScope.WORKSPACE, '[]')) as string[], + } + }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); this.menu = menuService.createMenu(MenuId.DebugConsoleContext, contextKeyService); this._register(this.menu); this.history = new HistoryNavigator(JSON.parse(this.storageService.get(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE, '[]')), 50); this.filter = new ReplFilter(); - this.filterState = new ReplFilterState(this); - this.filter.filterQuery = this.filterState.filterText = this.storageService.get(FILTER_VALUE_STORAGE_KEY, StorageScope.WORKSPACE, ''); + this.filter.filterQuery = filterText; this.multiSessionRepl = CONTEXT_MULTI_SESSION_REPL.bindTo(contextKeyService); this.replOptions = this._register(this.instantiationService.createInstance(ReplOptions, this.id, () => this.getBackgroundColor())); this._register(this.replOptions.onDidChange(() => this.onDidStyleChange())); @@ -186,7 +189,6 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { this.replInput.setModel(this.model); this.updateInputDecoration(); this.refreshReplElements(true); - this.layoutBody(this.dimension.height, this.dimension.width); } })); this._register(this.configurationService.onDidChangeConfiguration(e => { @@ -208,8 +210,8 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { this.setMode(); })); - this._register(this.filterState.onDidChange(() => { - this.filter.filterQuery = this.filterState.filterText; + this._register(this.filterWidget.onDidChangeFilterText(() => { + this.filter.filterQuery = this.filterWidget.getFilterText(); this.tree.refilter(); revealLastElement(this.tree); })); @@ -318,7 +320,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { } focusFilter(): void { - this.filterActionViewItem?.focus(); + this.filterWidget.focus(); } private setMode(): void { @@ -364,8 +366,8 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { this.tree.rerender(); - if (this.dimension) { - this.layoutBody(this.dimension.height, this.dimension.width); + if (this.bodyContentDimension) { + this.layoutBodyContent(this.bodyContentDimension.height, this.bodyContentDimension.width); } } } @@ -431,9 +433,9 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { this.replInput.setValue(''); const shouldRelayout = this.replInputLineCount > 1; this.replInputLineCount = 1; - if (shouldRelayout) { + if (shouldRelayout && this.bodyContentDimension) { // Trigger a layout to shrink a potential multi line input - this.layoutBody(this.dimension.height, this.dimension.width); + this.layoutBodyContent(this.bodyContentDimension.height, this.bodyContentDimension.width); } } } @@ -458,9 +460,8 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { return removeAnsiEscapeCodes(text); } - protected override layoutBody(height: number, width: number): void { - super.layoutBody(height, width); - this.dimension = new dom.Dimension(width, height); + protected override layoutBodyContent(height: number, width: number): void { + this.bodyContentDimension = new dom.Dimension(width, height); const replInputHeight = Math.min(this.replInput.getContentHeight(), height); if (this.tree) { const lastElementVisible = this.tree.scrollTop + this.tree.renderHeight >= this.tree.scrollHeight; @@ -476,6 +477,10 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { this.replInput.layout({ width: width - 30, height: replInputHeight }); } + override shouldShowFilterInHeader(): boolean { + return true; + } + collapseAll(): void { this.tree.collapseAll(); } @@ -492,11 +497,6 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { if (action.id === selectReplCommandId) { const session = (this.tree ? this.tree.getInput() : undefined) ?? this.debugService.getViewModel().focusedSession; return this.instantiationService.createInstance(SelectReplActionViewItem, action, session); - } else if (action.id === FILTER_ACTION_ID) { - const filterHistory = JSON.parse(this.storageService.get(FILTER_HISTORY_STORAGE_KEY, StorageScope.WORKSPACE, '[]')) as string[]; - this.filterActionViewItem = this.instantiationService.createInstance(ReplFilterActionViewItem, action, - localize({ key: 'workbench.debug.filter.placeholder', comment: ['Text in the brackets after e.g. is not localizable'] }, "Filter (e.g. text, !exclude)"), this.filterState, filterHistory, () => showHistoryKeybindingHint(this.keybindingService)); - return this.filterActionViewItem; } return super.getActionViewItem(action); @@ -538,7 +538,8 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { await autoExpandElements(session.getReplElements()); } // Repl elements count changed, need to update filter stats on the badge - this.filterState.updateFilterStats(); + const { total, filtered } = this.getFilterStats(); + this.filterWidget.updateBadge(total === filtered || total === 0 ? undefined : localize('showing filtered repl lines', "Showing {0} of {1}", filtered, total)); }, Repl.REFRESH_DELAY); } @@ -645,7 +646,9 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { const lineCount = model ? Math.min(10, model.getLineCount()) : 1; if (lineCount !== this.replInputLineCount) { this.replInputLineCount = lineCount; - this.layoutBody(this.dimension.height, this.dimension.width); + if (this.bodyContentDimension) { + this.layoutBodyContent(this.bodyContentDimension.height, this.bodyContentDimension.width); + } } })); // We add the input decoration only when the focus is in the input #61126 @@ -712,19 +715,17 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { } else { this.storageService.remove(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE); } - if (this.filterActionViewItem) { - const filterHistory = this.filterActionViewItem.getHistory(); - if (filterHistory.length) { - this.storageService.store(FILTER_HISTORY_STORAGE_KEY, JSON.stringify(filterHistory), StorageScope.WORKSPACE, StorageTarget.USER); - } else { - this.storageService.remove(FILTER_HISTORY_STORAGE_KEY, StorageScope.WORKSPACE); - } - const filterValue = this.filterState.filterText; - if (filterValue) { - this.storageService.store(FILTER_VALUE_STORAGE_KEY, filterValue, StorageScope.WORKSPACE, StorageTarget.USER); - } else { - this.storageService.remove(FILTER_VALUE_STORAGE_KEY, StorageScope.WORKSPACE); - } + const filterHistory = this.filterWidget.getHistory(); + if (filterHistory.length) { + this.storageService.store(FILTER_HISTORY_STORAGE_KEY, JSON.stringify(filterHistory), StorageScope.WORKSPACE, StorageTarget.USER); + } else { + this.storageService.remove(FILTER_HISTORY_STORAGE_KEY, StorageScope.WORKSPACE); + } + const filterValue = this.filterWidget.getFilterText(); + if (filterValue) { + this.storageService.store(FILTER_VALUE_STORAGE_KEY, filterValue, StorageScope.WORKSPACE, StorageTarget.USER); + } else { + this.storageService.remove(FILTER_VALUE_STORAGE_KEY, StorageScope.WORKSPACE); } super.saveState(); @@ -876,26 +877,6 @@ function getReplView(viewsService: IViewsService): Repl | undefined { return viewsService.getActiveViewWithId(REPL_VIEW_ID) as Repl ?? undefined; } -registerAction2(class extends Action2 { - constructor() { - super({ - id: FILTER_ACTION_ID, - title: localize('filter', "Filter"), - f1: false, - menu: { - id: MenuId.ViewTitle, - group: 'navigation', - when: ContextKeyExpr.equals('view', REPL_VIEW_ID), - order: 10 - } - }); - } - - run(_accessor: ServicesAccessor) { - // noop this action is just a placeholder for the filter action view item - } -}); - const selectReplCommandId = 'workbench.action.debug.selectRepl'; registerAction2(class extends ViewAction { constructor() { diff --git a/src/vs/workbench/contrib/debug/browser/replFilter.ts b/src/vs/workbench/contrib/debug/browser/replFilter.ts index 368979c2d24..ad5370e9fdc 100644 --- a/src/vs/workbench/contrib/debug/browser/replFilter.ts +++ b/src/vs/workbench/contrib/debug/browser/replFilter.ts @@ -7,23 +7,7 @@ import { matchesFuzzy } from 'vs/base/common/filters'; import { splitGlobAware } from 'vs/base/common/glob'; import { ITreeFilter, TreeVisibility, TreeFilterResult } from 'vs/base/browser/ui/tree/tree'; import { IReplElement } from 'vs/workbench/contrib/debug/common/debug'; -import * as DOM from 'vs/base/browser/dom'; -import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; -import { Delayer } from 'vs/base/common/async'; -import { IAction } from 'vs/base/common/actions'; -import { HistoryInputBox } from 'vs/base/browser/ui/inputbox/inputBox'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { toDisposable } from 'vs/base/common/lifecycle'; -import { Event, Emitter } from 'vs/base/common/event'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { KeyCode } from 'vs/base/common/keyCodes'; -import { ContextScopedHistoryInputBox } from 'vs/platform/history/browser/contextScopedHistoryWidget'; -import { attachInputBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { badgeBackground, badgeForeground, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { ReplEvaluationResult, ReplEvaluationInput } from 'vs/workbench/contrib/debug/common/replModel'; -import { localize } from 'vs/nls'; import { Variable } from 'vs/workbench/contrib/debug/common/debugModel'; @@ -79,184 +63,3 @@ export class ReplFilter implements ITreeFilter { return includeQueryPresent ? includeQueryMatched : (typeof parentVisibility !== 'undefined' ? parentVisibility : TreeVisibility.Visible); } } - -export interface IFilterStatsProvider { - getFilterStats(): { total: number; filtered: number }; -} - -export class ReplFilterState { - - constructor(private filterStatsProvider: IFilterStatsProvider) { } - - private readonly _onDidChange: Emitter = new Emitter(); - get onDidChange(): Event { - return this._onDidChange.event; - } - - private readonly _onDidStatsChange: Emitter = new Emitter(); - get onDidStatsChange(): Event { - return this._onDidStatsChange.event; - } - - private _filterText = ''; - private _stats = { total: 0, filtered: 0 }; - - get filterText(): string { - return this._filterText; - } - - get filterStats(): { total: number; filtered: number } { - return this._stats; - } - - set filterText(filterText: string) { - if (this._filterText !== filterText) { - this._filterText = filterText; - this._onDidChange.fire(); - this.updateFilterStats(); - } - } - - updateFilterStats(): void { - const { total, filtered } = this.filterStatsProvider.getFilterStats(); - if (this._stats.total !== total || this._stats.filtered !== filtered) { - this._stats = { total, filtered }; - this._onDidStatsChange.fire(); - } - } -} - -export class ReplFilterActionViewItem extends BaseActionViewItem { - - private delayedFilterUpdate: Delayer; - private container!: HTMLElement; - private filterBadge!: HTMLElement; - private filterInputBox!: HistoryInputBox; - - constructor( - action: IAction, - private placeholder: string, - private filters: ReplFilterState, - private history: string[], - private showHistoryHint: () => boolean, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IThemeService private readonly themeService: IThemeService, - @IContextViewService private readonly contextViewService: IContextViewService) { - super(null, action); - this.delayedFilterUpdate = new Delayer(400); - this._register(toDisposable(() => this.delayedFilterUpdate.cancel())); - } - - override render(container: HTMLElement): void { - this.container = container; - this.container.classList.add('repl-panel-filter-container'); - - this.element = DOM.append(this.container, DOM.$('')); - this.element.className = this.class; - this.createInput(this.element); - this.createBadge(this.element); - this.updateClass(); - } - - override focus(): void { - this.filterInputBox?.focus(); - } - - override blur(): void { - this.filterInputBox?.blur(); - } - - override setFocusable(): void { - // noop input elements are focusable by default - } - - getHistory(): string[] { - return this.filterInputBox.getHistory(); - } - - override get trapsArrowNavigation(): boolean { - return true; - } - - private clearFilterText(): void { - this.filterInputBox.value = ''; - } - - private createInput(container: HTMLElement): void { - this.filterInputBox = this._register(this.instantiationService.createInstance(ContextScopedHistoryInputBox, container, this.contextViewService, { - placeholder: this.placeholder, - history: this.history, - showHistoryHint: this.showHistoryHint - })); - this._register(attachInputBoxStyler(this.filterInputBox, this.themeService)); - this.filterInputBox.value = this.filters.filterText; - - this._register(this.filterInputBox.onDidChange(() => this.delayedFilterUpdate.trigger(() => this.onDidInputChange(this.filterInputBox!)))); - this._register(this.filters.onDidChange(() => { - this.filterInputBox.value = this.filters.filterText; - })); - this._register(DOM.addStandardDisposableListener(this.filterInputBox.inputElement, DOM.EventType.KEY_DOWN, (e: any) => this.onInputKeyDown(e))); - this._register(DOM.addStandardDisposableListener(container, DOM.EventType.KEY_DOWN, this.handleKeyboardEvent)); - this._register(DOM.addStandardDisposableListener(container, DOM.EventType.KEY_UP, this.handleKeyboardEvent)); - this._register(DOM.addStandardDisposableListener(this.filterInputBox.inputElement, DOM.EventType.CLICK, (e) => { - e.stopPropagation(); - e.preventDefault(); - })); - } - - private onDidInputChange(inputbox: HistoryInputBox) { - inputbox.addToHistory(); - this.filters.filterText = inputbox.value; - } - - // Action toolbar is swallowing some keys for action items which should not be for an input box - private handleKeyboardEvent(event: StandardKeyboardEvent) { - if (event.equals(KeyCode.Space) - || event.equals(KeyCode.LeftArrow) - || event.equals(KeyCode.RightArrow) - || event.equals(KeyCode.Escape) - ) { - event.stopPropagation(); - } - } - - private onInputKeyDown(event: StandardKeyboardEvent) { - if (event.equals(KeyCode.Escape)) { - this.clearFilterText(); - event.stopPropagation(); - event.preventDefault(); - } - } - - private createBadge(container: HTMLElement): void { - const controlsContainer = DOM.append(container, DOM.$('.repl-panel-filter-controls')); - const filterBadge = this.filterBadge = DOM.append(controlsContainer, DOM.$('.repl-panel-filter-badge')); - this._register(attachStylerCallback(this.themeService, { badgeBackground, badgeForeground, contrastBorder }, colors => { - const background = colors.badgeBackground ? colors.badgeBackground.toString() : ''; - const foreground = colors.badgeForeground ? colors.badgeForeground.toString() : ''; - const border = colors.contrastBorder ? colors.contrastBorder.toString() : ''; - - filterBadge.style.backgroundColor = background; - - filterBadge.style.borderWidth = border ? '1px' : ''; - filterBadge.style.borderStyle = border ? 'solid' : ''; - filterBadge.style.borderColor = border; - filterBadge.style.color = foreground; - })); - this.updateBadge(); - this._register(this.filters.onDidStatsChange(() => this.updateBadge())); - } - - private updateBadge(): void { - const { total, filtered } = this.filters.filterStats; - const filterBadgeHidden = total === filtered || total === 0; - - this.filterBadge.classList.toggle('hidden', filterBadgeHidden); - this.filterBadge.textContent = localize('showing filtered repl lines', "Showing {0} of {1}", filtered, total); - this.filterInputBox.inputElement.style.paddingRight = filterBadgeHidden ? '4px' : '150px'; - } - - protected get class(): string { - return 'panel-action-tree-filter'; - } -} diff --git a/src/vs/workbench/contrib/debug/browser/replViewer.ts b/src/vs/workbench/contrib/debug/browser/replViewer.ts index 94b7ecd5b13..9c4adf953f3 100644 --- a/src/vs/workbench/contrib/debug/browser/replViewer.ts +++ b/src/vs/workbench/contrib/debug/browser/replViewer.ts @@ -312,18 +312,21 @@ export class ReplDelegate extends CachedListVirtualDelegate { return super.getHeight(element); } + /** + * With wordWrap enabled, this is an estimate. With wordWrap disabled, this is the real height that the list will use. + */ protected estimateHeight(element: IReplElement, ignoreValueLength = false): number { const lineHeight = this.replOptions.replConfiguration.lineHeight; - const countNumberOfLines = (str: string) => Math.max(1, (str && str.match(/\r\n|\n/g) || []).length); + const countNumberOfLines = (str: string) => str.match(/\n/g)?.length ?? 0; const hasValue = (e: any): e is { value: string } => typeof e.value === 'string'; - // Calculate a rough overestimation for the height - // For every 70 characters increase the number of lines needed beyond the first if (hasValue(element) && !isNestedVariable(element)) { const value = element.value; - const valueRows = countNumberOfLines(value) + (ignoreValueLength ? 0 : Math.floor(value.length / 70)); + const valueRows = countNumberOfLines(value) + + (ignoreValueLength ? 0 : Math.floor(value.length / 70)) // Make an estimate for wrapping + + (element instanceof SimpleReplElement ? 0 : 1); // A SimpleReplElement ends in \n if it's a complete line - return valueRows * lineHeight; + return Math.max(valueRows, 1) * lineHeight; } return lineHeight; diff --git a/src/vs/workbench/contrib/debug/browser/statusbarColorProvider.ts b/src/vs/workbench/contrib/debug/browser/statusbarColorProvider.ts index 5ec8c29bae1..0932492fc82 100644 --- a/src/vs/workbench/contrib/debug/browser/statusbarColorProvider.ts +++ b/src/vs/workbench/contrib/debug/browser/statusbarColorProvider.ts @@ -69,7 +69,7 @@ export class StatusBarColorProvider implements IWorkbenchContribution { } protected update(): void { - this.enabled = isStatusbarInDebugMode(this.debugService.state, this.debugService.getViewModel().focusedSession); + this.enabled = isStatusbarInDebugMode(this.debugService.state, this.debugService.getModel().getSessions()); } dispose(): void { @@ -78,12 +78,8 @@ export class StatusBarColorProvider implements IWorkbenchContribution { } } -export function isStatusbarInDebugMode(state: State, session: IDebugSession | undefined): boolean { - if (state === State.Inactive || state === State.Initializing || session?.isSimpleUI) { - return false; - } - const isRunningWithoutDebug = session?.configuration?.noDebug; - if (isRunningWithoutDebug) { +export function isStatusbarInDebugMode(state: State, sessions: IDebugSession[]): boolean { + if (state === State.Inactive || state === State.Initializing || sessions.every(s => s.suppressDebugStatusbar || s.configuration?.noDebug)) { return false; } diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 11c3f051a43..3480cdecf3f 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -13,7 +13,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import severity from 'vs/base/common/severity'; import { URI as uri } from 'vs/base/common/uri'; import { IPosition, Position } from 'vs/editor/common/core/position'; -import { IRange, Range } from 'vs/editor/common/core/range'; +import { IRange } from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { ITextModel as EditorIModel } from 'vs/editor/common/model'; import * as nls from 'vs/nls'; @@ -73,7 +73,7 @@ export const CONTEXT_JUMP_TO_CURSOR_SUPPORTED = new RawContextKey('jump export const CONTEXT_STEP_INTO_TARGETS_SUPPORTED = new RawContextKey('stepIntoTargetsSupported', false, { type: 'boolean', description: nls.localize('stepIntoTargetsSupported', "True when the focused session supports 'stepIntoTargets' request.") }); export const CONTEXT_BREAKPOINTS_EXIST = new RawContextKey('breakpointsExist', false, { type: 'boolean', description: nls.localize('breakpointsExist', "True when at least one breakpoint exists.") }); export const CONTEXT_DEBUGGERS_AVAILABLE = new RawContextKey('debuggersAvailable', false, { type: 'boolean', description: nls.localize('debuggersAvailable', "True when there is at least one debug extensions active.") }); -export const CONTEXT_DEBUG_EXTENSION_AVAILABLE = new RawContextKey('debugExtensionAvailable', false, { type: 'boolean', description: nls.localize('debugExtensionsAvailable', "True when there is at least one debug extension installed and enabled.") }); +export const CONTEXT_DEBUG_EXTENSION_AVAILABLE = new RawContextKey('debugExtensionAvailable', true, { type: 'boolean', description: nls.localize('debugExtensionsAvailable', "True when there is at least one debug extension installed and enabled.") }); export const CONTEXT_DEBUG_PROTOCOL_VARIABLE_MENU_CONTEXT = new RawContextKey('debugProtocolVariableMenuContext', undefined, { type: 'string', description: nls.localize('debugProtocolVariableMenuContext', "Represents the context the debug adapter sets on the focused variable in the VARIABLES view.") }); export const CONTEXT_SET_VARIABLE_SUPPORTED = new RawContextKey('debugSetVariableSupported', false, { type: 'boolean', description: nls.localize('debugSetVariableSupported', "True when the focused session supports 'setVariable' request.") }); export const CONTEXT_SET_EXPRESSION_SUPPORTED = new RawContextKey('debugSetExpressionSupported', false, { type: 'boolean', description: nls.localize('debugSetExpressionSupported', "True when the focused session supports 'setExpression' request.") }); @@ -203,11 +203,11 @@ export interface IDebugSessionOptions { repl?: IDebugSessionReplMode; compoundRoot?: DebugCompoundRoot; compact?: boolean; - debugUI?: { - simple?: boolean; - }; startedByUser?: boolean; saveBeforeRestart?: boolean; + suppressDebugToolbar?: boolean; + suppressDebugStatusbar?: boolean; + suppressDebugView?: boolean; } export interface IDataBreakpointInfoResponse { @@ -300,8 +300,10 @@ export interface IDebugSession extends ITreeElement { readonly compoundRoot: DebugCompoundRoot | undefined; readonly saveBeforeRestart: boolean; readonly name: string; - readonly isSimpleUI: boolean; readonly autoExpandLazyVariables: boolean; + readonly suppressDebugToolbar: boolean; + readonly suppressDebugStatusbar: boolean; + readonly suppressDebugView: boolean; setSubId(subId: string | undefined): void; @@ -1133,7 +1135,7 @@ export const enum BreakpointWidgetContext { } export interface IDebugEditorContribution extends editorCommon.IEditorContribution { - showHover(range: Range, focus: boolean): Promise; + showHover(range: Position, focus: boolean): Promise; addLaunchConfiguration(): Promise; closeExceptionWidget(): void; } diff --git a/src/vs/workbench/contrib/debug/common/debugContentProvider.ts b/src/vs/workbench/contrib/debug/common/debugContentProvider.ts index e7c4d271d2e..62bf8a6c423 100644 --- a/src/vs/workbench/contrib/debug/common/debugContentProvider.ts +++ b/src/vs/workbench/contrib/debug/common/debugContentProvider.ts @@ -18,6 +18,7 @@ import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Range } from 'vs/editor/common/core/range'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry'; +import { ErrorNoTelemetry } from 'vs/base/common/errors'; /** * Debug URI format @@ -89,7 +90,7 @@ export class DebugContentProvider implements IWorkbenchContribution, ITextModelC } if (!session) { - return Promise.reject(new Error(localize('unable', "Unable to resolve the resource without a debug session"))); + return Promise.reject(new ErrorNoTelemetry(localize('unable', "Unable to resolve the resource without a debug session"))); } const createErrModel = (errMsg?: string) => { this.debugService.sourceIsNotAvailable(resource); diff --git a/src/vs/workbench/contrib/debug/node/debugAdapter.ts b/src/vs/workbench/contrib/debug/node/debugAdapter.ts index b4f6c7e4daf..4275f9e0a89 100644 --- a/src/vs/workbench/contrib/debug/node/debugAdapter.ts +++ b/src/vs/workbench/contrib/debug/node/debugAdapter.ts @@ -3,19 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Promises } from 'vs/base/node/pfs'; import * as cp from 'child_process'; -import * as stream from 'stream'; -import * as nls from 'vs/nls'; import * as net from 'net'; -import * as path from 'vs/base/common/path'; -import * as strings from 'vs/base/common/strings'; +import * as stream from 'stream'; import * as objects from 'vs/base/common/objects'; +import * as path from 'vs/base/common/path'; import * as platform from 'vs/base/common/platform'; -import { ExtensionsChannelId } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IOutputService } from 'vs/workbench/services/output/common/output'; -import { IDebugAdapterExecutable, IDebuggerContribution, IPlatformSpecificAdapterContribution, IDebugAdapterServer, IDebugAdapterNamedPipeServer } from 'vs/workbench/contrib/debug/common/debug'; +import * as strings from 'vs/base/common/strings'; +import { Promises } from 'vs/base/node/pfs'; +import * as nls from 'vs/nls'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { IDebugAdapterExecutable, IDebugAdapterNamedPipeServer, IDebugAdapterServer, IDebuggerContribution, IPlatformSpecificAdapterContribution } from 'vs/workbench/contrib/debug/common/debug'; import { AbstractDebugAdapter } from '../common/abstractDebugAdapter'; /** @@ -169,7 +167,7 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter { private serverProcess: cp.ChildProcess | undefined; - constructor(private adapterExecutable: IDebugAdapterExecutable, private debugType: string, private readonly outputService?: IOutputService) { + constructor(private adapterExecutable: IDebugAdapterExecutable, private debugType: string) { super(); } @@ -251,19 +249,7 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter { this._onError.fire(error); }); - const outputService = this.outputService; - if (outputService) { - const sanitize = (s: string) => s.toString().replace(/\r?\n$/mg, ''); - // this.serverProcess.stdout.on('data', (data: string) => { - // console.log('%c' + sanitize(data), 'background: #ddd; font-style: italic;'); - // }); - this.serverProcess.stderr!.on('data', (data: string) => { - const channel = outputService.getChannel(ExtensionsChannelId); - channel?.append(sanitize(data)); - }); - } else { - this.serverProcess.stderr!.resume(); - } + this.serverProcess.stderr!.resume(); // finally connect to the DA this.connect(this.serverProcess.stdout!, this.serverProcess.stdin!); diff --git a/src/vs/workbench/contrib/debug/test/browser/baseDebugView.test.ts b/src/vs/workbench/contrib/debug/test/browser/baseDebugView.test.ts index ab592668f64..a6169299daa 100644 --- a/src/vs/workbench/contrib/debug/test/browser/baseDebugView.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/baseDebugView.test.ts @@ -133,11 +133,17 @@ suite('Debug - Base Debug View', () => { test('statusbar in debug mode', () => { const model = createMockDebugModel(); const session = createMockSession(model); - assert.strictEqual(isStatusbarInDebugMode(State.Inactive, undefined), false); - assert.strictEqual(isStatusbarInDebugMode(State.Initializing, session), false); - assert.strictEqual(isStatusbarInDebugMode(State.Running, session), true); - assert.strictEqual(isStatusbarInDebugMode(State.Stopped, session), true); + const session2 = createMockSession(model, undefined, { suppressDebugStatusbar: true }); + assert.strictEqual(isStatusbarInDebugMode(State.Inactive, []), false); + assert.strictEqual(isStatusbarInDebugMode(State.Initializing, [session]), false); + assert.strictEqual(isStatusbarInDebugMode(State.Running, [session]), true); + assert.strictEqual(isStatusbarInDebugMode(State.Stopped, [session]), true); + + assert.strictEqual(isStatusbarInDebugMode(State.Running, [session2]), false); + assert.strictEqual(isStatusbarInDebugMode(State.Running, [session, session2]), true); + session.configuration.noDebug = true; - assert.strictEqual(isStatusbarInDebugMode(State.Running, session), false); + assert.strictEqual(isStatusbarInDebugMode(State.Running, [session]), false); + assert.strictEqual(isStatusbarInDebugMode(State.Running, [session, session2]), false); }); }); diff --git a/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts b/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts index 44eff810343..13df888a098 100644 --- a/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts +++ b/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts @@ -168,6 +168,9 @@ export class MockDebugService implements IDebugService { } export class MockSession implements IDebugSession { + readonly suppressDebugToolbar = false; + readonly suppressDebugStatusbar = false; + readonly suppressDebugView = false; readonly autoExpandLazyVariables = false; getMemory(memoryReference: string): IMemoryRegion { diff --git a/src/vs/workbench/contrib/deprecatedExtensionMigrator/browser/deprecatedExtensionMigrator.contribution.ts b/src/vs/workbench/contrib/deprecatedExtensionMigrator/browser/deprecatedExtensionMigrator.contribution.ts index 2a91d82f0d8..3abb2f55315 100644 --- a/src/vs/workbench/contrib/deprecatedExtensionMigrator/browser/deprecatedExtensionMigrator.contribution.ts +++ b/src/vs/workbench/contrib/deprecatedExtensionMigrator/browser/deprecatedExtensionMigrator.contribution.ts @@ -100,4 +100,4 @@ interface State { }[]; } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DeprecatedExtensionMigratorContribution, 'DeprecatedExtensionMigratorContribution', LifecyclePhase.Restored); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DeprecatedExtensionMigratorContribution, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts b/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts index 238f1612300..af450bcd68e 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts @@ -3,19 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Action2, IAction2Options, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { localize } from 'vs/nls'; -import { IEditSessionsStorageService, Change, ChangeType, Folder, EditSession, FileType, EDIT_SESSION_SYNC_CATEGORY, EDIT_SESSIONS_CONTAINER_ID, EditSessionSchemaVersion, IEditSessionsLogService, EDIT_SESSIONS_VIEW_ICON, EDIT_SESSIONS_TITLE, EDIT_SESSIONS_SHOW_VIEW, EDIT_SESSIONS_SIGNED_IN, EDIT_SESSIONS_DATA_VIEW_ID, decodeEditSessionFileContent } from 'vs/workbench/contrib/editSessions/common/editSessions'; +import { IEditSessionsStorageService, Change, ChangeType, Folder, EditSession, FileType, EDIT_SESSION_SYNC_CATEGORY, EDIT_SESSIONS_CONTAINER_ID, EditSessionSchemaVersion, IEditSessionsLogService, EDIT_SESSIONS_VIEW_ICON, EDIT_SESSIONS_TITLE, EDIT_SESSIONS_SHOW_VIEW, EDIT_SESSIONS_DATA_VIEW_ID, decodeEditSessionFileContent } from 'vs/workbench/contrib/editSessions/common/editSessions'; import { ISCMRepository, ISCMService } from 'vs/workbench/contrib/scm/common/scm'; import { IFileService } from 'vs/platform/files/common/files'; import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { URI } from 'vs/base/common/uri'; -import { joinPath, relativePath } from 'vs/base/common/resources'; +import { basename, joinPath, relativePath } from 'vs/base/common/resources'; import { encodeBase64 } from 'vs/base/common/buffer'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; @@ -23,8 +23,8 @@ import { EditSessionsWorkbenchService } from 'vs/workbench/contrib/editSessions/ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { UserDataSyncErrorCode, UserDataSyncStoreError } from 'vs/platform/userDataSync/common/userDataSync'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IDialogService, IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { getFileNamesMessage, IDialogService, IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IProductService } from 'vs/platform/product/common/productService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -53,24 +53,27 @@ import { IEditSessionIdentityService } from 'vs/platform/workspace/common/editSe import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IOutputService } from 'vs/workbench/services/output/common/output'; import * as Constants from 'vs/workbench/contrib/logs/common/logConstants'; +import { sha1Hex } from 'vs/base/browser/hash'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; registerSingleton(IEditSessionsLogService, EditSessionsLogService, false); registerSingleton(IEditSessionsStorageService, EditSessionsWorkbenchService, false); const continueWorkingOnCommand: IAction2Options = { - id: '_workbench.experimental.editSessions.actions.continueEditSession', + id: '_workbench.editSessions.actions.continueEditSession', title: { value: localize('continue working on', "Continue Working On..."), original: 'Continue Working On...' }, precondition: WorkspaceFolderCountContext.notEqualsTo('0'), f1: true }; const openLocalFolderCommand: IAction2Options = { - id: '_workbench.experimental.editSessions.actions.continueEditSession.openLocalFolder', + id: '_workbench.editSessions.actions.continueEditSession.openLocalFolder', title: { value: localize('continue edit session in local folder', "Open In Local Folder"), original: 'Open In Local Folder' }, category: EDIT_SESSION_SYNC_CATEGORY, precondition: IsWebContext }; const showOutputChannelCommand: IAction2Options = { - id: 'workbench.experimental.editSessions.actions.showOutputChannel', + id: 'workbench.editSessions.actions.showOutputChannel', title: { value: localize('show log', 'Show Log'), original: 'Show Log' }, category: EDIT_SESSION_SYNC_CATEGORY }; @@ -80,16 +83,17 @@ const resumingProgressOptions = { title: `[${localize('resuming edit session window', 'Resuming edit session...')}](command:${showOutputChannelCommand.id})` }; const queryParamName = 'editSessionId'; -const experimentalSettingName = 'workbench.experimental.editSessions.enabled'; -const useEditSessionsWithContinueOn = 'workbench.experimental.editSessions.continueOn'; +const useEditSessionsWithContinueOn = 'workbench.editSessions.continueOn'; export class EditSessionsContribution extends Disposable implements IWorkbenchContribution { - private registered = false; private continueEditSessionOptions: ContinueEditSessionItem[] = []; private readonly shouldShowViewsContext: IContextKey; + private static APPLICATION_LAUNCHED_VIA_CONTINUE_ON_STORAGE_KEY = 'applicationLaunchedViaContinueOn'; + private accountsMenuBadgeDisposable = this._register(new MutableDisposable()); + constructor( @IEditSessionsStorageService private readonly editSessionsStorageService: IEditSessionsStorageService, @IFileService private readonly fileService: IFileService, @@ -110,18 +114,14 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo @ICommandService private commandService: ICommandService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IFileDialogService private readonly fileDialogService: IFileDialogService, - @ILifecycleService private readonly lifecycleService: ILifecycleService + @ILifecycleService private readonly lifecycleService: ILifecycleService, + @IStorageService private readonly storageService: IStorageService, + @IActivityService private readonly activityService: IActivityService, ) { super(); this.autoResumeEditSession(); - this.configurationService.onDidChangeConfiguration((e) => { - if (e.affectsConfiguration(experimentalSettingName)) { - this.registerActions(); - } - }); - this.registerActions(); this.registerViews(); this.registerContributedEditSessionOptions(); @@ -130,6 +130,8 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo this._register(this.fileService.registerProvider(EditSessionsFileSystemProvider.SCHEMA, new EditSessionsFileSystemProvider(this.editSessionsStorageService))); this.lifecycleService.onWillShutdown((e) => e.join(this.autoStoreEditSession(), { id: 'autoStoreEditSession', label: localize('autoStoreEditSession', 'Storing current edit session...') })); + this._register(this.editSessionsStorageService.onDidSignIn(() => this.updateAccountsMenuBadge())); + this._register(this.editSessionsStorageService.onDidSignOut(() => this.updateAccountsMenuBadge())); } private autoResumeEditSession() { @@ -142,23 +144,67 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo }; this.telemetryService.publicLog2('editSessions.continue.resume'); + const shouldAutoResumeOnReload = this.configurationService.getValue('workbench.editSessions.autoResume') === 'onReload'; + if (this.environmentService.editSessionId !== undefined) { + this.logService.info(`Resuming edit session, reason: found editSessionId ${this.environmentService.editSessionId} in environment service...`); await this.resumeEditSession(this.environmentService.editSessionId).finally(() => this.environmentService.editSessionId = undefined); - } else if ( - this.configurationService.getValue('workbench.experimental.editSessions.enabled') === true && - this.configurationService.getValue('workbench.experimental.editSessions.autoResume') === 'onReload' && - this.editSessionsStorageService.isSignedIn - ) { + } else if (shouldAutoResumeOnReload && this.editSessionsStorageService.isSignedIn) { + this.logService.info('Resuming edit session, reason: edit sessions enabled...'); // Attempt to resume edit session based on edit workspace identifier // Note: at this point if the user is not signed into edit sessions, // we don't want them to be prompted to sign in and should just return early await this.resumeEditSession(undefined, true); + } else if (shouldAutoResumeOnReload) { + // The application has previously launched via a protocol URL Continue On flow + const hasApplicationLaunchedFromContinueOnFlow = this.storageService.getBoolean(EditSessionsContribution.APPLICATION_LAUNCHED_VIA_CONTINUE_ON_STORAGE_KEY, StorageScope.APPLICATION, false); + + const handlePendingEditSessions = () => { + // display a badge in the accounts menu but do not prompt the user to sign in again + this.updateAccountsMenuBadge(); + // attempt a resume if we are in a pending state and the user just signed in + const disposable = this.editSessionsStorageService.onDidSignIn(async () => { + disposable.dispose(); + this.resumeEditSession(undefined, true); + this.storageService.remove(EditSessionsContribution.APPLICATION_LAUNCHED_VIA_CONTINUE_ON_STORAGE_KEY, StorageScope.APPLICATION); + this.environmentService.continueOn = undefined; + }); + }; + + if ((this.environmentService.continueOn !== undefined) && + !this.editSessionsStorageService.isSignedIn && + // and user has not yet been prompted to sign in on this machine + hasApplicationLaunchedFromContinueOnFlow === false + ) { + this.storageService.store(EditSessionsContribution.APPLICATION_LAUNCHED_VIA_CONTINUE_ON_STORAGE_KEY, true, StorageScope.APPLICATION, StorageTarget.MACHINE); + await this.editSessionsStorageService.initialize(true); + if (this.editSessionsStorageService.isSignedIn) { + await this.resumeEditSession(undefined, true); + } else { + handlePendingEditSessions(); + } + // store the fact that we prompted the user + } else if (!this.editSessionsStorageService.isSignedIn && + // and user has been prompted to sign in on this machine + hasApplicationLaunchedFromContinueOnFlow === true + ) { + handlePendingEditSessions(); + } } performance.mark('code/didResumeEditSessionFromIdentifier'); }); } + private updateAccountsMenuBadge() { + if (this.editSessionsStorageService.isSignedIn) { + return this.accountsMenuBadgeDisposable.clear(); + } + + const badge = new NumberBadge(1, () => localize('check for pending edit sessions', 'Check for pending edit sessions')); + this.accountsMenuBadgeDisposable.value = this.activityService.showAccountsActivity({ badge, priority: 1 }); + } + private async autoStoreEditSession() { if (this.configurationService.getValue('workbench.experimental.editSessions.autoStore') === 'onShutdown') { await this.progressService.withProgress({ @@ -186,11 +232,6 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo } private registerActions() { - if (this.registered || this.configurationService.getValue(experimentalSettingName) !== true) { - this.logService.info(`Skipping registering edit sessions actions as edit sessions are currently disabled. Set ${experimentalSettingName} to enable edit sessions.`); - return; - } - this.registerContinueEditSessionAction(); this.registerResumeLatestEditSessionAction(); @@ -200,8 +241,6 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo this.registerShowEditSessionViewAction(); this.registerShowEditSessionOutputChannelAction(); - - this.registered = true; } private registerShowEditSessionOutputChannelAction() { @@ -225,8 +264,7 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo id: 'workbench.editSessions.actions.showEditSessions', title: { value: localize('show edit session', "Show Edit Sessions"), original: 'Show Edit Sessions' }, category: EDIT_SESSION_SYNC_CATEGORY, - f1: true, - precondition: EDIT_SESSIONS_SIGNED_IN + f1: true }); } @@ -267,7 +305,7 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo if (ref !== undefined && uri !== 'noDestinationUri') { const encodedRef = encodeURIComponent(ref); uri = uri.with({ - query: uri.query.length > 0 ? (uri.query + `&${queryParamName}=${encodedRef}`) : `${queryParamName}=${encodedRef}` + query: uri.query.length > 0 ? (uri.query + `&${queryParamName}=${encodedRef}&continueOn=1`) : `${queryParamName}=${encodedRef}&continueOn=1` }); // Open the URI @@ -289,7 +327,7 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo this._register(registerAction2(class ResumeLatestEditSessionAction extends Action2 { constructor() { super({ - id: 'workbench.experimental.editSessions.actions.resumeLatest', + id: 'workbench.editSessions.actions.resumeLatest', title: { value: localize('resume latest.v2', "Resume Latest Edit Session"), original: 'Resume Latest Edit Session' }, category: EDIT_SESSION_SYNC_CATEGORY, f1: true, @@ -315,7 +353,7 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo this._register(registerAction2(class StoreLatestEditSessionAction extends Action2 { constructor() { super({ - id: 'workbench.experimental.editSessions.actions.storeCurrent', + id: 'workbench.editSessions.actions.storeCurrent', title: { value: localize('store current.v2', "Store Current Edit Session"), original: 'Store Current Edit Session' }, category: EDIT_SESSION_SYNC_CATEGORY, f1: true, @@ -348,6 +386,10 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo this.logService.info(ref !== undefined ? `Resuming edit session with ref ${ref}...` : 'Resuming edit session...'); + if (silent && !(await this.editSessionsStorageService.initialize(false, true))) { + return; + } + const data = await this.editSessionsStorageService.read(ref); if (!data) { if (ref === undefined && !silent) { @@ -367,57 +409,29 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo } try { - const changes: ({ uri: URI; type: ChangeType; contents: string | undefined })[] = []; - let hasLocalUncommittedChanges = false; - const workspaceFolders = this.contextService.getWorkspace().folders; - - for (const folder of editSession.folders) { - const cancellationTokenSource = new CancellationTokenSource(); - let folderRoot: IWorkspaceFolder | undefined; - - if (folder.canonicalIdentity) { - // Look for an edit session identifier that we can use - for (const f of workspaceFolders) { - const identity = await this.editSessionIdentityService.getEditSessionIdentifier(f, cancellationTokenSource); - this.logService.info(`Matching identity ${identity} against edit session folder identity ${folder.canonicalIdentity}...`); - if (equals(identity, folder.canonicalIdentity)) { - folderRoot = f; - break; - } - } - } else { - folderRoot = workspaceFolders.find((f) => f.name === folder.name); - } - - if (!folderRoot) { - this.logService.info(`Skipping applying ${folder.workingChanges.length} changes from edit session with ref ${ref} as no matching workspace folder was found.`); - return; - } - - for (const repository of this.scmService.repositories) { - if (repository.provider.rootUri !== undefined && - this.contextService.getWorkspaceFolder(repository.provider.rootUri)?.name === folder.name && - this.getChangedResources(repository).length > 0 - ) { - hasLocalUncommittedChanges = true; - break; - } - } - - for (const { relativeFilePath, contents, type } of folder.workingChanges) { - const uri = joinPath(folderRoot.uri, relativeFilePath); - changes.push({ uri: uri, type: type, contents: contents }); - } + const { changes, conflictingChanges } = await this.generateChanges(editSession, ref); + if (changes.length === 0) { + return; } - if (hasLocalUncommittedChanges) { - // TODO@joyceerhl Provide the option to diff files which would be overwritten by edit session contents - const result = await this.dialogService.confirm({ - message: localize('resume edit session warning', 'Resuming your edit session may overwrite your existing uncommitted changes. Do you want to proceed?'), - type: 'warning', - title: EDIT_SESSION_SYNC_CATEGORY.value - }); - if (!result.confirmed) { + // TODO@joyceerhl Provide the option to diff files which would be overwritten by edit session contents + if (conflictingChanges.length > 0) { + const yes = localize('resume edit session yes', 'Yes'); + const cancel = localize('resume edit session cancel', 'Cancel'); + // Allow to show edit sessions + + const result = await this.dialogService.show( + Severity.Warning, + conflictingChanges.length > 1 ? + localize('resume edit session warning many', 'Resuming your edit session will overwrite the following {0} files. Do you want to proceed?', conflictingChanges.length) : + localize('resume edit session warning 1', 'Resuming your edit session will overwrite {0}. Do you want to proceed?', basename(conflictingChanges[0].uri)), + [cancel, yes], + { + detail: conflictingChanges.length > 1 ? getFileNamesMessage(conflictingChanges.map((c) => c.uri)) : undefined, + cancelId: 0 + }); + + if (result.choice === 0) { return; } } @@ -439,6 +453,77 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo } } + private async generateChanges(editSession: EditSession, ref: string) { + const changes: ({ uri: URI; type: ChangeType; contents: string | undefined })[] = []; + const conflictingChanges = []; + const workspaceFolders = this.contextService.getWorkspace().folders; + + for (const folder of editSession.folders) { + const cancellationTokenSource = new CancellationTokenSource(); + let folderRoot: IWorkspaceFolder | undefined; + + if (folder.canonicalIdentity) { + // Look for an edit session identifier that we can use + for (const f of workspaceFolders) { + const identity = await this.editSessionIdentityService.getEditSessionIdentifier(f, cancellationTokenSource); + this.logService.info(`Matching identity ${identity} against edit session folder identity ${folder.canonicalIdentity}...`); + if (equals(identity, folder.canonicalIdentity)) { + folderRoot = f; + break; + } + } + } else { + folderRoot = workspaceFolders.find((f) => f.name === folder.name); + } + + if (!folderRoot) { + this.logService.info(`Skipping applying ${folder.workingChanges.length} changes from edit session with ref ${ref} as no matching workspace folder was found.`); + return { changes: [], conflictingChanges: [] }; + } + + const localChanges = new Set(); + for (const repository of this.scmService.repositories) { + if (repository.provider.rootUri !== undefined && + this.contextService.getWorkspaceFolder(repository.provider.rootUri)?.name === folder.name + ) { + const repositoryChanges = this.getChangedResources(repository); + repositoryChanges.forEach((change) => localChanges.add(change.toString())); + } + } + + for (const change of folder.workingChanges) { + const uri = joinPath(folderRoot.uri, change.relativeFilePath); + + changes.push({ uri, type: change.type, contents: change.contents }); + if (await this.willChangeLocalContents(localChanges, uri, change)) { + conflictingChanges.push({ uri, type: change.type, contents: change.contents }); + } + } + } + + return { changes, conflictingChanges }; + } + + private async willChangeLocalContents(localChanges: Set, uriWithIncomingChanges: URI, incomingChange: Change) { + if (!localChanges.has(uriWithIncomingChanges.toString())) { + return false; + } + + const { contents, type } = incomingChange; + + switch (type) { + case (ChangeType.Addition): { + const [originalContents, incomingContents] = await Promise.all([sha1Hex(contents), sha1Hex(encodeBase64((await this.fileService.readFile(uriWithIncomingChanges)).value))]); + return originalContents !== incomingContents; + } + case (ChangeType.Deletion): { + return await this.fileService.exists(uriWithIncomingChanges); + } + default: + throw new Error('Unhandled change type.'); + } + } + async storeEditSession(fromStoreCommand: boolean): Promise { const folders: Folder[] = []; let hasEdits = false; @@ -530,17 +615,15 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo } private getChangedResources(repository: ISCMRepository) { - const trackedUris = repository.provider.groups.elements.reduce((resources, resourceGroups) => { + return repository.provider.groups.elements.reduce((resources, resourceGroups) => { resourceGroups.elements.forEach((resource) => resources.add(resource.sourceUri)); return resources; }, new Set()); // A URI might appear in more than one resource group - - return [...trackedUris]; } private hasEditSession() { for (const repository of this.scmService.repositories) { - if (this.getChangedResources(repository).length > 0) { + if (this.getChangedResources(repository).size > 0) { return true; } } @@ -725,7 +808,7 @@ const continueEditSessionExtPoint = ExtensionsRegistry.registerExtensionPoint(WorkbenchExtensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(EditSessionsContribution, 'EditSessionsContribution', LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(EditSessionsContribution, LifecyclePhase.Restored); Registry.as(ConfigurationExtensions.Configuration).registerConfiguration({ ...workbenchConfigurationNodeBase, @@ -741,23 +824,28 @@ Registry.as(ConfigurationExtensions.Configuration).regis 'default': 'off', 'markdownDescription': localize('autoStore', "Controls whether to automatically store an available edit session for the current workspace."), }, - 'workbench.experimental.editSessions.enabled': { - 'type': 'boolean', - 'tags': ['experimental', 'usesOnlineServices'], - 'default': true, - 'markdownDescription': localize('editSessionsEnabled', "Controls whether to display cloud-enabled actions to store and resume uncommitted changes when switching between web, desktop, or devices."), - }, - 'workbench.experimental.editSessions.autoResume': { + 'workbench.editSessions.autoResume': { enum: ['onReload', 'off'], enumDescriptions: [ localize('autoResume.onReload', "Automatically resume available edit session on window reload."), localize('autoResume.off', "Never attempt to resume an edit session.") ], 'type': 'string', - 'tags': ['experimental', 'usesOnlineServices'], + 'tags': ['usesOnlineServices'], 'default': 'onReload', 'markdownDescription': localize('autoResume', "Controls whether to automatically resume an available edit session for the current workspace."), }, + 'workbench.editSessions.continueOn': { + enum: ['prompt', 'off'], + enumDescriptions: [ + localize('continueOn.promptForAuth', 'Prompt the user to sign in to store edit sessions with Continue Working On.'), + localize('continueOn.off', 'Do not use edit sessions with Continue Working On unless the user has already turned on edit sessions.') + ], + type: 'string', + tags: ['usesOnlineServices'], + default: 'prompt', + markdownDescription: localize('continueOn', 'Controls whether to prompt the user to store edit sessions when using Continue Working On.') + }, 'workbench.experimental.editSessions.continueOn': { enum: ['prompt', 'off'], enumDescriptions: [ @@ -767,7 +855,25 @@ Registry.as(ConfigurationExtensions.Configuration).regis type: 'string', tags: ['experimental', 'usesOnlineServices'], default: 'prompt', + markdownDeprecationMessage: localize('continueOnDeprecated', 'This setting is deprecated in favor of {0}.', '`#workbench.experimental.continueOn#`'), markdownDescription: localize('continueOn', 'Controls whether to prompt the user to store edit sessions when using Continue Working On.') - } + }, + 'workbench.experimental.editSessions.enabled': { + 'type': 'boolean', + 'tags': ['experimental', 'usesOnlineServices'], + 'default': true, + 'markdownDeprecationMessage': localize('editSessionsEnabledDeprecated', "This setting is deprecated as Edit Sessions are no longer experimental. Please see {0} and {1} for configuring behavior related to Edit Sessions.", '`#workbench.editSessions.autoResume#`', '`#workbench.editSessions.continueOn#`') + }, + 'workbench.experimental.editSessions.autoResume': { + enum: ['onReload', 'off'], + enumDescriptions: [ + localize('autoResume.onReload', "Automatically resume available edit session on window reload."), + localize('autoResume.off', "Never attempt to resume an edit session.") + ], + 'type': 'string', + 'tags': ['experimental', 'usesOnlineServices'], + 'default': 'onReload', + 'markdownDeprecationMessage': localize('autoResumeDeprecated', "This setting is deprecated in favor of {0}.", '`#workbench.editSessions.autoResume#`') + }, } }); diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts index fdce08d7082..fcdcc89dcce 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts @@ -27,6 +27,7 @@ import { isWeb } from 'vs/base/common/platform'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { Codicon } from 'vs/base/common/codicons'; import { IUserDataSyncMachinesService, UserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; +import { Emitter } from 'vs/base/common/event'; type ExistingSession = IQuickPickItem & { session: AuthenticationSession & { providerId: string } }; type AuthenticationProviderOption = IQuickPickItem & { provider: IAuthenticationProvider }; @@ -50,6 +51,16 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes return this.existingSessionId !== undefined; } + private _didSignIn = new Emitter(); + get onDidSignIn() { + return this._didSignIn.event; + } + + private _didSignOut = new Emitter(); + get onDidSignOut() { + return this._didSignOut.event; + } + constructor( @IFileService private readonly fileService: IFileService, @IStorageService private readonly storageService: IStorageService, @@ -156,12 +167,15 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes return []; } - public async initialize(fromContinueOn: boolean) { + public async initialize(fromContinueOn: boolean, silent: boolean = false) { if (this.initialized) { return true; } - this.initialized = await this.doInitialize(fromContinueOn); + this.initialized = await this.doInitialize(fromContinueOn, silent); this.signedInContext.set(this.initialized); + if (this.initialized) { + this._didSignIn.fire(); + } return this.initialized; } @@ -172,7 +186,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes * meaning that authentication is configured and it * can be used to communicate with the remote storage service */ - private async doInitialize(fromContinueOn: boolean): Promise { + private async doInitialize(fromContinueOn: boolean, silent: boolean): Promise { // Wait for authentication extensions to be registered await this.extensionService.whenInstalledExtensionsRegistered(); @@ -197,7 +211,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes return true; } - const authenticationSession = await this.getAuthenticationSession(fromContinueOn); + const authenticationSession = await this.getAuthenticationSession(fromContinueOn, silent); if (authenticationSession !== undefined) { this.#authenticationInfo = authenticationSession; this.storeClient.setAuthToken(authenticationSession.token, authenticationSession.providerId); @@ -230,7 +244,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes return currentMachineId; } - private async getAuthenticationSession(fromContinueOn: boolean) { + private async getAuthenticationSession(fromContinueOn: boolean, silent: boolean) { // If the user signed in previously and the session is still available, reuse that without prompting the user again if (this.existingSessionId) { this.logService.info(`Searching for existing authentication session with ID ${this.existingSessionId}`); @@ -238,6 +252,8 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes if (existingSession) { this.logService.info(`Found existing authentication session with ID ${existingSession.session.id}`); return { sessionId: existingSession.session.id, token: existingSession.session.idToken ?? existingSession.session.accessToken, providerId: existingSession.session.providerId }; + } else { + this._didSignOut.fire(); } } @@ -252,6 +268,12 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes } } + // If we aren't supposed to prompt the user because + // we're in a silent flow, just return here + if (silent) { + return; + } + // Ask the user to pick a preferred account const authenticationSession = await this.getAccountPreference(fromContinueOn); if (authenticationSession !== undefined) { @@ -293,7 +315,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes quickpick.onDidTriggerItemButton(async (e) => { if (e.button.tooltip === configureContinueOnPreference.tooltip) { - await this.commandService.executeCommand('workbench.action.openSettings', 'workbench.experimental.editSessions.continueOn'); + await this.commandService.executeCommand('workbench.action.openSettings', 'workbench.editSessions.continueOn'); } }); diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessionsViews.ts b/src/vs/workbench/contrib/editSessions/browser/editSessionsViews.ts index feba1d9df63..92e368b719a 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessionsViews.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessionsViews.ts @@ -10,7 +10,7 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati import { Registry } from 'vs/platform/registry/common/platform'; import { TreeView, TreeViewPane } from 'vs/workbench/browser/parts/views/treeView'; import { Extensions, ITreeItem, ITreeViewDataProvider, ITreeViewDescriptor, IViewsRegistry, TreeItemCollapsibleState, TreeViewItemHandleArg, ViewContainer } from 'vs/workbench/common/views'; -import { EDIT_SESSIONS_DATA_VIEW_ID, EDIT_SESSIONS_SCHEME, EDIT_SESSIONS_SHOW_VIEW, EDIT_SESSIONS_SIGNED_IN, EDIT_SESSIONS_SIGNED_IN_KEY, EDIT_SESSIONS_TITLE, IEditSessionsStorageService } from 'vs/workbench/contrib/editSessions/common/editSessions'; +import { EDIT_SESSIONS_DATA_VIEW_ID, EDIT_SESSIONS_SCHEME, EDIT_SESSIONS_SHOW_VIEW, EDIT_SESSIONS_TITLE, IEditSessionsStorageService } from 'vs/workbench/contrib/editSessions/common/editSessions'; import { URI } from 'vs/base/common/uri'; import { fromNow } from 'vs/base/common/date'; import { Codicon } from 'vs/base/common/codicons'; @@ -54,7 +54,7 @@ export class EditSessionsDataViews extends Disposable { canMoveView: false, treeView, collapsed: false, - when: ContextKeyExpr.and(EDIT_SESSIONS_SIGNED_IN, EDIT_SESSIONS_SHOW_VIEW), + when: ContextKeyExpr.and(EDIT_SESSIONS_SHOW_VIEW), order: 100, hideByDefault: true, }], container); @@ -69,7 +69,7 @@ export class EditSessionsDataViews extends Disposable { localize('storeEditSessionTitle', 'Store Edit Session') ) ), - when: ContextKeyExpr.and(ContextKeyExpr.equals(EDIT_SESSIONS_SIGNED_IN_KEY, true), ContextKeyExpr.equals(EDIT_SESSIONS_COUNT_KEY, 0)), + when: ContextKeyExpr.equals(EDIT_SESSIONS_COUNT_KEY, 0), order: 1 }); @@ -90,7 +90,7 @@ export class EditSessionsDataViews extends Disposable { async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise { const editSessionId = URI.parse(handle.$treeItemHandle).path.substring(1); const commandService = accessor.get(ICommandService); - await commandService.executeCommand('workbench.experimental.editSessions.actions.resumeLatest', editSessionId); + await commandService.executeCommand('workbench.editSessions.actions.resumeLatest', editSessionId); await treeView.refresh(); } }); @@ -106,7 +106,7 @@ export class EditSessionsDataViews extends Disposable { async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise { const commandService = accessor.get(ICommandService); - await commandService.executeCommand('workbench.experimental.editSessions.actions.storeCurrent'); + await commandService.executeCommand('workbench.editSessions.actions.storeCurrent'); await treeView.refresh(); } }); @@ -208,7 +208,8 @@ class EditSessionDataViewDataProvider implements ITreeViewDataProvider { const sessionData = await this.editSessionsStorageService.read(session.ref); const label = sessionData?.editSession.folders.map((folder) => folder.name).join(', ') ?? session.ref; const machineId = sessionData?.editSession.machine; - const description = machineId === undefined ? fromNow(session.created, true) : `${fromNow(session.created, true)}\u00a0\u00a0\u2022\u00a0\u00a0${await this.editSessionsStorageService.getMachineById(machineId)}`; + const machineName = machineId ? await this.editSessionsStorageService.getMachineById(machineId) : undefined; + const description = machineName === undefined ? fromNow(session.created, true) : `${fromNow(session.created, true)}\u00a0\u00a0\u2022\u00a0\u00a0${machineName}`; editSessions.push({ handle: resource.toString(), diff --git a/src/vs/workbench/contrib/editSessions/common/editSessions.ts b/src/vs/workbench/contrib/editSessions/common/editSessions.ts index b0afa0d9e63..99e5c8a16cc 100644 --- a/src/vs/workbench/contrib/editSessions/common/editSessions.ts +++ b/src/vs/workbench/contrib/editSessions/common/editSessions.ts @@ -12,6 +12,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { ILogService } from 'vs/platform/log/common/log'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { IResourceRefHandle } from 'vs/platform/userDataSync/common/userDataSync'; +import { Event } from 'vs/base/common/event'; export const EDIT_SESSION_SYNC_CATEGORY: ILocalizedString = { original: 'Edit Sessions', @@ -23,8 +24,10 @@ export interface IEditSessionsStorageService { _serviceBrand: undefined; readonly isSignedIn: boolean; + readonly onDidSignIn: Event; + readonly onDidSignOut: Event; - initialize(fromContinueOn: boolean): Promise; + initialize(fromContinueOn: boolean, silent?: boolean): Promise; read(ref: string | undefined): Promise<{ ref: string; editSession: EditSession } | undefined>; write(editSession: EditSession): Promise; delete(ref: string | null): Promise; diff --git a/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts b/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts index 1f9f445495f..3bcd8b1d946 100644 --- a/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts +++ b/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts @@ -35,6 +35,7 @@ import { Event } from 'vs/base/common/event'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; const folderName = 'test-folder'; const folderUri = URI.file(`/${folderName}`); @@ -65,10 +66,18 @@ suite('Edit session sync', () => { override onWillShutdown = Event.None; }); instantiationService.stub(INotificationService, new TestNotificationService()); - instantiationService.stub(IEditSessionsStorageService, new class extends mock() { }); + instantiationService.stub(IEditSessionsStorageService, new class extends mock() { + override onDidSignIn = Event.None; + override onDidSignOut = Event.None; + }); instantiationService.stub(IProgressService, ProgressService); instantiationService.stub(ISCMService, SCMService); instantiationService.stub(IEnvironmentService, TestEnvironmentService); + instantiationService.stub(IDialogService, new class extends mock() { + override async show() { + return { choice: 1 }; + } + }); instantiationService.stub(IConfigurationService, new TestConfigurationService({ workbench: { experimental: { editSessions: { enabled: true } } } })); instantiationService.stub(IWorkspaceContextService, new class extends mock() { override getWorkspace() { @@ -133,6 +142,10 @@ suite('Edit session sync', () => { const readStub = sandbox.stub().returns({ editSession, ref: '0' }); instantiationService.stub(IEditSessionsStorageService, 'read', readStub); + // Ensure that user does not get prompted here + const dialogServiceShowStub = sandbox.stub(); + instantiationService.stub(IDialogService, 'show', dialogServiceShowStub); + // Create root folder await fileService.createFolder(folderUri); @@ -141,6 +154,7 @@ suite('Edit session sync', () => { // Verify edit session was correctly applied assert.equal((await fileService.readFile(fileUri)).value.toString(), fileContents); + assert.equal(dialogServiceShowStub.called, false); }); test('Edit session not stored if there are no edits', async function () { diff --git a/src/vs/workbench/contrib/experiments/browser/experiments.contribution.ts b/src/vs/workbench/contrib/experiments/browser/experiments.contribution.ts index 2dc9b0f9303..c2d36360806 100644 --- a/src/vs/workbench/contrib/experiments/browser/experiments.contribution.ts +++ b/src/vs/workbench/contrib/experiments/browser/experiments.contribution.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IExperimentService, ExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; @@ -13,9 +13,9 @@ import { ExperimentalPrompts } from 'vs/workbench/contrib/experiments/browser/ex import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; -registerSingleton(IExperimentService, ExperimentService, true); +registerSingleton(IExperimentService, ExperimentService, InstantiationType.Delayed); -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ExperimentalPrompts, 'ExperimentalPrompts', LifecyclePhase.Eventually); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ExperimentalPrompts, LifecyclePhase.Eventually); const registry = Registry.as(ConfigurationExtensions.Configuration); diff --git a/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts index e9d58bface0..6eb6663c74c 100644 --- a/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts @@ -35,7 +35,7 @@ import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/common/runtimeExtensionsInput'; import { Action2, MenuId } from 'vs/platform/actions/common/actions'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; interface IExtensionProfileInformation { @@ -491,7 +491,7 @@ export class ShowRuntimeExtensionsAction extends Action2 { super({ id: 'workbench.action.showRuntimeExtensions', title: { value: nls.localize('showRuntimeExtensions', "Show Running Extensions"), original: 'Show Running Extensions' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true, menu: { id: MenuId.ViewContainerTitle, diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 6f0c73a98c1..ba6774dd3f9 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -29,7 +29,7 @@ import { UpdateAction, ReloadAction, EnableDropDownAction, DisableDropDownAction, ExtensionStatusLabelAction, SetFileIconThemeAction, SetColorThemeAction, RemoteInstallAction, ExtensionStatusAction, LocalInstallAction, ToggleSyncExtensionAction, SetProductIconThemeAction, ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, UninstallAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction, - InstallAnotherVersionAction, ExtensionEditorManageExtensionAction, WebInstallAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction, MigrateDeprecatedExtensionAction, SetLanguageAction, ClearLanguageAction + InstallAnotherVersionAction, ExtensionEditorManageExtensionAction, WebInstallAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction, MigrateDeprecatedExtensionAction, SetLanguageAction, ClearLanguageAction, SkipUpdateAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; @@ -322,7 +322,8 @@ export class ExtensionEditor extends EditorPane { const actions = [ this.instantiationService.createInstance(ReloadAction), this.instantiationService.createInstance(ExtensionStatusLabelAction), - this.instantiationService.createInstance(UpdateAction), + this.instantiationService.createInstance(ActionWithDropDownAction, 'extensions.updateActions', '', + [[this.instantiationService.createInstance(UpdateAction)], [this.instantiationService.createInstance(SkipUpdateAction)]]), this.instantiationService.createInstance(SetColorThemeAction), this.instantiationService.createInstance(SetFileIconThemeAction), this.instantiationService.createInstance(SetProductIconThemeAction), @@ -674,7 +675,7 @@ export class ExtensionEditor extends EditorPane { private async openMarkdown(cacheResult: CacheResult, noContentCopy: string, container: HTMLElement, webviewIndex: WebviewIndex, token: CancellationToken): Promise { try { - const body = await this.renderMarkdown(cacheResult, container); + const body = await this.renderMarkdown(cacheResult, container, token); if (token.isCancellationRequested) { return Promise.resolve(null); } @@ -741,13 +742,21 @@ export class ExtensionEditor extends EditorPane { } } - private async renderMarkdown(cacheResult: CacheResult, container: HTMLElement) { + private async renderMarkdown(cacheResult: CacheResult, container: HTMLElement, token?: CancellationToken): Promise { const contents = await this.loadContents(() => cacheResult, container); - const content = await renderMarkdownDocument(contents, this.extensionService, this.languageService); + if (token?.isCancellationRequested) { + return ''; + } + + const content = await renderMarkdownDocument(contents, this.extensionService, this.languageService, true, false, token); + if (token?.isCancellationRequested) { + return ''; + } + return this.renderBody(content); } - private async renderBody(body: string): Promise { + private renderBody(body: string): string { const nonce = generateUuid(); const colorMap = TokenizationRegistry.getColorMap(); const css = colorMap ? generateTokensCSSForColorMap(colorMap) : ''; @@ -904,10 +913,14 @@ export class ExtensionEditor extends EditorPane { resources.push([localize('Marketplace', "Marketplace"), URI.parse(extension.url)]); } if (extension.repository) { - resources.push([localize('repository', "Repository"), URI.parse(extension.repository)]); + try { + resources.push([localize('repository', "Repository"), URI.parse(extension.repository)]); + } catch (error) {/* Ignore */ } } if (extension.url && extension.licenseUrl) { - resources.push([localize('license', "License"), URI.parse(extension.licenseUrl)]); + try { + resources.push([localize('license', "License"), URI.parse(extension.licenseUrl)]); + } catch (error) {/* Ignore */ } } if (extension.publisherUrl) { resources.push([extension.publisherDisplayName, extension.publisherUrl]); @@ -1167,7 +1180,14 @@ export class ExtensionEditor extends EditorPane { } else if (configuration) { properties = configuration.properties; } - const contrib = properties ? Object.keys(properties) : []; + + let contrib = properties ? Object.keys(properties) : []; + + // filter deprecated settings + contrib = contrib.filter(key => { + const config = properties[key]; + return !config.deprecationMessage && !config.markdownDeprecationMessage; + }); if (!contrib.length) { return false; @@ -1177,12 +1197,12 @@ export class ExtensionEditor extends EditorPane { $('summary', { tabindex: '0' }, localize('settings', "Settings ({0})", contrib.length)), $('table', undefined, $('tr', undefined, - $('th', undefined, localize('setting name', "Name")), + $('th', undefined, localize('setting name', "ID")), $('th', undefined, localize('description', "Description")), $('th', undefined, localize('default', "Default")) ), ...contrib.map(key => { - let description: (Node | string) = properties[key].description; + let description: (Node | string) = properties[key].description || ''; if (properties[key].markdownDescription) { const { element, dispose } = renderMarkdown({ value: properties[key].markdownDescription }, { actionHandler: { callback: (content) => this.openerService.open(content).catch(onUnexpectedError), disposables: this.contentDisposables } }); description = element; @@ -1358,7 +1378,7 @@ export class ExtensionEditor extends EditorPane { $('table', undefined, $('tr', undefined, $('th', undefined, localize('authentication.label', "Label")), - $('th', undefined, localize('authentication.id', "Id")) + $('th', undefined, localize('authentication.id', "ID")) ), ...authentication.map(action => $('tr', undefined, @@ -1440,7 +1460,7 @@ export class ExtensionEditor extends EditorPane { $('summary', { tabindex: '0' }, localize('colors', "Colors ({0})", colors.length)), $('table', undefined, $('tr', undefined, - $('th', undefined, localize('colorId', "Id")), + $('th', undefined, localize('colorId', "ID")), $('th', undefined, localize('description', "Description")), $('th', undefined, localize('defaultDark', "Dark Default")), $('th', undefined, localize('defaultLight', "Light Default")), @@ -1546,8 +1566,8 @@ export class ExtensionEditor extends EditorPane { $('summary', { tabindex: '0' }, localize('commands', "Commands ({0})", commands.length)), $('table', undefined, $('tr', undefined, - $('th', undefined, localize('command name', "Name")), - $('th', undefined, localize('description', "Description")), + $('th', undefined, localize('command name', "ID")), + $('th', undefined, localize('command title', "Title")), $('th', undefined, localize('keyboard shortcuts', "Keyboard Shortcuts")), $('th', undefined, localize('menuContexts', "Menu Contexts")) ), @@ -1657,7 +1677,7 @@ export class ExtensionEditor extends EditorPane { $('summary', { tabindex: '0' }, localize('Notebooks', "Notebooks ({0})", contrib.length)), $('table', undefined, $('tr', undefined, - $('th', undefined, localize('Notebook id', "Id")), + $('th', undefined, localize('Notebook id', "ID")), $('th', undefined, localize('Notebook name', "Name")), ), ...contrib.map(d => $('tr', undefined, diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 41169964b15..5571c168904 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -8,13 +8,12 @@ import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { Registry } from 'vs/platform/registry/common/platform'; import { MenuRegistry, MenuId, registerAction2, Action2, ISubmenuItem, IMenuItem, IAction2Options } from 'vs/platform/actions/common/actions'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { ExtensionsLabel, ExtensionsLocalizedLabel, ExtensionsChannelId, IExtensionManagementService, IExtensionGalleryService, PreferencesLocalizedLabel, InstallOperation, InstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { ExtensionsLocalizedLabel, IExtensionManagementService, IExtensionGalleryService, PreferencesLocalizedLabel, InstallOperation, InstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; import { EnablementState, IExtensionManagementServerService, IWorkbenchExtensionEnablementService, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IExtensionIgnoredRecommendationsService, IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IOutputChannelRegistry, Extensions as OutputExtensions } from 'vs/workbench/services/output/common/output'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, WORKSPACE_RECOMMENDATIONS_VIEW_ID, IWorkspaceRecommendedExtensionsView, AutoUpdateConfigurationKey, HasOutdatedExtensionsContext, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID, ExtensionEditorTab, THEME_ACTIONS_GROUP, INSTALL_ACTIONS_GROUP } from 'vs/workbench/contrib/extensions/common/extensions'; +import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, WORKSPACE_RECOMMENDATIONS_VIEW_ID, IWorkspaceRecommendedExtensionsView, AutoUpdateConfigurationKey, HasOutdatedExtensionsContext, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID, ExtensionEditorTab, THEME_ACTIONS_GROUP, INSTALL_ACTIONS_GROUP, OUTDATED_EXTENSIONS_VIEW_ID, CONTEXT_HAS_GALLERY } from 'vs/workbench/contrib/extensions/common/extensions'; import { ReinstallAction, InstallSpecificVersionOfExtensionAction, ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, PromptExtensionInstallFailureAction, SearchExtensionsAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction, SetColorThemeAction, SetFileIconThemeAction, SetProductIconThemeAction, ClearLanguageAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; import { ExtensionEditor } from 'vs/workbench/contrib/extensions/browser/extensionEditor'; @@ -46,7 +45,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { MultiCommand } from 'vs/editor/browser/editorExtensions'; import { IWebview } from 'vs/workbench/contrib/webview/browser/webview'; import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; import { ExtensionRecommendationNotificationService } from 'vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService'; import { IExtensionService, toExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; @@ -81,12 +80,9 @@ import { IStringDictionary } from 'vs/base/common/collections'; // Singletons registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService, InstantiationType.Eager /* Auto updates extensions */); -registerSingleton(IExtensionRecommendationNotificationService, ExtensionRecommendationNotificationService, true); +registerSingleton(IExtensionRecommendationNotificationService, ExtensionRecommendationNotificationService, InstantiationType.Delayed); registerSingleton(IExtensionRecommendationsService, ExtensionRecommendationsService, InstantiationType.Eager /* Prompts recommendations in the background */); -Registry.as(OutputExtensions.OutputChannels) - .registerChannel({ id: ExtensionsChannelId, label: ExtensionsLabel, log: false }); - // Quick Access Registry.as(Extensions.Quickaccess).registerQuickAccessProvider({ ctor: ManageExtensionsQuickAccessProvider, @@ -438,7 +434,6 @@ overrideActionForActiveExtensionEditorWebview(CutAction, webview => webview.cut( overrideActionForActiveExtensionEditorWebview(PasteAction, webview => webview.paste()); // Contexts -export const CONTEXT_HAS_GALLERY = new RawContextKey('hasGallery', false); export const CONTEXT_HAS_LOCAL_SERVER = new RawContextKey('hasLocalServer', false); export const CONTEXT_HAS_REMOTE_SERVER = new RawContextKey('hasRemoteServer', false); export const CONTEXT_HAS_WEB_SERVER = new RawContextKey('hasWebServer', false); @@ -453,10 +448,10 @@ async function runAction(action: IAction): Promise { } } -interface IExtensionActionOptions extends IAction2Options { +type IExtensionActionOptions = IAction2Options & { menuTitles?: { [id: string]: string }; run(accessor: ServicesAccessor, ...args: any[]): Promise; -} +}; class ExtensionsContributions extends Disposable implements IWorkbenchContribution { @@ -591,7 +586,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER)) }, { id: MenuId.ViewContainerTitle, - when: ContextKeyExpr.equals('viewContainer', VIEWLET_ID), + when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), CONTEXT_HAS_GALLERY), group: '1_updates', order: 1 }], @@ -610,7 +605,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi MenuRegistry.appendMenuItem(MenuId.ViewContainerTitle, { submenu: autoUpdateExtensionsSubMenu, title: localize('configure auto updating extensions', "Auto Update Extensions"), - when: ContextKeyExpr.equals('viewContainer', VIEWLET_ID), + when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), CONTEXT_HAS_GALLERY), group: '1_updates', order: 5, }); @@ -653,15 +648,23 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi title: { value: localize('updateAll', "Update All Extensions"), original: 'Update All Extensions' }, category: ExtensionsLocalizedLabel, precondition: HasOutdatedExtensionsContext, - menu: [{ - id: MenuId.CommandPalette, - when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER)) - }, { - id: MenuId.ViewContainerTitle, - when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), ContextKeyExpr.or(ContextKeyExpr.has(`config.${AutoUpdateConfigurationKey}`).negate(), ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, 'onlyEnabledExtensions'))), - group: '1_updates', - order: 2 - }], + menu: [ + { + id: MenuId.CommandPalette, + when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER)) + }, { + id: MenuId.ViewContainerTitle, + when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), ContextKeyExpr.or(ContextKeyExpr.has(`config.${AutoUpdateConfigurationKey}`).negate(), ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, 'onlyEnabledExtensions'))), + group: '1_updates', + order: 2 + }, { + id: MenuId.ViewTitle, + when: ContextKeyExpr.equals('view', OUTDATED_EXTENSIONS_VIEW_ID), + group: 'navigation', + order: 1 + } + ], + icon: installWorkspaceRecommendedIcon, run: () => { return Promise.all(this.extensionsWorkbenchService.outdated.map(async extension => { try { @@ -678,6 +681,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi title: { value: localize('disableAutoUpdate', "Disable Auto Update for all extensions"), original: 'Disable Auto Update for all extensions' }, category: ExtensionsLocalizedLabel, f1: true, + precondition: CONTEXT_HAS_GALLERY, run: (accessor: ServicesAccessor) => accessor.get(IConfigurationService).updateValue(AutoUpdateConfigurationKey, false) }); @@ -686,6 +690,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi title: { value: localize('enableAutoUpdate', "Enable Auto Update for all extensions"), original: 'Enable Auto Update for all extensions' }, category: ExtensionsLocalizedLabel, f1: true, + precondition: CONTEXT_HAS_GALLERY, run: (accessor: ServicesAccessor) => accessor.get(IConfigurationService).updateValue(AutoUpdateConfigurationKey, true) }); @@ -830,7 +835,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi this.registerExtensionAction({ id: 'workbench.extensions.action.installWebExtensionFromLocation', title: { value: localize('installWebExtensionFromLocation', "Install Web Extension..."), original: 'Install Web Extension...' }, - category: CATEGORIES.Developer, + category: Categories.Developer, menu: [{ id: MenuId.CommandPalette, when: ContextKeyExpr.or(CONTEXT_HAS_WEB_SERVER) @@ -988,11 +993,12 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi id: 'workbench.extensions.action.extensionUpdates', title: { value: localize('extensionUpdates', "Show Extension Updates"), original: 'Show Extension Updates' }, category: ExtensionsLocalizedLabel, + precondition: CONTEXT_HAS_GALLERY, + f1: true, menu: [{ - id: MenuId.CommandPalette, - }, { id: extensionsFilterSubMenu, group: '3_installed', + when: CONTEXT_HAS_GALLERY, order: 1, }], menuTitles: { @@ -1042,7 +1048,6 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi id: 'workbench.extensions.action.showDisabledExtensions', title: { value: localize('showDisabledExtensions', "Show Disabled Extensions"), original: 'Show Disabled Extensions' }, category: ExtensionsLocalizedLabel, - menu: [{ id: MenuId.CommandPalette, when: ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER) @@ -1182,7 +1187,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi this.registerExtensionAction({ id: ReinstallAction.ID, title: { value: ReinstallAction.LABEL, original: 'Reinstall Extension...' }, - category: CATEGORIES.Developer, + category: Categories.Developer, menu: { id: MenuId.CommandPalette, when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER)) @@ -1580,18 +1585,18 @@ class ExtensionStorageCleaner implements IWorkbenchContribution { } const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(ExtensionsContributions, 'ExtensionsContributions', LifecyclePhase.Restored); -workbenchRegistry.registerWorkbenchContribution(StatusUpdater, 'StatusUpdater', LifecyclePhase.Restored); -workbenchRegistry.registerWorkbenchContribution(MaliciousExtensionChecker, 'MaliciousExtensionChecker', LifecyclePhase.Eventually); -workbenchRegistry.registerWorkbenchContribution(KeymapExtensions, 'KeymapExtensions', LifecyclePhase.Restored); -workbenchRegistry.registerWorkbenchContribution(ExtensionsViewletViewsContribution, 'ExtensionsViewletViewsContribution', LifecyclePhase.Restored); -workbenchRegistry.registerWorkbenchContribution(ExtensionActivationProgress, 'ExtensionActivationProgress', LifecyclePhase.Eventually); -workbenchRegistry.registerWorkbenchContribution(ExtensionDependencyChecker, 'ExtensionDependencyChecker', LifecyclePhase.Eventually); -workbenchRegistry.registerWorkbenchContribution(ExtensionEnablementWorkspaceTrustTransitionParticipant, 'ExtensionEnablementWorkspaceTrustTransitionParticipant', LifecyclePhase.Restored); -workbenchRegistry.registerWorkbenchContribution(ExtensionsCompletionItemsProvider, 'ExtensionsCompletionItemsProvider', LifecyclePhase.Restored); -workbenchRegistry.registerWorkbenchContribution(UnsupportedExtensionsMigrationContrib, 'UnsupportedExtensionsMigrationContrib', LifecyclePhase.Eventually); +workbenchRegistry.registerWorkbenchContribution(ExtensionsContributions, LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(StatusUpdater, LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(MaliciousExtensionChecker, LifecyclePhase.Eventually); +workbenchRegistry.registerWorkbenchContribution(KeymapExtensions, LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(ExtensionsViewletViewsContribution, LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(ExtensionActivationProgress, LifecyclePhase.Eventually); +workbenchRegistry.registerWorkbenchContribution(ExtensionDependencyChecker, LifecyclePhase.Eventually); +workbenchRegistry.registerWorkbenchContribution(ExtensionEnablementWorkspaceTrustTransitionParticipant, LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(ExtensionsCompletionItemsProvider, LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(UnsupportedExtensionsMigrationContrib, LifecyclePhase.Eventually); if (isWeb) { - workbenchRegistry.registerWorkbenchContribution(ExtensionStorageCleaner, 'ExtensionStorageCleaner', LifecyclePhase.Eventually); + workbenchRegistry.registerWorkbenchContribution(ExtensionStorageCleaner, LifecyclePhase.Eventually); } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 634dd88be4e..102fec7ea15 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -802,18 +802,18 @@ export class UninstallAction extends ExtensionAction { } } -export class UpdateAction extends ExtensionAction { +abstract class AbstractUpdateAction extends ExtensionAction { private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} prominent update`; - private static readonly DisabledClass = `${UpdateAction.EnabledClass} disabled`; + private static readonly DisabledClass = `${AbstractUpdateAction.EnabledClass} disabled`; private readonly updateThrottler = new Throttler(); constructor( - @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, - @IInstantiationService private readonly instantiationService: IInstantiationService, + id: string, label: string | undefined, + protected readonly extensionsWorkbenchService: IExtensionsWorkbenchService, ) { - super(`extensions.update`, '', UpdateAction.DisabledClass, false); + super(id, label, AbstractUpdateAction.DisabledClass, false); this.update(); } @@ -824,7 +824,6 @@ export class UpdateAction extends ExtensionAction { private async computeAndUpdateEnablement(): Promise { this.enabled = false; this.class = UpdateAction.DisabledClass; - this.label = this.getLabel(); if (!this.extension) { return; @@ -838,8 +837,17 @@ export class UpdateAction extends ExtensionAction { const isInstalled = this.extension.state === ExtensionState.Installed; this.enabled = canInstall && isInstalled && this.extension.outdated; - this.class = this.enabled ? UpdateAction.EnabledClass : UpdateAction.DisabledClass; - this.label = this.getLabel(this.extension); + this.class = this.enabled ? AbstractUpdateAction.EnabledClass : AbstractUpdateAction.DisabledClass; + } +} + +export class UpdateAction extends AbstractUpdateAction { + + constructor( + @IExtensionsWorkbenchService override readonly extensionsWorkbenchService: IExtensionsWorkbenchService, + @IInstantiationService protected readonly instantiationService: IInstantiationService, + ) { + super(`extensions.update`, localize('update', "Update"), extensionsWorkbenchService); } override async run(): Promise { @@ -858,15 +866,35 @@ export class UpdateAction extends ExtensionAction { this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, extension.latestVersion, InstallOperation.Update, undefined, err).run(); } } +} - private getLabel(extension?: IExtension): string { - if (!extension?.outdated) { - return localize('updateAction', "Update"); +export class SkipUpdateAction extends AbstractUpdateAction { + + constructor( + @IExtensionsWorkbenchService override readonly extensionsWorkbenchService: IExtensionsWorkbenchService + ) { + super(`extensions.ignoreUpdates`, localize('ignoreUpdates', "Ignore Updates"), extensionsWorkbenchService); + } + + override update() { + if (!this.extension) { + return; } - if (extension.outdatedTargetPlatform) { - return localize('updateToTargetPlatformVersion', "Update to {0} version", TargetPlatformToString(extension.gallery!.properties.targetPlatform)); + if (this.extension.isBuiltin) { + this.enabled = false; + return; } - return localize('updateToLatestVersion', "Update to {0}", extension.latestVersion); + super.update(); + this._checked = this.extensionsWorkbenchService.isExtensionIgnoresUpdates(this.extension); + } + + override async run(): Promise { + if (!this.extension) { + return; + } + alert(localize('ignoreExtensionUpdate', "Ignoring {0} updates", this.extension.displayName)); + const newIgnoresAutoUpdates = !this.extensionsWorkbenchService.isExtensionIgnoresUpdates(this.extension); + this.extensionsWorkbenchService.setExtensionIgnoresUpdate(this.extension, newIgnoresAutoUpdates); } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts index 931710ee3d5..7c9a3c17f22 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts @@ -13,7 +13,7 @@ import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging'; import { Event } from 'vs/base/common/event'; import { IExtension, ExtensionContainers, ExtensionState, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; -import { UpdateAction, ManageExtensionAction, ReloadAction, ExtensionStatusLabelAction, RemoteInstallAction, ExtensionStatusAction, LocalInstallAction, ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction, WebInstallAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction, MigrateDeprecatedExtensionAction, SetLanguageAction, ClearLanguageAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; +import { ManageExtensionAction, ReloadAction, ExtensionStatusLabelAction, RemoteInstallAction, ExtensionStatusAction, LocalInstallAction, ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction, WebInstallAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction, MigrateDeprecatedExtensionAction, SetLanguageAction, ClearLanguageAction, UpdateAction, SkipUpdateAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget, ExtensionPackCountWidget as ExtensionPackBadgeWidget, SyncIgnoredWidget, ExtensionHoverWidget, ExtensionActivationStatusWidget, PreReleaseBookmarkWidget, extensionVerifiedPublisherIconColor } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets'; import { IExtensionService, toExtension } from 'vs/workbench/services/extensions/common/extensions'; @@ -120,7 +120,8 @@ export class Renderer implements IPagedRenderer { this.instantiationService.createInstance(ExtensionStatusLabelAction), this.instantiationService.createInstance(MigrateDeprecatedExtensionAction, true), reloadAction, - this.instantiationService.createInstance(UpdateAction), + this.instantiationService.createInstance(ActionWithDropDownAction, 'extensions.updateActions', '', + [[this.instantiationService.createInstance(UpdateAction)], [this.instantiationService.createInstance(SkipUpdateAction)]]), this.instantiationService.createInstance(InstallDropdownAction), this.instantiationService.createInstance(InstallingLabelAction), this.instantiationService.createInstance(SetLanguageAction), diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index fb9c4ee55ab..cf017e97f5e 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -16,7 +16,7 @@ import { append, $, Dimension, hide, show, DragAndDropObserver } from 'vs/base/b import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IExtensionsWorkbenchService, IExtensionsViewPaneContainer, VIEWLET_ID, CloseExtensionDetailsOnViewChangeKey, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, WORKSPACE_RECOMMENDATIONS_VIEW_ID, AutoCheckUpdatesConfigurationKey } from '../common/extensions'; +import { IExtensionsWorkbenchService, IExtensionsViewPaneContainer, VIEWLET_ID, CloseExtensionDetailsOnViewChangeKey, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, WORKSPACE_RECOMMENDATIONS_VIEW_ID, AutoCheckUpdatesConfigurationKey, OUTDATED_EXTENSIONS_VIEW_ID, CONTEXT_HAS_GALLERY } from '../common/extensions'; import { InstallLocalExtensionsInRemoteAction, InstallRemoteExtensionsInLocalAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; @@ -60,6 +60,7 @@ import { coalesce } from 'vs/base/common/arrays'; import { extractEditorsAndFilesDropData } from 'vs/platform/dnd/browser/dnd'; import { extname } from 'vs/base/common/resources'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { ILocalizedString } from 'vs/platform/action/common/action'; export const DefaultViewsContext = new RawContextKey('defaultExtensionViews', true); export const ExtensionsSortByContext = new RawContextKey('extensionsSortByValue', ''); @@ -79,6 +80,8 @@ const SearchDeprecatedExtensionsContext = new RawContextKey('searchDepr export const RecommendedExtensionsContext = new RawContextKey('recommendedExtensions', false); const SortByUpdateDateContext = new RawContextKey('sortByUpdateDate', false); +const REMOTE_CATEGORY: ILocalizedString = { value: localize({ key: 'remote', comment: ['Remote as in remote machine'] }, "Remote"), original: 'Remote' }; + export class ExtensionsViewletViewsContribution implements IWorkbenchContribution { private readonly container: ViewContainer; @@ -170,7 +173,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio original: `Install Local Extensions in '${server.label}'...`, }; }, - category: localize({ key: 'remote', comment: ['Remote as in remote machine'] }, "Remote"), + category: REMOTE_CATEGORY, icon: installLocalInRemoteIcon, f1: true, menu: { @@ -193,7 +196,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio super({ id: 'workbench.extensions.actions.installLocalExtensionsInRemote', title: { value: localize('install remote in local', "Install Remote Extensions Locally..."), original: 'Install Remote Extensions Locally...' }, - category: localize({ key: 'remote', comment: ['Remote as in remote machine'] }, "Remote"), + category: REMOTE_CATEGORY, f1: true }); } @@ -212,7 +215,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio id: 'workbench.views.extensions.popular', name: localize('popularExtensions', "Popular"), ctorDescriptor: new SyncDescriptor(DefaultPopularExtensionsView, [{ hideBadge: true }]), - when: ContextKeyExpr.and(DefaultViewsContext, ContextKeyExpr.not('hasInstalledExtensions')), + when: ContextKeyExpr.and(DefaultViewsContext, ContextKeyExpr.not('hasInstalledExtensions'), CONTEXT_HAS_GALLERY), weight: 60, order: 2, canToggleVisibility: false @@ -227,7 +230,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio id: 'extensions.recommendedList', name: localize('recommendedExtensions', "Recommended"), ctorDescriptor: new SyncDescriptor(DefaultRecommendedExtensionsView, [{ flexibleHeight: true }]), - when: ContextKeyExpr.and(DefaultViewsContext, SortByUpdateDateContext.negate(), ContextKeyExpr.not('config.extensions.showRecommendationsOnlyOnDemand')), + when: ContextKeyExpr.and(DefaultViewsContext, SortByUpdateDateContext.negate(), ContextKeyExpr.not('config.extensions.showRecommendationsOnlyOnDemand'), CONTEXT_HAS_GALLERY), weight: 40, order: 3, canToggleVisibility: true @@ -328,7 +331,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio * View used for searching outdated extensions */ viewDescriptors.push({ - id: 'workbench.views.extensions.searchOutdated', + id: OUTDATED_EXTENSIONS_VIEW_ID, name: localize('availableUpdates', "Available Updates"), ctorDescriptor: new SyncDescriptor(OutdatedExtensionsView, [{}]), when: ContextKeyExpr.or(SearchExtensionUpdatesContext, ContextKeyExpr.has('searchOutdatedExtensions')), @@ -800,25 +803,25 @@ export class StatusUpdater extends Disposable implements IWorkbenchContribution @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService ) { super(); - this._register(extensionsWorkbenchService.onChange(this.onServiceChange, this)); + this._register(Event.debounce(extensionsWorkbenchService.onChange, () => undefined, 100, undefined, undefined, this._store)(this.onServiceChange, this)); } private onServiceChange(): void { this.badgeHandle.clear(); const extensionsReloadRequired = this.extensionsWorkbenchService.installed.filter(e => e.reloadRequiredStatus !== undefined); - const outdated = this.extensionsWorkbenchService.outdated.reduce((r, e) => r + (this.extensionEnablementService.isEnabled(e.local!) && !extensionsReloadRequired.includes(e) ? 1 : 0), 0); + const outdated = this.extensionsWorkbenchService.outdated.reduce((r, e) => r + (this.extensionEnablementService.isEnabled(e.local!) && !this.extensionsWorkbenchService.isExtensionIgnoresUpdates(e) && !extensionsReloadRequired.includes(e) ? 1 : 0), 0); const newBadgeNumber = outdated + extensionsReloadRequired.length; if (newBadgeNumber > 0) { let msg = ''; if (outdated) { - msg += outdated === 1 ? localize('extensionToUpdate', '{0} Extension requires update', outdated) : localize('extensionsToUpdate', '{0} Extensions require update', outdated); + msg += outdated === 1 ? localize('extensionToUpdate', '{0} requires update', outdated) : localize('extensionsToUpdate', '{0} require update', outdated); } if (outdated > 0 && extensionsReloadRequired.length > 0) { msg += ', '; } if (extensionsReloadRequired.length) { - msg += extensionsReloadRequired.length === 1 ? localize('extensionToReload', '{0} Extension requires reload', extensionsReloadRequired.length) : localize('extensionsToReload', '{0} Extensions require reload', extensionsReloadRequired.length); + msg += extensionsReloadRequired.length === 1 ? localize('extensionToReload', '{0} requires reload', extensionsReloadRequired.length) : localize('extensionsToReload', '{0} require reload', extensionsReloadRequired.length); } const badge = new NumberBadge(newBadgeNumber, () => msg); this.badgeHandle.value = this.activityService.showViewContainerActivity(VIEWLET_ID, { badge, clazz: 'extensions-badge count-badge' }); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index ec3821e1885..575a220bc18 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -512,16 +512,18 @@ export class ExtensionsListView extends ViewPane { value = value.replace(/@installed/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase(); - let result = local - .filter(e => !e.isBuiltin - && (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1) - && (!categories.length || categories.some(category => (e.local && e.local.manifest.categories || []).some(c => c.toLowerCase() === category)))); + const matchingText = (e: IExtension) => (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1) + && (!categories.length || categories.some(category => (e.local && e.local.manifest.categories || []).some(c => c.toLowerCase() === category))); + let result; if (options.sortBy !== undefined) { + result = local.filter(e => !e.isBuiltin && matchingText(e)); result = this.sortExtensions(result, options); } else { + result = local.filter(e => (!e.isBuiltin || e.outdated || e.reloadRequiredStatus !== undefined) && matchingText(e)); const runningExtensionsById = runningExtensions.reduce((result, e) => { result.set(ExtensionIdentifier.toKey(e.identifier.value), e); return result; }, new Map()); - result = result.sort((e1, e2) => { + + const defaultSort = (e1: IExtension, e2: IExtension) => { const running1 = runningExtensionsById.get(ExtensionIdentifier.toKey(e1.identifier.id)); const isE1Running = !!running1 && this.extensionManagementServerService.getExtensionManagementServer(toExtension(running1)) === e1.server; const running2 = runningExtensionsById.get(ExtensionIdentifier.toKey(e2.identifier.id)); @@ -544,7 +546,24 @@ export class ExtensionsListView extends ViewPane { return e1.displayName.localeCompare(e2.displayName); } return isE1Running ? -1 : 1; + }; + + const outdated: IExtension[] = []; + const reloadRequired: IExtension[] = []; + const noActionRequired: IExtension[] = []; + result.forEach(e => { + if (e.outdated && !this.extensionsWorkbenchService.isExtensionIgnoresUpdates(e)) { + outdated.push(e); + } + else if (e.reloadRequiredStatus) { + reloadRequired.push(e); + } + else { + noActionRequired.push(e); + } }); + + result = [...outdated.sort(defaultSort), ...reloadRequired.sort(defaultSort), ...noActionRequired.sort(defaultSort)]; } return result; } @@ -663,7 +682,7 @@ export class ExtensionsListView extends ViewPane { private filterRecentlyUpdatedExtensions(local: IExtension[], query: Query, options: IQueryOptions): IExtension[] { let { value, categories } = this.parseCategories(query.value); const currentTime = Date.now(); - local = local.filter(e => !e.isBuiltin && e.local?.installedTimestamp !== undefined && currentTime - e.local.installedTimestamp < ExtensionsListView.RECENT_UPDATE_DURATION); + local = local.filter(e => !e.isBuiltin && !e.outdated && e.local?.updated && e.local?.installedTimestamp !== undefined && currentTime - e.local.installedTimestamp < ExtensionsListView.RECENT_UPDATE_DURATION); value = value.replace(/@recentlyUpdated/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase(); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 4dda6675f73..adc6daaa487 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -275,7 +275,7 @@ export class Extension implements IExtension { } get preview(): boolean { - return this.gallery ? this.gallery.preview : false; + return this.local?.manifest.preview ?? this.gallery?.preview ?? false; } get hasPreReleaseVersion(): boolean { @@ -894,38 +894,38 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension queryGallery(token: CancellationToken): Promise>; queryGallery(options: IQueryOptions, token: CancellationToken): Promise>; async queryGallery(arg1: any, arg2?: any): Promise> { + if (!this.galleryService.isEnabled()) { + return singlePagePager([]); + } + const options: IQueryOptions = CancellationToken.isCancellationToken(arg1) ? {} : arg1; const token: CancellationToken = CancellationToken.isCancellationToken(arg1) ? arg1 : arg2; options.text = options.text ? this.resolveQueryText(options.text) : options.text; options.includePreRelease = isUndefined(options.includePreRelease) ? this.preferPreReleases : options.includePreRelease; const extensionsControlManifest = await this.extensionManagementService.getExtensionsControlManifest(); - try { - const pager = await this.galleryService.query(options, token); - this.syncInstalledExtensionsWithGallery(pager.firstPage); - return { - firstPage: pager.firstPage.map(gallery => this.fromGallery(gallery, extensionsControlManifest)), - total: pager.total, - pageSize: pager.pageSize, - getPage: async (pageIndex, token) => { - const page = await pager.getPage(pageIndex, token); - this.syncInstalledExtensionsWithGallery(page); - return page.map(gallery => this.fromGallery(gallery, extensionsControlManifest)); - } - }; - } catch (error) { - if (/No extension gallery service configured/.test(error.message)) { - return Promise.resolve(singlePagePager([])); + const pager = await this.galleryService.query(options, token); + this.syncInstalledExtensionsWithGallery(pager.firstPage); + return { + firstPage: pager.firstPage.map(gallery => this.fromGallery(gallery, extensionsControlManifest)), + total: pager.total, + pageSize: pager.pageSize, + getPage: async (pageIndex, token) => { + const page = await pager.getPage(pageIndex, token); + this.syncInstalledExtensionsWithGallery(page); + return page.map(gallery => this.fromGallery(gallery, extensionsControlManifest)); } - throw error; - } + }; } getExtensions(extensionInfos: IExtensionInfo[], token: CancellationToken): Promise; getExtensions(extensionInfos: IExtensionInfo[], options: IExtensionQueryOptions, token: CancellationToken): Promise; async getExtensions(extensionInfos: IExtensionInfo[], arg1: any, arg2?: any): Promise { - extensionInfos.forEach(e => e.preRelease = e.preRelease ?? this.preferPreReleases); + if (!this.galleryService.isEnabled()) { + return []; + } + extensionInfos.forEach(e => e.preRelease = e.preRelease ?? this.preferPreReleases); const extensionsControlManifest = await this.extensionManagementService.getExtensionsControlManifest(); const galleryExtensions = await this.galleryService.getExtensions(extensionInfos, arg1, arg2); this.syncInstalledExtensionsWithGallery(galleryExtensions); @@ -1030,12 +1030,12 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension const extensionInOtherServer = this.installed.filter(e => areSameExtensions(e.identifier, extension!.identifier) && e.server !== extension!.server)[0]; if (extensionInOtherServer) { // This extension prefers to run on UI/Local side but is running in remote - if (runningExtensionServer === this.extensionManagementServerService.remoteExtensionManagementServer && this.extensionManifestPropertiesService.prefersExecuteOnUI(extension.local!.manifest)) { + if (runningExtensionServer === this.extensionManagementServerService.remoteExtensionManagementServer && this.extensionManifestPropertiesService.prefersExecuteOnUI(extension.local!.manifest) && extensionInOtherServer.server === this.extensionManagementServerService.localExtensionManagementServer) { return nls.localize('enable locally', "Please reload Visual Studio Code to enable this extension locally."); } // This extension prefers to run on Workspace/Remote side but is running in local - if (runningExtensionServer === this.extensionManagementServerService.localExtensionManagementServer && this.extensionManifestPropertiesService.prefersExecuteOnWorkspace(extension.local!.manifest)) { + if (runningExtensionServer === this.extensionManagementServerService.localExtensionManagementServer && this.extensionManifestPropertiesService.prefersExecuteOnWorkspace(extension.local!.manifest) && extensionInOtherServer.server === this.extensionManagementServerService.remoteExtensionManagementServer) { return nls.localize('enable remote', "Please reload Visual Studio Code to enable this extension in {0}.", this.extensionManagementServerService.remoteExtensionManagementServer?.label); } } @@ -1215,6 +1215,9 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension } async checkForUpdates(onlyBuiltin?: boolean): Promise { + if (!this.galleryService.isEnabled()) { + return; + } const extensions: Extensions[] = []; if (this.localExtensions) { extensions.push(this.localExtensions); @@ -1587,14 +1590,15 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension for (const extension of toCheck) { checked.push(extension); } - const extensionsToDisable = installed.filter(i => { + const extensionsToEanbleOrDisable = installed.filter(i => { if (checked.indexOf(i) !== -1) { return false; } - if (i.enablementState === enablementState) { + const enable = enablementState === EnablementState.EnabledGlobally || enablementState === EnablementState.EnabledWorkspace; + const isExtensionEnabled = i.enablementState === EnablementState.EnabledGlobally || i.enablementState === EnablementState.EnabledWorkspace; + if (enable === isExtensionEnabled) { return false; } - const enable = enablementState === EnablementState.EnabledGlobally || enablementState === EnablementState.EnabledWorkspace; return (enable || !i.isBuiltin) // Include all Extensions for enablement and only non builtin extensions for disablement && (options.dependencies || options.pack) && extensions.some(extension => @@ -1602,10 +1606,10 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension || (options.pack && extension.extensionPack.some(id => areSameExtensions({ id }, i.identifier))) ); }); - if (extensionsToDisable.length) { - extensionsToDisable.push(...this.getExtensionsRecursively(extensionsToDisable, installed, enablementState, options, checked)); + if (extensionsToEanbleOrDisable.length) { + extensionsToEanbleOrDisable.push(...this.getExtensionsRecursively(extensionsToEanbleOrDisable, installed, enablementState, options, checked)); } - return extensionsToDisable; + return extensionsToEanbleOrDisable; } return []; } @@ -1770,6 +1774,24 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension } } + setExtensionIgnoresUpdate(extension: IExtension, ignoreAutoUpate: boolean): void { + const extensionKey = new ExtensionKey(extension.identifier, extension.version); + if (ignoreAutoUpate) { + this.ignoreAutoUpdate(extensionKey); + } + else if (this.isAutoUpdateIgnored(extensionKey)) { + this.ignoredAutoUpdateExtensions = this.ignoredAutoUpdateExtensions.filter(extensionId => extensionId !== extensionKey.toString()); + } + else { + return; + } + this._onChange.fire(extension); + } + + isExtensionIgnoresUpdates(extension: IExtension): boolean { + return this.isAutoUpdateIgnored(new ExtensionKey(extension.identifier, extension.version)); + } + private isAutoUpdateIgnored(extensionKey: ExtensionKey): boolean { return this.ignoredAutoUpdateExtensions.indexOf(extensionKey.toString()) !== -1; } diff --git a/src/vs/workbench/contrib/extensions/common/extensions.ts b/src/vs/workbench/contrib/extensions/common/extensions.ts index 9b6ca4e0345..9b0e6b6afc2 100644 --- a/src/vs/workbench/contrib/extensions/common/extensions.ts +++ b/src/vs/workbench/contrib/extensions/common/extensions.ts @@ -111,6 +111,8 @@ export interface IExtensionsWorkbenchService { canSetLanguage(extension: IExtension): boolean; setLanguage(extension: IExtension): Promise; setEnablement(extensions: IExtension | IExtension[], enablementState: EnablementState): Promise; + setExtensionIgnoresUpdate(extension: IExtension, ignoreAutoUpate: boolean): void; + isExtensionIgnoresUpdates(extension: IExtension): boolean; open(extension: IExtension, options?: IExtensionEditorOptions): Promise; checkForUpdates(): Promise; getExtensionStatus(extension: IExtension): IExtensionsStatus | undefined; @@ -181,6 +183,7 @@ export class ExtensionContainers extends Disposable { } export const WORKSPACE_RECOMMENDATIONS_VIEW_ID = 'workbench.views.extensions.workspaceRecommendations'; +export const OUTDATED_EXTENSIONS_VIEW_ID = 'workbench.views.extensions.searchOutdated'; export const TOGGLE_IGNORE_EXTENSION_ACTION_ID = 'workbench.extensions.action.toggleIgnoreExtension'; export const SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID = 'workbench.extensions.action.installVSIX'; export const INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID = 'workbench.extensions.command.installFromVSIX'; @@ -189,6 +192,7 @@ export const LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID = 'workbench.exten // Context Keys export const HasOutdatedExtensionsContext = new RawContextKey('hasOutdatedExtensions', false); +export const CONTEXT_HAS_GALLERY = new RawContextKey('hasGallery', false); // Context Menu Groups export const THEME_ACTIONS_GROUP = '_theme_'; diff --git a/src/vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution.ts index 4af6ab2713f..eeb6f4d4c6b 100644 --- a/src/vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution.ts @@ -25,12 +25,12 @@ import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services import { ExtensionRecommendationNotificationServiceChannel } from 'vs/platform/extensionRecommendations/electron-sandbox/extensionRecommendationsIpc'; import { Codicon } from 'vs/base/common/codicons'; import { RemoteExtensionsInitializerContribution } from 'vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ExtensionHostProfileService } from 'vs/workbench/contrib/extensions/electron-sandbox/extensionProfileService'; import { ExtensionsAutoProfiler } from 'vs/workbench/contrib/extensions/electron-sandbox/extensionsAutoProfiler'; // Singletons -registerSingleton(IExtensionHostProfileService, ExtensionHostProfileService, true); +registerSingleton(IExtensionHostProfileService, ExtensionHostProfileService, InstantiationType.Delayed); // Running Extensions Editor Registry.as(EditorExtensions.EditorPane).registerEditorPane( @@ -67,9 +67,9 @@ class ExtensionsContributions implements IWorkbenchContribution { } const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(ExtensionsContributions, 'ExtensionsContributions', LifecyclePhase.Restored); -workbenchRegistry.registerWorkbenchContribution(ExtensionsAutoProfiler, 'ExtensionsAutoProfiler', LifecyclePhase.Eventually); -workbenchRegistry.registerWorkbenchContribution(RemoteExtensionsInitializerContribution, 'RemoteExtensionsInitializerContribution', LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(ExtensionsContributions, LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(ExtensionsAutoProfiler, LifecyclePhase.Eventually); +workbenchRegistry.registerWorkbenchContribution(RemoteExtensionsInitializerContribution, LifecyclePhase.Restored); // Register Commands CommandsRegistry.registerCommand(DebugExtensionHostAction.ID, (accessor: ServicesAccessor) => { diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts index 84f36953148..bc185062083 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts @@ -68,7 +68,7 @@ let installEvent: Emitter, let disposables: DisposableStore; -async function setupTest() { +function setupTest() { disposables = new DisposableStore(); installEvent = new Emitter(); didInstallEvent = new Emitter(); @@ -136,9 +136,10 @@ async function setupTest() { instantiationService.stub(IExtensionRecommendationsService, {}); instantiationService.stub(IURLService, NativeURLService); + instantiationService.stub(IExtensionGalleryService, 'isEnabled', true); instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage()); instantiationService.stubPromise(IExtensionGalleryService, 'getExtensions', []); - instantiationService.stub(IExtensionService, >{ extensions: [], onDidChangeExtensions: Event.None, canAddExtension: (extension: IExtensionDescription) => false, canRemoveExtension: (extension: IExtensionDescription) => false }); + instantiationService.stub(IExtensionService, >{ extensions: [], onDidChangeExtensions: Event.None, canAddExtension: (extension: IExtensionDescription) => false, canRemoveExtension: (extension: IExtensionDescription) => false, whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); (instantiationService.get(IWorkbenchExtensionEnablementService)).reset(); instantiationService.stub(IUserDataSyncEnablementService, instantiationService.createInstance(UserDataSyncEnablementService)); @@ -415,11 +416,22 @@ suite('ExtensionsActions', () => { instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); instantiationService.stubPromise(IExtensionGalleryService, 'getCompatibleExtension', gallery); instantiationService.stubPromise(IExtensionGalleryService, 'getExtensions', [gallery]); - await instantiationService.get(IExtensionsWorkbenchService).queryGallery(CancellationToken.None); - const promise = Event.toPromise(testObject.onDidChange); - installEvent.fire({ identifier: local.identifier, source: gallery }); - await promise; - assert.ok(!testObject.enabled); + await new Promise(c => { + testObject.onDidChange(() => { + if (testObject.enabled) { + c(); + } + }); + instantiationService.get(IExtensionsWorkbenchService).queryGallery(CancellationToken.None); + }); + await new Promise(c => { + testObject.onDidChange(() => { + if (!testObject.enabled) { + c(); + } + }); + installEvent.fire({ identifier: local.identifier, source: gallery }); + }); }); test('Test ManageExtensionAction when there is no extension', () => { @@ -783,6 +795,7 @@ suite('ExtensionsActions', () => { instantiationService.stub(IExtensionService, >{ extensions: [toExtensionDescription(local)], onDidChangeExtensions: Event.None, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); @@ -837,6 +850,7 @@ suite('ExtensionsActions', () => { instantiationService.stub(IExtensionService, >{ extensions: [toExtensionDescription(local)], onDidChangeExtensions: Event.None, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); return instantiationService.get(IExtensionsWorkbenchService).queryLocal() @@ -853,6 +867,7 @@ suite('ExtensionsActions', () => { instantiationService.stub(IExtensionService, >{ extensions: [toExtensionDescription(local)], onDidChangeExtensions: Event.None, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); return instantiationService.get(IExtensionsWorkbenchService).queryLocal() @@ -868,6 +883,7 @@ suite('ExtensionsActions', () => { instantiationService.stub(IExtensionService, >{ extensions: [toExtensionDescription(local)], onDidChangeExtensions: Event.None, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); return instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([local], EnablementState.DisabledGlobally) @@ -889,6 +905,7 @@ suite('ExtensionsActions', () => { instantiationService.stub(IExtensionService, >{ extensions: [toExtensionDescription(aLocalExtension('a'))], onDidChangeExtensions: Event.None, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); return instantiationService.get(IExtensionsWorkbenchService).queryGallery(CancellationToken.None) @@ -905,6 +922,7 @@ suite('ExtensionsActions', () => { instantiationService.stub(IExtensionService, >{ extensions: [toExtensionDescription(aLocalExtension('a'))], onDidChangeExtensions: Event.None, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); return instantiationService.get(IExtensionsWorkbenchService).queryGallery(CancellationToken.None) @@ -923,6 +941,7 @@ suite('ExtensionsActions', () => { instantiationService.stub(IExtensionService, >{ extensions: [toExtensionDescription(local)], onDidChangeExtensions: Event.None, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); return instantiationService.get(IExtensionsWorkbenchService).queryLocal() @@ -979,7 +998,8 @@ suite('ReloadAction', () => { instantiationService.stub(IExtensionService, >{ extensions: [toExtensionDescription(aLocalExtension('b'))], onDidChangeExtensions: onDidChangeExtensionsEmitter.event, - canAddExtension: (extension) => false + canAddExtension: (extension) => false, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); instantiationService.createInstance(ExtensionContainers, [testObject]); @@ -1003,7 +1023,8 @@ suite('ReloadAction', () => { instantiationService.stub(IExtensionService, >{ extensions: [toExtensionDescription(aLocalExtension('b'))], onDidChangeExtensions: onDidChangeExtensionsEmitter.event, - canAddExtension: (extension) => true + canAddExtension: (extension) => true, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); instantiationService.createInstance(ExtensionContainers, [testObject]); @@ -1024,7 +1045,8 @@ suite('ReloadAction', () => { extensions: [toExtensionDescription(aLocalExtension('b'))], onDidChangeExtensions: Event.None, canRemoveExtension: (extension) => false, - canAddExtension: (extension) => false + canAddExtension: (extension) => false, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); instantiationService.createInstance(ExtensionContainers, [testObject]); @@ -1047,7 +1069,8 @@ suite('ReloadAction', () => { extensions: [toExtensionDescription(aLocalExtension('a', { version: '1.0.0' }))], onDidChangeExtensions: Event.None, canRemoveExtension: (extension) => false, - canAddExtension: (extension) => false + canAddExtension: (extension) => false, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); instantiationService.set(IExtensionsWorkbenchService, instantiationService.createInstance(ExtensionsWorkbenchService)); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); @@ -1069,7 +1092,8 @@ suite('ReloadAction', () => { extensions: [toExtensionDescription(local)], onDidChangeExtensions: Event.None, canRemoveExtension: (extension) => true, - canAddExtension: (extension) => true + canAddExtension: (extension) => true, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); instantiationService.createInstance(ExtensionContainers, [testObject]); @@ -1087,7 +1111,8 @@ suite('ReloadAction', () => { extensions: [toExtensionDescription(aLocalExtension('a', { version: '1.0.0' }))], onDidChangeExtensions: Event.None, canRemoveExtension: (extension) => false, - canAddExtension: (extension) => false + canAddExtension: (extension) => false, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); instantiationService.createInstance(ExtensionContainers, [testObject]); @@ -1112,7 +1137,8 @@ suite('ReloadAction', () => { extensions: [toExtensionDescription(aLocalExtension('a', { version: '1.0.1' }))], onDidChangeExtensions: Event.None, canRemoveExtension: (extension) => true, - canAddExtension: (extension) => false + canAddExtension: (extension) => false, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); instantiationService.set(IExtensionsWorkbenchService, instantiationService.createInstance(ExtensionsWorkbenchService)); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); @@ -1140,7 +1166,8 @@ suite('ReloadAction', () => { extensions: [toExtensionDescription(aLocalExtension('b'))], onDidChangeExtensions: Event.None, canRemoveExtension: (extension) => false, - canAddExtension: (extension) => false + canAddExtension: (extension) => false, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); const local = aLocalExtension('a', { version: '1.0.1' }); await instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([local], EnablementState.DisabledGlobally); @@ -1163,7 +1190,8 @@ suite('ReloadAction', () => { extensions: [toExtensionDescription(aLocalExtension('a'))], onDidChangeExtensions: Event.None, canRemoveExtension: (extension) => false, - canAddExtension: (extension) => false + canAddExtension: (extension) => false, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); instantiationService.set(IExtensionsWorkbenchService, instantiationService.createInstance(ExtensionsWorkbenchService)); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); @@ -1185,7 +1213,8 @@ suite('ReloadAction', () => { extensions: [toExtensionDescription(aLocalExtension('a', { version: '1.0.0' }))], onDidChangeExtensions: Event.None, canRemoveExtension: (extension) => false, - canAddExtension: (extension) => false + canAddExtension: (extension) => false, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); instantiationService.set(IExtensionsWorkbenchService, instantiationService.createInstance(ExtensionsWorkbenchService)); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); @@ -1205,7 +1234,8 @@ suite('ReloadAction', () => { extensions: [toExtensionDescription(aLocalExtension('b'))], onDidChangeExtensions: Event.None, canRemoveExtension: (extension) => false, - canAddExtension: (extension) => false + canAddExtension: (extension) => false, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); const local = aLocalExtension('a'); await instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([local], EnablementState.DisabledGlobally); @@ -1226,7 +1256,8 @@ suite('ReloadAction', () => { extensions: [toExtensionDescription(aLocalExtension('b'))], onDidChangeExtensions: Event.None, canRemoveExtension: (extension) => false, - canAddExtension: (extension) => false + canAddExtension: (extension) => false, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); const local = aLocalExtension('a'); await instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([local], EnablementState.DisabledGlobally); @@ -1246,7 +1277,8 @@ suite('ReloadAction', () => { extensions: [toExtensionDescription(aLocalExtension('a'))], onDidChangeExtensions: Event.None, canRemoveExtension: (extension) => false, - canAddExtension: (extension) => false + canAddExtension: (extension) => false, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); const local = aLocalExtension('a', { version: '1.0.1' }); await instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([local], EnablementState.DisabledGlobally); @@ -1271,7 +1303,8 @@ suite('ReloadAction', () => { extensions: [toExtensionDescription(aLocalExtension('b'))], onDidChangeExtensions: Event.None, canRemoveExtension: (extension) => false, - canAddExtension: (extension) => false + canAddExtension: (extension) => false, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); instantiationService.createInstance(ExtensionContainers, [testObject]); @@ -1292,7 +1325,8 @@ suite('ReloadAction', () => { extensions: [toExtensionDescription(aLocalExtension('a', { version: '1.0.1' }))], onDidChangeExtensions: Event.None, canRemoveExtension: (extension) => false, - canAddExtension: (extension) => false + canAddExtension: (extension) => false, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); instantiationService.createInstance(ExtensionContainers, [testObject]); @@ -1320,7 +1354,8 @@ suite('ReloadAction', () => { instantiationService.stub(IExtensionService, >{ extensions: [toExtensionDescription(remoteExtension)], onDidChangeExtensions: onDidChangeExtensionsEmitter.event, - canAddExtension: (extension) => false + canAddExtension: (extension) => false, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService); instantiationService.set(IExtensionsWorkbenchService, workbenchService); @@ -1353,7 +1388,8 @@ suite('ReloadAction', () => { instantiationService.stub(IExtensionService, >{ extensions: [toExtensionDescription(remoteExtension)], onDidChangeExtensions: onDidChangeExtensionsEmitter.event, - canAddExtension: (extension) => false + canAddExtension: (extension) => false, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService); instantiationService.set(IExtensionsWorkbenchService, workbenchService); @@ -1391,7 +1427,8 @@ suite('ReloadAction', () => { instantiationService.stub(IExtensionService, >{ extensions: [], onDidChangeExtensions: onDidChangeExtensionsEmitter.event, - canAddExtension: (extension) => false + canAddExtension: (extension) => false, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); instantiationService.createInstance(ExtensionContainers, [testObject]); @@ -1429,7 +1466,8 @@ suite('ReloadAction', () => { instantiationService.stub(IExtensionService, >{ extensions: [], onDidChangeExtensions: onDidChangeExtensionsEmitter.event, - canAddExtension: (extension) => false + canAddExtension: (extension) => false, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); instantiationService.createInstance(ExtensionContainers, [testObject]); @@ -1465,7 +1503,8 @@ suite('ReloadAction', () => { instantiationService.stub(IExtensionService, >{ extensions: [toExtensionDescription(localExtension)], onDidChangeExtensions: onDidChangeExtensionsEmitter.event, - canAddExtension: (extension) => false + canAddExtension: (extension) => false, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService); instantiationService.set(IExtensionsWorkbenchService, workbenchService); @@ -1499,7 +1538,8 @@ suite('ReloadAction', () => { instantiationService.stub(IExtensionService, >{ extensions: [toExtensionDescription(localExtension)], onDidChangeExtensions: onDidChangeExtensionsEmitter.event, - canAddExtension: (extension) => false + canAddExtension: (extension) => false, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); instantiationService.createInstance(ExtensionContainers, [testObject]); @@ -1530,7 +1570,8 @@ suite('ReloadAction', () => { instantiationService.stub(IExtensionService, >{ extensions: [toExtensionDescription(remoteExtension)], onDidChangeExtensions: onDidChangeExtensionsEmitter.event, - canAddExtension: (extension) => false + canAddExtension: (extension) => false, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); instantiationService.createInstance(ExtensionContainers, [testObject]); @@ -1561,7 +1602,8 @@ suite('ReloadAction', () => { instantiationService.stub(IExtensionService, >{ extensions: [toExtensionDescription(localExtension)], onDidChangeExtensions: onDidChangeExtensionsEmitter.event, - canAddExtension: (extension) => false + canAddExtension: (extension) => false, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); instantiationService.createInstance(ExtensionContainers, [testObject]); @@ -1592,7 +1634,8 @@ suite('ReloadAction', () => { instantiationService.stub(IExtensionService, >{ extensions: [toExtensionDescription(remoteExtension)], onDidChangeExtensions: onDidChangeExtensionsEmitter.event, - canAddExtension: (extension) => false + canAddExtension: (extension) => false, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); instantiationService.createInstance(ExtensionContainers, [testObject]); @@ -1604,6 +1647,62 @@ suite('ReloadAction', () => { assert.ok(testObject.extension); assert.ok(testObject.enabled); }); + + test('Test ReloadAction when ui+workspace+web extension is installed in web and remote and running in remote', async () => { + // multi server setup + const gallery = aGalleryExtension('a'); + const webExtension = aLocalExtension('a', { extensionKind: ['ui', 'workspace'], 'browser': 'browser.js' }, { location: URI.file('pub.a').with({ scheme: Schemas.vscodeUserData }) }); + const remoteExtension = aLocalExtension('a', { extensionKind: ['ui', 'workspace'], 'browser': 'browser.js' }, { location: URI.file('pub.a').with({ scheme: Schemas.vscodeRemote }) }); + const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, null, createExtensionManagementService([remoteExtension]), createExtensionManagementService([webExtension])); + instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); + instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + instantiationService.stub(IExtensionService, >{ + extensions: [toExtensionDescription(remoteExtension)], + onDidChangeExtensions: Event.None, + canAddExtension: (extension) => false, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) + }); + const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService); + instantiationService.set(IExtensionsWorkbenchService, workbenchService); + + const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); + instantiationService.createInstance(ExtensionContainers, [testObject]); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); + + await workbenchService.queryGallery(CancellationToken.None); + const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!); + testObject.extension = extensions[0]; + assert.ok(testObject.extension); + assert.ok(!testObject.enabled); + }); + + test('Test ReloadAction when workspace+ui+web extension is installed in web and local and running in local', async () => { + // multi server setup + const gallery = aGalleryExtension('a'); + const webExtension = aLocalExtension('a', { extensionKind: ['workspace', 'ui'], 'browser': 'browser.js' }, { location: URI.file('pub.a').with({ scheme: Schemas.vscodeUserData }) }); + const localExtension = aLocalExtension('a', { extensionKind: ['workspace', 'ui'], 'browser': 'browser.js' }, { location: URI.file('pub.a') }); + const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localExtension]), null, createExtensionManagementService([webExtension])); + instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); + instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + instantiationService.stub(IExtensionService, >{ + extensions: [toExtensionDescription(localExtension)], + onDidChangeExtensions: Event.None, + canAddExtension: (extension) => false, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) + }); + const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService); + instantiationService.set(IExtensionsWorkbenchService, workbenchService); + + const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); + instantiationService.createInstance(ExtensionContainers, [testObject]); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); + + await workbenchService.queryGallery(CancellationToken.None); + const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!); + testObject.extension = extensions[0]; + assert.ok(testObject.extension); + assert.ok(!testObject.enabled); + }); }); suite('RemoteInstallAction', () => { @@ -2477,22 +2576,27 @@ function aSingleRemoteExtensionManagementServerService(instantiationService: Tes }; } -function aMultiExtensionManagementServerService(instantiationService: TestInstantiationService, localExtensionManagementService?: IProfileAwareExtensionManagementService, remoteExtensionManagementService?: IProfileAwareExtensionManagementService): IExtensionManagementServerService { - const localExtensionManagementServer: IExtensionManagementServer = { +function aMultiExtensionManagementServerService(instantiationService: TestInstantiationService, localExtensionManagementService?: IProfileAwareExtensionManagementService | null, remoteExtensionManagementService?: IProfileAwareExtensionManagementService | null, webExtensionManagementService?: IProfileAwareExtensionManagementService): IExtensionManagementServerService { + const localExtensionManagementServer: IExtensionManagementServer | null = localExtensionManagementService === null ? null : { id: 'vscode-local', label: 'local', extensionManagementService: localExtensionManagementService || createExtensionManagementService(), }; - const remoteExtensionManagementServer: IExtensionManagementServer = { + const remoteExtensionManagementServer: IExtensionManagementServer | null = remoteExtensionManagementService === null ? null : { id: 'vscode-remote', label: 'remote', extensionManagementService: remoteExtensionManagementService || createExtensionManagementService(), }; + const webExtensionManagementServer: IExtensionManagementServer | null = webExtensionManagementService ? { + id: 'vscode-web', + label: 'web', + extensionManagementService: webExtensionManagementService, + } : null; return { _serviceBrand: undefined, localExtensionManagementServer, remoteExtensionManagementServer, - webExtensionManagementServer: null, + webExtensionManagementServer, getExtensionManagementServer: (extension: IExtension) => { if (extension.location.scheme === Schemas.file) { return localExtensionManagementServer; @@ -2500,11 +2604,23 @@ function aMultiExtensionManagementServerService(instantiationService: TestInstan if (extension.location.scheme === Schemas.vscodeRemote) { return remoteExtensionManagementServer; } + if (extension.location.scheme === Schemas.vscodeUserData) { + return webExtensionManagementServer; + } throw new Error(''); }, getExtensionInstallLocation(extension: IExtension): ExtensionInstallLocation | null { const server = this.getExtensionManagementServer(extension); - return server === remoteExtensionManagementServer ? ExtensionInstallLocation.Remote : ExtensionInstallLocation.Local; + if (server === null) { + return null; + } + if (server === remoteExtensionManagementServer) { + return ExtensionInstallLocation.Remote; + } + if (server === webExtensionManagementServer) { + return ExtensionInstallLocation.Web; + } + return ExtensionInstallLocation.Local; } }; } diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts index d513feae320..ef14de7b97c 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts @@ -47,8 +47,9 @@ import { Schemas } from 'vs/base/common/network'; import { platform } from 'vs/base/common/platform'; import { arch } from 'vs/base/common/process'; import { IProductService } from 'vs/platform/product/common/productService'; +import { CancellationToken } from 'vs/base/common/cancellation'; -suite('ExtensionsListView Tests', () => { +suite('ExtensionsViews Tests', () => { let instantiationService: TestInstantiationService; let testableView: ExtensionsListView; @@ -58,13 +59,15 @@ suite('ExtensionsListView Tests', () => { didUninstallEvent: Emitter; const localEnabledTheme = aLocalExtension('first-enabled-extension', { categories: ['Themes', 'random'] }, { installedTimestamp: 123456 }); - const localEnabledLanguage = aLocalExtension('second-enabled-extension', { categories: ['Programming languages'] }, { installedTimestamp: Date.now() }); + const localEnabledLanguage = aLocalExtension('second-enabled-extension', { categories: ['Programming languages'], version: '1.0.0' }, { installedTimestamp: Date.now(), updated: false }); const localDisabledTheme = aLocalExtension('first-disabled-extension', { categories: ['themes'] }, { installedTimestamp: 234567 }); - const localDisabledLanguage = aLocalExtension('second-disabled-extension', { categories: ['programming languages'] }, { installedTimestamp: Date.now() - 50000 }); + const localDisabledLanguage = aLocalExtension('second-disabled-extension', { categories: ['programming languages'] }, { installedTimestamp: Date.now() - 50000, updated: true }); const localRandom = aLocalExtension('random-enabled-extension', { categories: ['random'] }, { installedTimestamp: 345678 }); const builtInTheme = aLocalExtension('my-theme', { contributes: { themes: ['my-theme'] } }, { type: ExtensionType.System, installedTimestamp: 222 }); const builtInBasic = aLocalExtension('my-lang', { contributes: { grammars: [{ language: 'my-language' }] } }, { type: ExtensionType.System, installedTimestamp: 666666 }); + const galleryEnabledLanguage = aGalleryExtension(localEnabledLanguage.manifest.name, { ...localEnabledLanguage.manifest, version: '1.0.1', identifier: localDisabledLanguage.identifier }); + const workspaceRecommendationA = aGalleryExtension('workspace-recommendation-A'); const workspaceRecommendationB = aGalleryExtension('workspace-recommendation-B'); const configBasedRecommendationA = aGalleryExtension('configbased-recommendation-A'); @@ -100,7 +103,8 @@ suite('ExtensionsListView Tests', () => { async getInstalled() { return []; }, async canInstall() { return true; }, async getExtensionsControlManifest() { return { malicious: [], deprecated: {} }; }, - async getTargetPlatform() { return getTargetPlatform(platform, arch); } + async getTargetPlatform() { return getTargetPlatform(platform, arch); }, + async updateMetadata(local) { return local; } }); instantiationService.stub(IRemoteAgentService, RemoteAgentService); instantiationService.stub(IContextKeyService, new MockContextKeyService()); @@ -165,8 +169,10 @@ suite('ExtensionsListView Tests', () => { setup(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [localEnabledTheme, localEnabledLanguage, localRandom, localDisabledTheme, localDisabledLanguage, builtInTheme, builtInBasic]); instantiationService.stubPromise(IExtensionManagementService, 'getExtensgetExtensionsControlManifestionsReport', {}); - instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage()); - instantiationService.stubPromise(IExtensionGalleryService, 'getExtensions', []); + instantiationService.stub(IExtensionGalleryService, 'isEnabled', true); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(galleryEnabledLanguage)); + instantiationService.stubPromise(IExtensionGalleryService, 'getCompatibleExtension', galleryEnabledLanguage); + instantiationService.stubPromise(IExtensionGalleryService, 'getExtensions', [galleryEnabledLanguage]); instantiationService.stubPromise(IExperimentService, 'getExperimentsByType', []); instantiationService.stub(IViewDescriptorService, { @@ -184,7 +190,9 @@ suite('ExtensionsListView Tests', () => { toExtensionDescription(localRandom), toExtensionDescription(builtInTheme), toExtensionDescription(builtInBasic) - ] + ], + canAddExtension: (extension) => true, + whenInstalledExtensionsRegistered: () => Promise.resolve(true) }); await (instantiationService.get(IWorkbenchExtensionEnablementService)).setEnablement([localDisabledTheme], EnablementState.DisabledGlobally); await (instantiationService.get(IWorkbenchExtensionEnablementService)).setEnablement([localDisabledLanguage], EnablementState.DisabledGlobally); @@ -241,6 +249,29 @@ suite('ExtensionsListView Tests', () => { }); }); + test('Test default view actions required sorting', async () => { + const workbenchService = instantiationService.get(IExtensionsWorkbenchService); + const extension = (await workbenchService.queryLocal()).find(ex => ex.identifier === localEnabledLanguage.identifier); + + await new Promise(c => { + const disposable = workbenchService.onChange(() => { + if (extension?.outdated) { + disposable.dispose(); + c(); + } + }); + instantiationService.get(IExtensionsWorkbenchService).queryGallery(CancellationToken.None); + }); + + const result = await testableView.show('@installed'); + assert.strictEqual(result.length, 5, 'Unexpected number of results for @installed query'); + const actual = [result.get(0).name, result.get(1).name, result.get(2).name, result.get(3).name, result.get(4).name]; + const expected = [localEnabledLanguage.manifest.name, localEnabledTheme.manifest.name, localRandom.manifest.name, localDisabledTheme.manifest.name, localDisabledLanguage.manifest.name]; + for (let i = 0; i < result.length; i++) { + assert.strictEqual(actual[i], expected[i], 'Unexpected extension for @installed query with outadted extension.'); + } + }); + test('Test installed query results', async () => { await testableView.show('@installed').then(result => { assert.strictEqual(result.length, 5, 'Unexpected number of results for @installed query'); @@ -351,12 +382,8 @@ suite('ExtensionsListView Tests', () => { test('Test local query with sorting order', async () => { await testableView.show('@recentlyUpdated').then(result => { - assert.strictEqual(result.length, 2, 'Unexpected number of results for @recentlyUpdated'); - const actual = [result.get(0).name, result.get(1).name]; - const expected = [localEnabledLanguage.manifest.name, localDisabledLanguage.manifest.name]; - for (let i = 0; i < actual.length; i++) { - assert.strictEqual(actual[i], expected[i], 'Unexpected default sort order of extensions for @recentlyUpdate query'); - } + assert.strictEqual(result.length, 1, 'Unexpected number of results for @recentlyUpdated'); + assert.strictEqual(result.get(0).name, localDisabledLanguage.manifest.name, 'Unexpected default sort order of extensions for @recentlyUpdate query'); }); await testableView.show('@installed @sort:updateDate').then(result => { @@ -398,7 +425,7 @@ suite('ExtensionsListView Tests', () => { const target = instantiationService.stubPromise(IExtensionGalleryService, 'getExtensions', allRecommendedExtensions); return testableView.show('@recommended').then(result => { - const extensionInfos: IExtensionInfo[] = target.args[0][0]; + const extensionInfos: IExtensionInfo[] = target.args[1][0]; assert.strictEqual(extensionInfos.length, allRecommendedExtensions.length); assert.strictEqual(result.length, allRecommendedExtensions.length); @@ -423,7 +450,7 @@ suite('ExtensionsListView Tests', () => { const target = instantiationService.stubPromise(IExtensionGalleryService, 'getExtensions', allRecommendedExtensions); return testableView.show('@recommended:all').then(result => { - const extensionInfos: IExtensionInfo[] = target.args[0][0]; + const extensionInfos: IExtensionInfo[] = target.args[1][0]; assert.strictEqual(extensionInfos.length, allRecommendedExtensions.length); assert.strictEqual(result.length, allRecommendedExtensions.length); diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts index f862b70fb53..d3fa2255b1b 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts @@ -132,6 +132,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { setup(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', []); + instantiationService.stub(IExtensionGalleryService, 'isEnabled', true); instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage()); instantiationService.stubPromise(IExtensionGalleryService, 'getExtensions', []); instantiationService.stubPromise(INotificationService, 'prompt', 0); diff --git a/src/vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution.ts b/src/vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution.ts index fdb07a0941f..b1cdf5d227a 100644 --- a/src/vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution.ts +++ b/src/vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution.ts @@ -136,4 +136,4 @@ export class ExternalTerminalContribution extends Disposable implements IWorkben } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ExternalTerminalContribution, 'ExternalTerminalContribution', LifecyclePhase.Restored); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ExternalTerminalContribution, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/externalTerminal/electron-sandbox/externalTerminal.contribution.ts b/src/vs/workbench/contrib/externalTerminal/electron-sandbox/externalTerminal.contribution.ts index e0759e2b781..e910ea0b127 100644 --- a/src/vs/workbench/contrib/externalTerminal/electron-sandbox/externalTerminal.contribution.ts +++ b/src/vs/workbench/contrib/externalTerminal/electron-sandbox/externalTerminal.contribution.ts @@ -135,4 +135,4 @@ export class ExternalTerminalContribution implements IWorkbenchContribution { // Register workbench contributions const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(ExternalTerminalContribution, 'ExternalTerminalContribution', LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(ExternalTerminalContribution, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/feedback/browser/feedback.contribution.ts b/src/vs/workbench/contrib/feedback/browser/feedback.contribution.ts index 238e4095817..94485ad4969 100644 --- a/src/vs/workbench/contrib/feedback/browser/feedback.contribution.ts +++ b/src/vs/workbench/contrib/feedback/browser/feedback.contribution.ts @@ -8,4 +8,4 @@ import { FeedbackStatusbarConribution } from 'vs/workbench/contrib/feedback/brow import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(FeedbackStatusbarConribution, 'FeedbackStatusbarConribution', LifecyclePhase.Starting); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(FeedbackStatusbarConribution, LifecyclePhase.Starting); diff --git a/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts b/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts index 1c3145bffc1..aaed0100585 100644 --- a/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts +++ b/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts @@ -14,7 +14,7 @@ import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/c import { IOpenerService } from 'vs/platform/opener/common/opener'; import { URI } from 'vs/base/common/uri'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { HIDE_NOTIFICATIONS_CENTER, HIDE_NOTIFICATION_TOAST } from 'vs/workbench/browser/parts/notifications/notificationsCommands'; import { isIOS } from 'vs/base/common/platform'; @@ -80,7 +80,7 @@ export class FeedbackStatusbarConribution extends Disposable implements IWorkben MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: FeedbackStatusbarConribution.TOGGLE_FEEDBACK_COMMAND, - category: CATEGORIES.Help, + category: Categories.Help, title: localize('status.feedback', "Tweet Feedback") } }); diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index 57bb796dfc4..5ebfe7c291c 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -4,13 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { ToggleAutoSaveAction, FocusFilesExplorer, GlobalCompareResourcesAction, ShowActiveFileInExplorer, CompareWithClipboardAction, NEW_FILE_COMMAND_ID, NEW_FILE_LABEL, NEW_FOLDER_COMMAND_ID, NEW_FOLDER_LABEL, TRIGGER_RENAME_LABEL, MOVE_FILE_TO_TRASH_LABEL, COPY_FILE_LABEL, PASTE_FILE_LABEL, FileCopiedContext, renameHandler, moveFileToTrashHandler, copyFileHandler, pasteFileHandler, deleteFileHandler, cutFileHandler, DOWNLOAD_COMMAND_ID, openFilePreserveFocusHandler, DOWNLOAD_LABEL, ShowOpenedFileInNewWindow, UPLOAD_COMMAND_ID, UPLOAD_LABEL } from 'vs/workbench/contrib/files/browser/fileActions'; +import { ToggleAutoSaveAction, FocusFilesExplorer, GlobalCompareResourcesAction, ShowActiveFileInExplorer, CompareWithClipboardAction, NEW_FILE_COMMAND_ID, NEW_FILE_LABEL, NEW_FOLDER_COMMAND_ID, NEW_FOLDER_LABEL, TRIGGER_RENAME_LABEL, MOVE_FILE_TO_TRASH_LABEL, COPY_FILE_LABEL, PASTE_FILE_LABEL, FileCopiedContext, renameHandler, moveFileToTrashHandler, copyFileHandler, pasteFileHandler, deleteFileHandler, cutFileHandler, DOWNLOAD_COMMAND_ID, openFilePreserveFocusHandler, DOWNLOAD_LABEL, ShowOpenedFileInNewWindow, UPLOAD_COMMAND_ID, UPLOAD_LABEL, fileCategory } from 'vs/workbench/contrib/files/browser/fileActions'; import { revertLocalChangesCommand, acceptLocalChangesCommand, CONFLICT_RESOLUTION_CONTEXT } from 'vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler'; -import { SyncActionDescriptor, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; +import { MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; import { ILocalizedString } from 'vs/platform/action/common/action'; -import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; -import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { openWindowCommand, newWindowCommand } from 'vs/workbench/contrib/files/browser/fileCommands'; import { COPY_PATH_COMMAND_ID, REVEAL_IN_EXPLORER_COMMAND_ID, OPEN_TO_SIDE_COMMAND_ID, REVERT_FILE_COMMAND_ID, SAVE_FILE_COMMAND_ID, SAVE_FILE_LABEL, SAVE_FILE_AS_COMMAND_ID, SAVE_FILE_AS_LABEL, SAVE_ALL_IN_GROUP_COMMAND_ID, OpenEditorsGroupContext, COMPARE_WITH_SAVED_COMMAND_ID, COMPARE_RESOURCE_COMMAND_ID, SELECT_FOR_COMPARE_COMMAND_ID, ResourceSelectedForCompareContext, OpenEditorsDirtyEditorContext, COMPARE_SELECTED_COMMAND_ID, REMOVE_ROOT_FOLDER_COMMAND_ID, REMOVE_ROOT_FOLDER_LABEL, SAVE_FILES_COMMAND_ID, COPY_RELATIVE_PATH_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_LABEL, OpenEditorsReadonlyEditorContext, OPEN_WITH_EXPLORER_COMMAND_ID, NEW_UNTITLED_FILE_COMMAND_ID, NEW_UNTITLED_FILE_LABEL, SAVE_ALL_COMMAND_ID } from 'vs/workbench/contrib/files/browser/fileConstants'; import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands'; @@ -18,11 +16,11 @@ import { ContextKeyExpr, ContextKeyExpression } from 'vs/platform/contextkey/com import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { FilesExplorerFocusCondition, ExplorerRootContext, ExplorerFolderContext, ExplorerResourceNotReadonlyContext, ExplorerResourceCut, ExplorerResourceMoveableToTrash, ExplorerViewletVisibleContext, ExplorerResourceAvailableEditorIdsContext } from 'vs/workbench/contrib/files/common/files'; import { ADD_ROOT_FOLDER_COMMAND_ID, ADD_ROOT_FOLDER_LABEL } from 'vs/workbench/browser/actions/workspaceCommands'; -import { CLOSE_SAVED_EDITORS_COMMAND_ID, CLOSE_EDITORS_IN_GROUP_COMMAND_ID, CLOSE_EDITOR_COMMAND_ID, CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands'; +import { CLOSE_SAVED_EDITORS_COMMAND_ID, CLOSE_EDITORS_IN_GROUP_COMMAND_ID, CLOSE_EDITOR_COMMAND_ID, CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, REOPEN_WITH_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands'; import { AutoSaveAfterShortDelayContext } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { WorkbenchListDoubleSelection } from 'vs/platform/list/browser/listService'; import { Schemas } from 'vs/base/common/network'; -import { DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, WorkbenchStateContext, WorkspaceFolderCountContext, SidebarFocusContext, ActiveEditorCanRevertContext, ActiveEditorContext, ResourceContextKey } from 'vs/workbench/common/contextkeys'; +import { DirtyWorkingCopiesContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, WorkbenchStateContext, WorkspaceFolderCountContext, SidebarFocusContext, ActiveEditorCanRevertContext, ActiveEditorContext, ResourceContextKey, ActiveEditorAvailableEditorIdsContext } from 'vs/workbench/common/contextkeys'; import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; @@ -30,15 +28,13 @@ import { IExplorerService } from 'vs/workbench/contrib/files/browser/files'; import { Codicon } from 'vs/base/common/codicons'; // Contribute Global Actions -const category = { value: nls.localize('filesCategory', "File"), original: 'File' }; -const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(SyncActionDescriptor.from(GlobalCompareResourcesAction), 'File: Compare Active File With...', category.value, ActiveEditorContext); -registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusFilesExplorer), 'File: Focus on Files Explorer', category.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(ShowActiveFileInExplorer), 'File: Reveal Active File in Explorer View', category.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(CompareWithClipboardAction, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.KeyC) }), 'File: Compare Active File with Clipboard', category.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleAutoSaveAction), 'File: Toggle Auto Save', category.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(ShowOpenedFileInNewWindow, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.KeyO) }), 'File: Open Active File in New Window', category.value, EmptyWorkspaceSupportContext); +registerAction2(GlobalCompareResourcesAction); +registerAction2(FocusFilesExplorer); +registerAction2(ShowActiveFileInExplorer); +registerAction2(CompareWithClipboardAction); +registerAction2(ToggleAutoSaveAction); +registerAction2(ShowOpenedFileInNewWindow); // Commands CommandsRegistry.registerCommand('_files.windowOpen', openWindowCommand); @@ -198,18 +194,18 @@ export function appendToCommandPalette(id: string, title: ILocalizedString, cate }); } -appendToCommandPalette(COPY_PATH_COMMAND_ID, { value: nls.localize('copyPathOfActive', "Copy Path of Active File"), original: 'Copy Path of Active File' }, category); -appendToCommandPalette(COPY_RELATIVE_PATH_COMMAND_ID, { value: nls.localize('copyRelativePathOfActive', "Copy Relative Path of Active File"), original: 'Copy Relative Path of Active File' }, category); -appendToCommandPalette(SAVE_FILE_COMMAND_ID, { value: SAVE_FILE_LABEL, original: 'Save' }, category); -appendToCommandPalette(SAVE_FILE_WITHOUT_FORMATTING_COMMAND_ID, { value: SAVE_FILE_WITHOUT_FORMATTING_LABEL, original: 'Save without Formatting' }, category); -appendToCommandPalette(SAVE_ALL_IN_GROUP_COMMAND_ID, { value: nls.localize('saveAllInGroup', "Save All in Group"), original: 'Save All in Group' }, category); -appendToCommandPalette(SAVE_FILES_COMMAND_ID, { value: nls.localize('saveFiles', "Save All Files"), original: 'Save All Files' }, category); -appendToCommandPalette(REVERT_FILE_COMMAND_ID, { value: nls.localize('revert', "Revert File"), original: 'Revert File' }, category); -appendToCommandPalette(COMPARE_WITH_SAVED_COMMAND_ID, { value: nls.localize('compareActiveWithSaved', "Compare Active File with Saved"), original: 'Compare Active File with Saved' }, category); -appendToCommandPalette(SAVE_FILE_AS_COMMAND_ID, { value: SAVE_FILE_AS_LABEL, original: 'Save As...' }, category); -appendToCommandPalette(NEW_FILE_COMMAND_ID, { value: NEW_FILE_LABEL, original: 'New File' }, category, WorkspaceFolderCountContext.notEqualsTo('0')); -appendToCommandPalette(NEW_FOLDER_COMMAND_ID, { value: NEW_FOLDER_LABEL, original: 'New Folder' }, category, WorkspaceFolderCountContext.notEqualsTo('0')); -appendToCommandPalette(NEW_UNTITLED_FILE_COMMAND_ID, { value: NEW_UNTITLED_FILE_LABEL, original: 'New Untitled File' }, category); +appendToCommandPalette(COPY_PATH_COMMAND_ID, { value: nls.localize('copyPathOfActive', "Copy Path of Active File"), original: 'Copy Path of Active File' }, fileCategory); +appendToCommandPalette(COPY_RELATIVE_PATH_COMMAND_ID, { value: nls.localize('copyRelativePathOfActive', "Copy Relative Path of Active File"), original: 'Copy Relative Path of Active File' }, fileCategory); +appendToCommandPalette(SAVE_FILE_COMMAND_ID, { value: SAVE_FILE_LABEL, original: 'Save' }, fileCategory); +appendToCommandPalette(SAVE_FILE_WITHOUT_FORMATTING_COMMAND_ID, { value: SAVE_FILE_WITHOUT_FORMATTING_LABEL, original: 'Save without Formatting' }, fileCategory); +appendToCommandPalette(SAVE_ALL_IN_GROUP_COMMAND_ID, { value: nls.localize('saveAllInGroup', "Save All in Group"), original: 'Save All in Group' }, fileCategory); +appendToCommandPalette(SAVE_FILES_COMMAND_ID, { value: nls.localize('saveFiles', "Save All Files"), original: 'Save All Files' }, fileCategory); +appendToCommandPalette(REVERT_FILE_COMMAND_ID, { value: nls.localize('revert', "Revert File"), original: 'Revert File' }, fileCategory); +appendToCommandPalette(COMPARE_WITH_SAVED_COMMAND_ID, { value: nls.localize('compareActiveWithSaved', "Compare Active File with Saved"), original: 'Compare Active File with Saved' }, fileCategory); +appendToCommandPalette(SAVE_FILE_AS_COMMAND_ID, { value: SAVE_FILE_AS_LABEL, original: 'Save As...' }, fileCategory); +appendToCommandPalette(NEW_FILE_COMMAND_ID, { value: NEW_FILE_LABEL, original: 'New File' }, fileCategory, WorkspaceFolderCountContext.notEqualsTo('0')); +appendToCommandPalette(NEW_FOLDER_COMMAND_ID, { value: NEW_FOLDER_LABEL, original: 'New Folder' }, fileCategory, WorkspaceFolderCountContext.notEqualsTo('0')); +appendToCommandPalette(NEW_UNTITLED_FILE_COMMAND_ID, { value: NEW_UNTITLED_FILE_LABEL, original: 'New Untitled File' }, fileCategory); // Menu registration - open editors @@ -226,6 +222,16 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { when: isFileOrUntitledResourceContextKey }); +MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { + group: '1_open', + order: 10, + command: { + id: REOPEN_WITH_COMMAND_ID, + title: nls.localize('reopenWith', "Reopen Editor With...") + }, + when: ActiveEditorAvailableEditorIdsContext +}); + MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { group: '1_cutcopypaste', order: 10, diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index caf9e8e8967..b1f29827fbb 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -51,6 +51,10 @@ import { BrowserFileUpload, FileDownload } from 'vs/workbench/contrib/files/brow import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; +import { Action2 } from 'vs/platform/actions/common/actions'; +import { ActiveEditorContext, EmptyWorkspaceSupportContext } from 'vs/workbench/common/contextkeys'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; export const NEW_FILE_COMMAND_ID = 'explorer.newFile'; export const NEW_FILE_LABEL = nls.localize('newFile', "New File..."); @@ -67,6 +71,7 @@ export const UPLOAD_COMMAND_ID = 'explorer.upload'; export const UPLOAD_LABEL = nls.localize('upload', "Upload..."); const CONFIRM_DELETE_SETTING_KEY = 'explorer.confirmDelete'; const MAX_UNDO_FILE_SIZE = 5000000; // 5mb +export const fileCategory = { value: nls.localize('filesCategory', "File"), original: 'File' }; function onError(notificationService: INotificationService, error: any): void { if (error.message === 'string') { @@ -451,30 +456,34 @@ async function askForOverwrite(fileService: IFileService, dialogService: IDialog } // Global Compare with -export class GlobalCompareResourcesAction extends Action { +export class GlobalCompareResourcesAction extends Action2 { static readonly ID = 'workbench.files.action.compareFileWith'; static readonly LABEL = nls.localize('globalCompareFile', "Compare Active File With..."); - constructor( - id: string, - label: string, - @IQuickInputService private readonly quickInputService: IQuickInputService, - @IEditorService private readonly editorService: IEditorService, - @ITextModelService private readonly textModelService: ITextModelService - ) { - super(id, label); + constructor() { + super({ + id: GlobalCompareResourcesAction.ID, + title: { value: GlobalCompareResourcesAction.LABEL, original: 'Compare Active File With...' }, + f1: true, + category: fileCategory, + precondition: ActiveEditorContext + }); } - override async run(): Promise { - const activeInput = this.editorService.activeEditor; + override async run(accessor: ServicesAccessor): Promise { + const editorService = accessor.get(IEditorService); + const textModelService = accessor.get(ITextModelService); + const quickInputService = accessor.get(IQuickInputService); + + const activeInput = editorService.activeEditor; const activeResource = EditorResourceAccessor.getOriginalUri(activeInput); - if (activeResource && this.textModelService.canHandleResource(activeResource)) { - const picks = await this.quickInputService.quickAccess.pick('', { itemActivation: ItemActivation.SECOND }); + if (activeResource && textModelService.canHandleResource(activeResource)) { + const picks = await quickInputService.quickAccess.pick('', { itemActivation: ItemActivation.SECOND }); if (picks?.length === 1) { const resource = (picks[0] as unknown as { resource: unknown }).resource; - if (URI.isUri(resource) && this.textModelService.canHandleResource(resource)) { - this.editorService.openEditor({ + if (URI.isUri(resource) && textModelService.canHandleResource(resource)) { + editorService.openEditor({ original: { resource: activeResource }, modified: { resource: resource }, options: { pinned: true } @@ -485,20 +494,22 @@ export class GlobalCompareResourcesAction extends Action { } } -export class ToggleAutoSaveAction extends Action { +export class ToggleAutoSaveAction extends Action2 { static readonly ID = 'workbench.action.toggleAutoSave'; static readonly LABEL = nls.localize('toggleAutoSave', "Toggle Auto Save"); - constructor( - id: string, - label: string, - @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService - ) { - super(id, label); + constructor() { + super({ + id: ToggleAutoSaveAction.ID, + title: { value: ToggleAutoSaveAction.LABEL, original: 'Toggle Auto Save' }, + f1: true, + category: fileCategory + }); } - override run(): Promise { - return this.filesConfigurationService.toggleAutoSave(); + override run(accessor: ServicesAccessor): Promise { + const filesConfigurationService = accessor.get(IFilesConfigurationService); + return filesConfigurationService.toggleAutoSave(); } } @@ -573,69 +584,79 @@ export class CloseGroupAction extends Action { } } -export class FocusFilesExplorer extends Action { +export class FocusFilesExplorer extends Action2 { static readonly ID = 'workbench.files.action.focusFilesExplorer'; static readonly LABEL = nls.localize('focusFilesExplorer', "Focus on Files Explorer"); - constructor( - id: string, - label: string, - @IPaneCompositePartService private readonly paneCompositeService: IPaneCompositePartService - ) { - super(id, label); + constructor() { + super({ + id: FocusFilesExplorer.ID, + title: { value: FocusFilesExplorer.LABEL, original: 'Focus on Files Explorer' }, + f1: true, + category: fileCategory + }); } - override async run(): Promise { - await this.paneCompositeService.openPaneComposite(VIEWLET_ID, ViewContainerLocation.Sidebar, true); + override async run(accessor: ServicesAccessor): Promise { + const paneCompositeService = accessor.get(IPaneCompositePartService); + await paneCompositeService.openPaneComposite(VIEWLET_ID, ViewContainerLocation.Sidebar, true); } } -export class ShowActiveFileInExplorer extends Action { +export class ShowActiveFileInExplorer extends Action2 { static readonly ID = 'workbench.files.action.showActiveFileInExplorer'; static readonly LABEL = nls.localize('showInExplorer', "Reveal Active File in Explorer View"); - constructor( - id: string, - label: string, - @IEditorService private readonly editorService: IEditorService, - @ICommandService private readonly commandService: ICommandService - ) { - super(id, label); + constructor() { + super({ + id: ShowActiveFileInExplorer.ID, + title: { value: ShowActiveFileInExplorer.LABEL, original: 'Reveal Active File in Explorer View' }, + f1: true, + category: fileCategory + }); } - override async run(): Promise { - const resource = EditorResourceAccessor.getOriginalUri(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY }); + override async run(accessor: ServicesAccessor): Promise { + const commandService = accessor.get(ICommandService); + const editorService = accessor.get(IEditorService); + const resource = EditorResourceAccessor.getOriginalUri(editorService.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY }); if (resource) { - this.commandService.executeCommand(REVEAL_IN_EXPLORER_COMMAND_ID, resource); + commandService.executeCommand(REVEAL_IN_EXPLORER_COMMAND_ID, resource); } } } -export class ShowOpenedFileInNewWindow extends Action { +export class ShowOpenedFileInNewWindow extends Action2 { static readonly ID = 'workbench.action.files.showOpenedFileInNewWindow'; static readonly LABEL = nls.localize('openFileInNewWindow', "Open Active File in New Window"); constructor( - id: string, - label: string, - @IEditorService private readonly editorService: IEditorService, - @IHostService private readonly hostService: IHostService, - @IDialogService private readonly dialogService: IDialogService, - @IFileService private readonly fileService: IFileService ) { - super(id, label); + super({ + id: ShowOpenedFileInNewWindow.ID, + title: { value: ShowOpenedFileInNewWindow.LABEL, original: 'Open Active File in New Window' }, + f1: true, + category: fileCategory, + keybinding: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.KeyO), weight: KeybindingWeight.WorkbenchContrib }, + precondition: EmptyWorkspaceSupportContext + }); } - override async run(): Promise { - const fileResource = EditorResourceAccessor.getOriginalUri(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY }); + override async run(accessor: ServicesAccessor): Promise { + const editorService = accessor.get(IEditorService); + const hostService = accessor.get(IHostService); + const dialogService = accessor.get(IDialogService); + const fileService = accessor.get(IFileService); + + const fileResource = EditorResourceAccessor.getOriginalUri(editorService.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY }); if (fileResource) { - if (this.fileService.hasProvider(fileResource)) { - this.hostService.openWindow([{ fileUri: fileResource }], { forceNewWindow: true }); + if (fileService.hasProvider(fileResource)) { + hostService.openWindow([{ fileUri: fileResource }], { forceNewWindow: true }); } else { - this.dialogService.show(Severity.Error, nls.localize('openFileToShowInNewWindow.unsupportedschema', "The active editor must contain an openable resource.")); + dialogService.show(Severity.Error, nls.localize('openFileToShowInNewWindow.unsupportedschema', "The active editor must contain an openable resource.")); } } } @@ -718,7 +739,7 @@ function getWellFormedFileName(filename: string): string { return filename; } -export class CompareWithClipboardAction extends Action { +export class CompareWithClipboardAction extends Action2 { static readonly ID = 'workbench.files.action.compareWithClipboard'; static readonly LABEL = nls.localize('compareWithClipboard', "Compare Active File with Clipboard"); @@ -726,32 +747,34 @@ export class CompareWithClipboardAction extends Action { private registrationDisposal: IDisposable | undefined; private static SCHEME_COUNTER = 0; - constructor( - id: string, - label: string, - @IEditorService private readonly editorService: IEditorService, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @ITextModelService private readonly textModelService: ITextModelService, - @IFileService private readonly fileService: IFileService - ) { - super(id, label); - - this.enabled = true; + constructor() { + super({ + id: CompareWithClipboardAction.ID, + title: { value: CompareWithClipboardAction.LABEL, original: 'Compare Active File with Clipboard' }, + f1: true, + category: fileCategory, + keybinding: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.KeyC), weight: KeybindingWeight.WorkbenchContrib } + }); } - override async run(): Promise { - const resource = EditorResourceAccessor.getOriginalUri(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY }); + override async run(accessor: ServicesAccessor): Promise { + const editorService = accessor.get(IEditorService); + const instantiationService = accessor.get(IInstantiationService); + const textModelService = accessor.get(ITextModelService); + const fileService = accessor.get(IFileService); + + const resource = EditorResourceAccessor.getOriginalUri(editorService.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY }); const scheme = `clipboardCompare${CompareWithClipboardAction.SCHEME_COUNTER++}`; - if (resource && (this.fileService.hasProvider(resource) || resource.scheme === Schemas.untitled)) { + if (resource && (fileService.hasProvider(resource) || resource.scheme === Schemas.untitled)) { if (!this.registrationDisposal) { - const provider = this.instantiationService.createInstance(ClipboardContentProvider); - this.registrationDisposal = this.textModelService.registerTextModelContentProvider(scheme, provider); + const provider = instantiationService.createInstance(ClipboardContentProvider); + this.registrationDisposal = textModelService.registerTextModelContentProvider(scheme, provider); } const name = resources.basename(resource); const editorLabel = nls.localize('clipboardComparisonLabel', "Clipboard ↔ {0}", name); - await this.editorService.openEditor({ + await editorService.openEditor({ original: { resource: resource.with({ scheme }) }, modified: { resource: resource }, label: editorLabel, @@ -763,9 +786,7 @@ export class CompareWithClipboardAction extends Action { } } - override dispose(): void { - super.dispose(); - + dispose(): void { dispose(this.registrationDisposal); this.registrationDisposal = undefined; } diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index 062fb218400..d51f8cae859 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -22,7 +22,7 @@ import { ExplorerViewletViewsContribution } from 'vs/workbench/contrib/files/bro import { IEditorPaneRegistry, EditorPaneDescriptor } from 'vs/workbench/browser/editor'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { ILabelService } from 'vs/platform/label/common/label'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ExplorerService, UNDO_REDO_SOURCE } from 'vs/workbench/contrib/files/browser/explorerService'; import { SUPPORTED_ENCODINGS } from 'vs/workbench/services/textfile/common/encoding'; import { Schemas } from 'vs/base/common/network'; @@ -53,7 +53,7 @@ class FileUriLabelContribution implements IWorkbenchContribution { } } -registerSingleton(IExplorerService, ExplorerService, true); +registerSingleton(IExplorerService, ExplorerService, InstantiationType.Delayed); // Register file editors Registry.as(EditorExtensions.EditorPane).registerEditorPane( @@ -83,25 +83,25 @@ Registry.as(EditorExtensions.EditorFactory).registerFile // Register Editor Input Serializer & Handler Registry.as(EditorExtensions.EditorFactory).registerEditorSerializer(FILE_EDITOR_INPUT_ID, FileEditorInputSerializer); -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(FileEditorWorkingCopyEditorHandler, 'FileEditorWorkingCopyEditorHandler', LifecyclePhase.Ready); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(FileEditorWorkingCopyEditorHandler, LifecyclePhase.Ready); // Register Explorer views -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ExplorerViewletViewsContribution, 'ExplorerViewletViewsContribution', LifecyclePhase.Starting); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ExplorerViewletViewsContribution, LifecyclePhase.Starting); // Register Text File Editor Tracker -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TextFileEditorTracker, 'TextFileEditorTracker', LifecyclePhase.Starting); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TextFileEditorTracker, LifecyclePhase.Starting); // Register Text File Save Error Handler -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TextFileSaveErrorHandler, 'TextFileSaveErrorHandler', LifecyclePhase.Starting); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TextFileSaveErrorHandler, LifecyclePhase.Starting); // Register uri display for file uris -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(FileUriLabelContribution, 'FileUriLabelContribution', LifecyclePhase.Starting); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(FileUriLabelContribution, LifecyclePhase.Starting); // Register Workspace Watcher -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(WorkspaceWatcher, 'WorkspaceWatcher', LifecyclePhase.Restored); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(WorkspaceWatcher, LifecyclePhase.Restored); // Register Dirty Files Indicator -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DirtyFilesIndicator, 'DirtyFilesIndicator', LifecyclePhase.Starting); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DirtyFilesIndicator, LifecyclePhase.Starting); // Configuration const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index 8dc7c9a2e1e..eb3cb2b6df9 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import * as perf from 'vs/base/common/performance'; -import { IAction, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; +import { WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; import { memoize } from 'vs/base/common/decorators'; import { IFilesConfiguration, ExplorerFolderContext, FilesExplorerFocusedContext, ExplorerFocusedContext, ExplorerRootContext, ExplorerResourceReadonlyContext, ExplorerResourceCut, ExplorerResourceMoveableToTrash, ExplorerCompressedFocusContext, ExplorerCompressedFirstFocusContext, ExplorerCompressedLastFocusContext, ExplorerResourceAvailableEditorIdsContext, VIEW_ID, VIEWLET_ID, ExplorerResourceNotReadonlyContext, ViewHasSomeCollapsibleRootItemContext } from 'vs/workbench/contrib/files/common/files'; import { FileCopiedContext, NEW_FILE_COMMAND_ID, NEW_FOLDER_COMMAND_ID } from 'vs/workbench/contrib/files/browser/fileActions'; @@ -31,8 +31,7 @@ import { ExplorerDelegate, ExplorerDataSource, FilesRenderer, ICompressedNavigat import { IThemeService, IFileIconTheme } from 'vs/platform/theme/common/themeService'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { ITreeContextMenuEvent, TreeVisibility } from 'vs/base/browser/ui/tree/tree'; -import { IMenuService, MenuId, IMenu, Action2, registerAction2 } from 'vs/platform/actions/common/actions'; -import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { MenuId, Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ExplorerItem, NewExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel'; import { ResourceLabels } from 'vs/workbench/browser/labels'; @@ -202,7 +201,6 @@ export class ExplorerView extends ViewPane implements IExplorerView { @IDecorationsService private readonly decorationService: IDecorationsService, @ILabelService private readonly labelService: ILabelService, @IThemeService themeService: IWorkbenchThemeService, - @IMenuService private readonly menuService: IMenuService, @ITelemetryService telemetryService: ITelemetryService, @IExplorerService private readonly explorerService: IExplorerService, @IStorageService private readonly storageService: IStorageService, @@ -242,13 +240,6 @@ export class ExplorerView extends ViewPane implements IExplorerView { // noop } - // Memoized locals - @memoize private get contributedContextMenu(): IMenu { - const contributedContextMenu = this.menuService.createMenu(MenuId.ExplorerContext, this.tree.contextKeyService); - this._register(contributedContextMenu); - return contributedContextMenu; - } - @memoize private get fileCopiedContextKey(): IContextKey { return FileCopiedContext.bindTo(this.contextKeyService); } @@ -271,7 +262,8 @@ export class ExplorerView extends ViewPane implements IExplorerView { const title = workspace.folders.map(folder => folder.name).join(); titleElement.textContent = this.name; titleElement.title = title; - titleElement.setAttribute('aria-label', nls.localize('explorerSection', "Explorer Section: {0}", this.name)); + this.ariaHeaderLabel = nls.localize('explorerSection', "Explorer Section: {0}", this.name); + titleElement.setAttribute('aria-label', this.ariaHeaderLabel); }; this._register(this.contextService.onDidChangeWorkspaceName(setHeader)); @@ -460,6 +452,7 @@ export class ExplorerView extends ViewPane implements IExplorerView { } }); this._register(this.tree); + this._register(this.themeService.onDidColorThemeChange(() => this.tree.rerender())); // Bind configuration const onDidChangeCompressionConfiguration = Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('explorer.compactFolders')); @@ -582,7 +575,6 @@ export class ExplorerView extends ViewPane implements IExplorerView { const selection = this.tree.getSelection(); - const actions: IAction[] = []; const roots = this.explorerService.roots; // If the click is outside of the elements pass the root resource if there is only one root. If there are multiple roots pass empty object. let arg: URI | {}; if (stat instanceof ExplorerItem) { @@ -591,11 +583,12 @@ export class ExplorerView extends ViewPane implements IExplorerView { } else { arg = roots.length === 1 ? roots[0].resource : {}; } - createAndFillInContextMenuActions(this.contributedContextMenu, { arg, shouldForwardArgs: true }, actions); this.contextMenuService.showContextMenu({ + menuId: MenuId.ExplorerContext, + menuActionOptions: { arg, shouldForwardArgs: true }, + contextKeyService: this.tree.contextKeyService, getAnchor: () => anchor, - getActions: () => actions, onHide: (wasCancelled?: boolean) => { if (wasCancelled) { this.tree.domFocus(); diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index b48b6da99f5..e1ded9a550c 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -255,9 +255,12 @@ export class CompressedNavigationController implements ICompressedNavigationCont } export interface IFileTemplateData { - elementDisposable: IDisposable; - label: IResourceLabel; - container: HTMLElement; + readonly templateDisposables: DisposableStore; + readonly elementDisposables: DisposableStore; + readonly label: IResourceLabel; + readonly container: HTMLElement; + + currentContext?: ExplorerItem; } export class FilesRenderer implements ICompressibleTreeRenderer, IListAccessibilityProvider, IDisposable { @@ -312,15 +315,27 @@ export class FilesRenderer implements ICompressibleTreeRenderer { + try { + if (templateData.currentContext) { + this.updateWidth(templateData.currentContext); + } + } catch (e) { + // noop since the element might no longer be in the tree, no update of width necessary + } + })); + + const templateData: IFileTemplateData = { templateDisposables, elementDisposables: templateDisposables.add(new DisposableStore()), label, container }; + return templateData; } renderElement(node: ITreeNode, index: number, templateData: IFileTemplateData): void { - templateData.elementDisposable.dispose(); const stat = node.element; + templateData.currentContext = stat; + const editableData = this.explorerService.getEditableData(stat); templateData.label.element.classList.remove('compressed'); @@ -328,20 +343,20 @@ export class FilesRenderer implements ICompressibleTreeRenderer, FuzzyScore>, index: number, templateData: IFileTemplateData, height: number | undefined): void { - templateData.elementDisposable.dispose(); - const stat = node.element.elements[node.element.elements.length - 1]; + templateData.currentContext = stat; + const editable = node.element.elements.filter(e => this.explorerService.isEditable(e)); const editableData = editable.length === 0 ? undefined : this.explorerService.getEditableData(editable[0]); @@ -350,20 +365,19 @@ export class FilesRenderer implements ICompressibleTreeRenderer e.name); - disposables.add(this.renderStat(stat, label, id, node.filterData, templateData)); + this.renderStat(stat, label, id, node.filterData, templateData); const compressedNavigationController = new CompressedNavigationController(id, node.element.elements, templateData, node.depth, node.collapsed); - disposables.add(compressedNavigationController); + templateData.elementDisposables.add(compressedNavigationController); this.compressedNavigationControllers.set(stat, compressedNavigationController); // accessibility - disposables.add(this._onDidChangeActiveDescendant.add(compressedNavigationController.onDidChange)); + templateData.elementDisposables.add(this._onDidChangeActiveDescendant.add(compressedNavigationController.onDidChange)); - disposables.add(DOM.addDisposableListener(templateData.container, 'mousedown', e => { + templateData.elementDisposables.add(DOM.addDisposableListener(templateData.container, 'mousedown', e => { const result = getIconLabelNameFromHTMLElement(e.target); if (result) { @@ -371,68 +385,44 @@ export class FilesRenderer implements ICompressibleTreeRenderer this.compressedNavigationControllers.delete(stat))); - - templateData.elementDisposable = disposables; + templateData.elementDisposables.add(toDisposable(() => this.compressedNavigationControllers.delete(stat))); } // Input Box else { templateData.label.element.classList.remove('compressed'); templateData.label.element.style.display = 'none'; - templateData.elementDisposable = this.renderInputBox(templateData.container, editable[0], editableData); + templateData.elementDisposables.add(this.renderInputBox(templateData.container, editable[0], editableData)); } } - private renderStat(stat: ExplorerItem, label: string | string[], domId: string | undefined, filterData: FuzzyScore | undefined, templateData: IFileTemplateData): IDisposable { - const elementDisposables = new DisposableStore(); + private renderStat(stat: ExplorerItem, label: string | string[], domId: string | undefined, filterData: FuzzyScore | undefined, templateData: IFileTemplateData): void { templateData.label.element.style.display = 'flex'; const extraClasses = ['explorer-item']; if (this.explorerService.isCut(stat)) { extraClasses.push('cut'); } - const setResourceData = () => { - // Offset nested children unless folders have both chevrons and icons, otherwise alignment breaks - const theme = this.themeService.getFileIconTheme(); + // Offset nested children unless folders have both chevrons and icons, otherwise alignment breaks + const theme = this.themeService.getFileIconTheme(); - // Hack to always render chevrons for file nests, or else may not be able to identify them. - const twistieContainer = (templateData.container.parentElement?.parentElement?.querySelector('.monaco-tl-twistie') as HTMLElement); - if (twistieContainer) { - if (stat.hasNests && theme.hidesExplorerArrows) { - twistieContainer.classList.add('force-twistie'); - } else { - twistieContainer.classList.remove('force-twistie'); - } - } + // Hack to always render chevrons for file nests, or else may not be able to identify them. + const twistieContainer = templateData.container.parentElement?.parentElement?.querySelector('.monaco-tl-twistie'); + twistieContainer?.classList.toggle('force-twistie', stat.hasNests && theme.hidesExplorerArrows); - // when explorer arrows are hidden or there are no folder icons, nests get misaligned as they are forced to have arrows and files typically have icons - // Apply some CSS magic to get things looking as reasonable as possible. - const themeIsUnhappyWithNesting = theme.hasFileIcons && (theme.hidesExplorerArrows || !theme.hasFolderIcons); - const realignNestedChildren = stat.nestedParent && themeIsUnhappyWithNesting; + // when explorer arrows are hidden or there are no folder icons, nests get misaligned as they are forced to have arrows and files typically have icons + // Apply some CSS magic to get things looking as reasonable as possible. + const themeIsUnhappyWithNesting = theme.hasFileIcons && (theme.hidesExplorerArrows || !theme.hasFolderIcons); + const realignNestedChildren = stat.nestedParent && themeIsUnhappyWithNesting; - templateData.label.setResource({ resource: stat.resource, name: label }, { - fileKind: stat.isRoot ? FileKind.ROOT_FOLDER : stat.isDirectory ? FileKind.FOLDER : FileKind.FILE, - extraClasses: realignNestedChildren ? [...extraClasses, 'align-nest-icon-with-parent-icon'] : extraClasses, - fileDecorations: this.config.explorer.decorations, - matches: createMatches(filterData), - separator: this.labelService.getSeparator(stat.resource.scheme, stat.resource.authority), - domId - }); - }; - - elementDisposables.add(this.themeService.onDidFileIconThemeChange(() => setResourceData())); - setResourceData(); - - elementDisposables.add(templateData.label.onDidRender(() => { - try { - this.updateWidth(stat); - } catch (e) { - // noop since the element might no longer be in the tree, no update of width necessary - } - })); - - return elementDisposables; + templateData.label.setResource({ resource: stat.resource, name: label }, { + fileKind: stat.isRoot ? FileKind.ROOT_FOLDER : stat.isDirectory ? FileKind.FOLDER : FileKind.FILE, + extraClasses: realignNestedChildren ? [...extraClasses, 'align-nest-icon-with-parent-icon'] : extraClasses, + fileDecorations: this.config.explorer.decorations, + matches: createMatches(filterData), + separator: this.labelService.getSeparator(stat.resource.scheme, stat.resource.authority), + domId + }); } private renderInputBox(container: HTMLElement, stat: ExplorerItem, editableData: IEditableData): IDisposable { @@ -560,16 +550,17 @@ export class FilesRenderer implements ICompressibleTreeRenderer, index: number, templateData: IFileTemplateData): void { - templateData.elementDisposable.dispose(); + templateData.currentContext = undefined; + templateData.elementDisposables.clear(); } disposeCompressedElements(node: ITreeNode, FuzzyScore>, index: number, templateData: IFileTemplateData): void { - templateData.elementDisposable.dispose(); + templateData.currentContext = undefined; + templateData.elementDisposables.clear(); } disposeTemplate(templateData: IFileTemplateData): void { - templateData.elementDisposable.dispose(); - templateData.label.dispose(); + templateData.templateDisposables.dispose(); } getCompressedNavigationController(stat: ExplorerItem): ICompressedNavigationController | undefined { diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index 3d2a4c48a6e..90494a4ffd5 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -28,8 +28,7 @@ import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IMenuService, MenuId, IMenu, Action2, registerAction2, MenuRegistry } from 'vs/platform/actions/common/actions'; +import { MenuId, Action2, registerAction2, MenuRegistry } from 'vs/platform/actions/common/actions'; import { OpenEditorsDirtyEditorContext, OpenEditorsGroupContext, OpenEditorsReadonlyEditorContext, SAVE_ALL_LABEL, SAVE_ALL_COMMAND_ID, NEW_UNTITLED_FILE_COMMAND_ID } from 'vs/workbench/contrib/files/browser/fileConstants'; import { ResourceContextKey } from 'vs/workbench/common/contextkeys'; import { CodeDataTransfers, containsDragType } from 'vs/platform/dnd/browser/dnd'; @@ -69,7 +68,6 @@ export class OpenEditorsView extends ViewPane { private structuralRefreshDelay: number; private list!: WorkbenchList; private listLabels: ResourceLabels | undefined; - private contributedContextMenu!: IMenu; private needsRefresh = false; private elements: (OpenEditor | IEditorGroup)[] = []; private sortOrder: 'editorOrder' | 'alphabetical' | 'fullPath'; @@ -89,7 +87,6 @@ export class OpenEditorsView extends ViewPane { @IContextKeyService contextKeyService: IContextKeyService, @IThemeService themeService: IThemeService, @ITelemetryService telemetryService: ITelemetryService, - @IMenuService private readonly menuService: IMenuService, @IWorkingCopyService private readonly workingCopyService: IWorkingCopyService, @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService, @IOpenerService openerService: IOpenerService, @@ -243,9 +240,6 @@ export class OpenEditorsView extends ViewPane { this._register(this.list); this._register(this.listLabels); - this.contributedContextMenu = this.menuService.createMenu(MenuId.OpenEditorsContext, this.list.contextKeyService); - this._register(this.contributedContextMenu); - this.updateSize(); // Bind context keys @@ -396,12 +390,12 @@ export class OpenEditorsView extends ViewPane { } const element = e.element; - const actions: IAction[] = []; - createAndFillInContextMenuActions(this.contributedContextMenu, { shouldForwardArgs: true, arg: element instanceof OpenEditor ? EditorResourceAccessor.getOriginalUri(element.editor) : {} }, actions); this.contextMenuService.showContextMenu({ + menuId: MenuId.OpenEditorsContext, + menuActionOptions: { shouldForwardArgs: true, arg: element instanceof OpenEditor ? EditorResourceAccessor.getOriginalUri(element.editor) : {} }, + contextKeyService: this.list.contextKeyService, getAnchor: () => e.anchor, - getActions: () => actions, getActionsContext: () => element instanceof OpenEditor ? { groupId: element.groupId, editorIndex: element.group.getIndexOfEditor(element.editor) } : { groupId: element.id } }); } diff --git a/src/vs/workbench/contrib/files/test/browser/explorerView.test.ts b/src/vs/workbench/contrib/files/test/browser/explorerView.test.ts index 35d1fb3ab9f..77b772a742a 100644 --- a/src/vs/workbench/contrib/files/test/browser/explorerView.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/explorerView.test.ts @@ -12,7 +12,7 @@ import { getContext } from 'vs/workbench/contrib/files/browser/views/explorerVie import { listInvalidItemForeground } from 'vs/platform/theme/common/colorRegistry'; import { CompressedNavigationController } from 'vs/workbench/contrib/files/browser/views/explorerViewer'; import * as dom from 'vs/base/browser/dom'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { provideDecorations } from 'vs/workbench/contrib/files/browser/views/explorerDecorationsProvider'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; const $ = dom.$; @@ -84,7 +84,8 @@ suite('Files - ExplorerView', () => { const navigationController = new CompressedNavigationController('id', [s1, s2, s3], { container, - elementDisposable: Disposable.None, + templateDisposables: new DisposableStore(), + elementDisposables: new DisposableStore(), label: { container: label, onDidRender: emitter.event diff --git a/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts b/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts index e76a5bfc3b2..71c8d537e30 100644 --- a/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts +++ b/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts @@ -241,7 +241,6 @@ class DefaultFormatter extends Disposable implements IWorkbenchContribution { Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( DefaultFormatter, - 'DefaultFormatter', LifecyclePhase.Restored ); diff --git a/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts b/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts index 4647031ad07..d380e002f7a 100644 --- a/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts +++ b/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts @@ -24,6 +24,7 @@ import { ITextModelContentProvider, ITextModelService } from 'vs/editor/common/s import { peekViewBorder /*, peekViewEditorBackground, peekViewResultsBackground */ } from 'vs/editor/contrib/peekView/browser/peekView'; import { Context as SuggestContext } from 'vs/editor/contrib/suggest/browser/suggest'; import { localize } from 'vs/nls'; +import { ILocalizedString } from 'vs/platform/action/common/action'; import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; @@ -31,7 +32,7 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { EditorActivation, IResourceEditorInput } from 'vs/platform/editor/common/editor'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ILogService } from 'vs/platform/log/common/log'; @@ -54,6 +55,7 @@ import { NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT } from 'vs/workbench/contrib/noteb import { INotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; import * as icons from 'vs/workbench/contrib/notebook/browser/notebookIcons'; +import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; import { CellEditType, CellKind, CellUri, ICellOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { INotebookContentProvider, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; @@ -63,7 +65,7 @@ import { IEditorResolverService, RegisteredEditorPriority } from 'vs/workbench/s import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; - +const interactiveWindowCategory: ILocalizedString = { value: localize('interactiveWindow', 'Interactive Window'), original: 'Interactive Window' }; Registry.as(EditorExtensions.EditorPane).registerEditorPane( EditorPaneDescriptor.create( @@ -267,8 +269,8 @@ class InteractiveInputContentProvider implements ITextModelContentProvider { const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchContributionsRegistry.registerWorkbenchContribution(InteractiveDocumentContribution, 'InteractiveDocumentContribution', LifecyclePhase.Ready); -workbenchContributionsRegistry.registerWorkbenchContribution(InteractiveInputContentProvider, 'InteractiveInputContentProvider', LifecyclePhase.Ready); +workbenchContributionsRegistry.registerWorkbenchContribution(InteractiveDocumentContribution, LifecyclePhase.Ready); +workbenchContributionsRegistry.registerWorkbenchContribution(InteractiveInputContentProvider, LifecyclePhase.Ready); export class InteractiveEditorSerializer implements IEditorSerializer { public static readonly ID = InteractiveEditorInput.ID; @@ -315,8 +317,8 @@ Registry.as(EditorExtensions.EditorFactory) InteractiveEditorSerializer.ID, InteractiveEditorSerializer); -registerSingleton(IInteractiveHistoryService, InteractiveHistoryService, false); -registerSingleton(IInteractiveDocumentService, InteractiveDocumentService, false); +registerSingleton(IInteractiveHistoryService, InteractiveHistoryService, InstantiationType.Delayed); +registerSingleton(IInteractiveDocumentService, InteractiveDocumentService, InstantiationType.Delayed); registerAction2(class extends Action2 { constructor() { @@ -324,7 +326,7 @@ registerAction2(class extends Action2 { id: '_interactive.open', title: { value: localize('interactive.open', "Open Interactive Window"), original: 'Open Interactive Window' }, f1: false, - category: 'Interactive', + category: interactiveWindowCategory, description: { description: localize('interactive.open', "Open Interactive Window"), args: [ @@ -440,7 +442,7 @@ registerAction2(class extends Action2 { super({ id: 'interactive.execute', title: { value: localize('interactive.execute', "Execute Code"), original: 'Execute Code' }, - category: 'Interactive', + category: interactiveWindowCategory, keybinding: { // when: NOTEBOOK_CELL_LIST_FOCUSED, when: ContextKeyExpr.equals('resourceScheme', Schemas.vscodeInteractive), @@ -474,6 +476,7 @@ registerAction2(class extends Action2 { const editorService = accessor.get(IEditorService); const bulkEditService = accessor.get(IBulkEditService); const historyService = accessor.get(IInteractiveHistoryService); + const notebookEditorService = accessor.get(INotebookEditorService); let editorControl: { notebookEditor: NotebookEditorWidget | undefined; codeEditor: CodeEditorWidget } | undefined; if (context) { if (context.scheme === Schemas.vscodeInteractive) { @@ -514,6 +517,7 @@ registerAction2(class extends Action2 { outputCollapsed: false } : undefined; + await bulkEditService.apply([ new ResourceNotebookCellEdit(notebookDocument.uri, { @@ -534,8 +538,16 @@ registerAction2(class extends Action2 { ]); // reveal the cell into view first - editorControl.notebookEditor.revealCellRangeInView({ start: index, end: index + 1 }); + const range = { start: index, end: index + 1 }; + editorControl.notebookEditor.revealCellRangeInView(range); await editorControl.notebookEditor.executeNotebookCells(editorControl.notebookEditor.getCellsInRange({ start: index, end: index + 1 })); + + // update the selection and focus in the extension host model + const editor = notebookEditorService.getNotebookEditor(editorControl.notebookEditor.getId()); + if (editor) { + editor.setSelections([range]); + editor.setFocus(range); + } } } } @@ -546,7 +558,7 @@ registerAction2(class extends Action2 { super({ id: 'interactive.input.clear', title: { value: localize('interactive.input.clear', "Clear the interactive window input editor contents"), original: 'Clear the interactive window input editor contents' }, - category: 'Interactive', + category: interactiveWindowCategory, f1: false }); } @@ -572,7 +584,7 @@ registerAction2(class extends Action2 { super({ id: 'interactive.history.previous', title: { value: localize('interactive.history.previous', "Previous value in history"), original: 'Previous value in history' }, - category: 'Interactive', + category: interactiveWindowCategory, f1: false, keybinding: { when: ContextKeyExpr.and( @@ -611,7 +623,7 @@ registerAction2(class extends Action2 { super({ id: 'interactive.history.next', title: { value: localize('interactive.history.next', "Next value in history"), original: 'Next value in history' }, - category: 'Interactive', + category: interactiveWindowCategory, f1: false, keybinding: { when: ContextKeyExpr.and( @@ -657,7 +669,7 @@ registerAction2(class extends Action2 { mac: { primary: KeyMod.CtrlCmd | KeyCode.UpArrow }, weight: KeybindingWeight.WorkbenchContrib }, - category: 'Interactive', + category: interactiveWindowCategory, }); } @@ -686,7 +698,7 @@ registerAction2(class extends Action2 { mac: { primary: KeyMod.CtrlCmd | KeyCode.DownArrow }, weight: KeybindingWeight.WorkbenchContrib }, - category: 'Interactive', + category: interactiveWindowCategory, }); } @@ -710,8 +722,8 @@ registerAction2(class extends Action2 { super({ id: 'interactive.input.focus', title: { value: localize('interactive.input.focus', "Focus input editor in the interactive window"), original: 'Focus input editor in the interactive window' }, - category: 'Interactive', - f1: false + category: interactiveWindowCategory, + f1: true }); } @@ -745,8 +757,9 @@ registerAction2(class extends Action2 { super({ id: 'interactive.history.focus', title: { value: localize('interactive.history.focus', "Focus history in the interactive window"), original: 'Focus input editor in the interactive window' }, - category: 'Interactive', - f1: false + category: interactiveWindowCategory, + f1: true, + precondition: ContextKeyExpr.equals('resourceScheme', Schemas.vscodeInteractive), }); } diff --git a/src/vs/workbench/contrib/issue/browser/issue.web.contribution.ts b/src/vs/workbench/contrib/issue/browser/issue.web.contribution.ts index c3822e6c68c..d207b297844 100644 --- a/src/vs/workbench/contrib/issue/browser/issue.web.contribution.ts +++ b/src/vs/workbench/contrib/issue/browser/issue.web.contribution.ts @@ -7,11 +7,11 @@ import * as nls from 'vs/nls'; import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { ICommandAction } from 'vs/platform/action/common/action'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IProductService } from 'vs/platform/product/common/productService'; import { Registry } from 'vs/platform/registry/common/platform'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { WebIssueService } from 'vs/workbench/contrib/issue/browser/issueService'; import { OpenIssueReporterArgs, OpenIssueReporterActionId, OpenIssueReporterApiCommandId } from 'vs/workbench/contrib/issue/common/commands'; @@ -86,7 +86,7 @@ class RegisterIssueContribution implements IWorkbenchContribution { const command: ICommandAction = { id: OpenIssueReporterActionId, title: { value: OpenIssueReporterActionLabel, original: 'Report Issue' }, - category: CATEGORIES.Help + category: Categories.Help }; MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command }); @@ -100,10 +100,10 @@ class RegisterIssueContribution implements IWorkbenchContribution { } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(RegisterIssueContribution, 'RegisterIssueContribution', LifecyclePhase.Starting); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(RegisterIssueContribution, LifecyclePhase.Starting); CommandsRegistry.registerCommand('_issues.getSystemStatus', (accessor) => { return nls.localize('statusUnsupported', "The --status argument is not yet supported in browsers."); }); -registerSingleton(IWorkbenchIssueService, WebIssueService, true); +registerSingleton(IWorkbenchIssueService, WebIssueService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/issue.contribution.ts b/src/vs/workbench/contrib/issue/electron-sandbox/issue.contribution.ts index de4f64639c2..09a805b8e5c 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/issue.contribution.ts +++ b/src/vs/workbench/contrib/issue/electron-sandbox/issue.contribution.ts @@ -7,9 +7,9 @@ import { localize } from 'vs/nls'; import product from 'vs/platform/product/common/product'; import { MenuRegistry, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { ICommandAction } from 'vs/platform/action/common/action'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { ReportPerformanceIssueUsingReporterAction, OpenProcessExplorer, StopTracing } from 'vs/workbench/contrib/issue/electron-sandbox/issueActions'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue'; import { WorkbenchIssueService } from 'vs/workbench/services/issue/electron-sandbox/issueService'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; @@ -78,7 +78,7 @@ if (!!product.reportIssueUrl) { value: localize({ key: 'reportIssueInEnglish', comment: ['Translate this to "Report Issue in English" in all languages please!'] }, "Report Issue..."), original: 'Report Issue...' }, - category: CATEGORIES.Help + category: Categories.Help }; MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: reportIssue }); @@ -105,7 +105,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { registerAction2(OpenProcessExplorer); registerAction2(StopTracing); -registerSingleton(IWorkbenchIssueService, WorkbenchIssueService, true); +registerSingleton(IWorkbenchIssueService, WorkbenchIssueService, InstantiationType.Delayed); CommandsRegistry.registerCommand('_issues.getSystemStatus', (accessor) => { return accessor.get(IIssueService).getSystemStatus(); diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/issueActions.ts b/src/vs/workbench/contrib/issue/electron-sandbox/issueActions.ts index 68ff563a009..fb21da86476 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/issueActions.ts +++ b/src/vs/workbench/contrib/issue/electron-sandbox/issueActions.ts @@ -13,7 +13,7 @@ import { IssueType } from 'vs/platform/issue/common/issue'; import { IIssueService } from 'vs/platform/issue/electron-sandbox/issue'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue'; export class OpenProcessExplorer extends Action2 { @@ -24,7 +24,7 @@ export class OpenProcessExplorer extends Action2 { super({ id: OpenProcessExplorer.ID, title: { value: localize('openProcessExplorer', "Open Process Explorer"), original: 'Open Process Explorer' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true }); } @@ -44,7 +44,7 @@ export class ReportPerformanceIssueUsingReporterAction extends Action2 { super({ id: ReportPerformanceIssueUsingReporterAction.ID, title: { value: localize({ key: 'reportPerformanceIssue', comment: [`Here, 'issue' means problem or bug`] }, "Report Performance Issue..."), original: 'Report Performance Issue' }, - category: CATEGORIES.Help, + category: Categories.Help, f1: true }); } @@ -64,7 +64,7 @@ export class StopTracing extends Action2 { super({ id: StopTracing.ID, title: { value: localize('stopTracing', "Stop Tracing"), original: 'Stop Tracing' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true }); } diff --git a/src/vs/workbench/contrib/keybindings/browser/keybindings.contribution.ts b/src/vs/workbench/contrib/keybindings/browser/keybindings.contribution.ts index 5f6cdfb3298..5e7443e410c 100644 --- a/src/vs/workbench/contrib/keybindings/browser/keybindings.contribution.ts +++ b/src/vs/workbench/contrib/keybindings/browser/keybindings.contribution.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { rendererLogChannelId } from 'vs/workbench/contrib/logs/common/logConstants'; import { IOutputService } from 'vs/workbench/services/output/common/output'; @@ -17,7 +17,7 @@ class ToggleKeybindingsLogAction extends Action2 { super({ id: 'workbench.action.toggleKeybindingsLog', title: { value: nls.localize('toggleKeybindingsLog', "Toggle Keyboard Shortcuts Troubleshooting"), original: 'Toggle Keyboard Shortcuts Troubleshooting' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true }); } diff --git a/src/vs/workbench/contrib/languageDetection/browser/languageDetection.contribution.ts b/src/vs/workbench/contrib/languageDetection/browser/languageDetection.contribution.ts index d5605600b90..363f76cb570 100644 --- a/src/vs/workbench/contrib/languageDetection/browser/languageDetection.contribution.ts +++ b/src/vs/workbench/contrib/languageDetection/browser/languageDetection.contribution.ts @@ -115,7 +115,7 @@ class LanguageDetectionStatusContribution implements IWorkbenchContribution { } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(LanguageDetectionStatusContribution, 'LanguageDetectionStatusContribution', LifecyclePhase.Restored); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(LanguageDetectionStatusContribution, LifecyclePhase.Restored); registerAction2(class extends Action2 { diff --git a/src/vs/workbench/contrib/languageStatus/browser/languageStatus.contribution.ts b/src/vs/workbench/contrib/languageStatus/browser/languageStatus.contribution.ts index f1661dcd48e..22af57bcfa3 100644 --- a/src/vs/workbench/contrib/languageStatus/browser/languageStatus.contribution.ts +++ b/src/vs/workbench/contrib/languageStatus/browser/languageStatus.contribution.ts @@ -30,6 +30,7 @@ import { equals } from 'vs/base/common/arrays'; import { URI } from 'vs/base/common/uri'; import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; class LanguageStatusViewModel { @@ -391,7 +392,7 @@ class EditorStatusContribution implements IWorkbenchContribution { } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(EditorStatusContribution, 'EditorStatusContribution', LifecyclePhase.Restored); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(EditorStatusContribution, LifecyclePhase.Restored); registerAction2(class extends Action2 { @@ -402,10 +403,7 @@ registerAction2(class extends Action2 { value: localize('reset', 'Reset Language Status Interaction Counter'), original: 'Reset Language Status Interaction Counter' }, - category: { - value: localize('cat', 'View'), - original: 'View' - }, + category: Categories.View, f1: true }); } diff --git a/src/vs/workbench/contrib/list/browser/list.contribution.ts b/src/vs/workbench/contrib/list/browser/list.contribution.ts index a8593efd37b..a2347a33886 100644 --- a/src/vs/workbench/contrib/list/browser/list.contribution.ts +++ b/src/vs/workbench/contrib/list/browser/list.contribution.ts @@ -20,4 +20,4 @@ export class ListContext implements IWorkbenchContribution { } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ListContext, 'ListContext', LifecyclePhase.Starting); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ListContext, LifecyclePhase.Starting); diff --git a/src/vs/workbench/contrib/localHistory/browser/localHistory.contribution.ts b/src/vs/workbench/contrib/localHistory/browser/localHistory.contribution.ts index 44e11d4afa6..dda52a6f3a5 100644 --- a/src/vs/workbench/contrib/localHistory/browser/localHistory.contribution.ts +++ b/src/vs/workbench/contrib/localHistory/browser/localHistory.contribution.ts @@ -10,4 +10,4 @@ import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle import { LocalHistoryTimeline } from 'vs/workbench/contrib/localHistory/browser/localHistoryTimeline'; // Register Local History Timeline -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(LocalHistoryTimeline, 'LocalHistoryTimeline', LifecyclePhase.Ready); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(LocalHistoryTimeline, LifecyclePhase.Ready); diff --git a/src/vs/workbench/contrib/localization/browser/localization.contribution.ts b/src/vs/workbench/contrib/localization/browser/localization.contribution.ts index 85716e8bcb9..e529fd5414d 100644 --- a/src/vs/workbench/contrib/localization/browser/localization.contribution.ts +++ b/src/vs/workbench/contrib/localization/browser/localization.contribution.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { registerAction2 } from 'vs/platform/actions/common/actions'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { WebLocaleService } from 'vs/workbench/contrib/localization/browser/localeService'; import { ClearDisplayLanguageAction, ConfigureDisplayLanguageAction } from 'vs/workbench/contrib/localization/browser/localizationsActions'; import { ILocaleService } from 'vs/workbench/contrib/localization/common/locale'; -registerSingleton(ILocaleService, WebLocaleService, true); +registerSingleton(ILocaleService, WebLocaleService, InstantiationType.Delayed); // Register action to configure locale and related settings registerAction2(ConfigureDisplayLanguageAction); diff --git a/src/vs/workbench/contrib/localization/electron-sandbox/localization.contribution.ts b/src/vs/workbench/contrib/localization/electron-sandbox/localization.contribution.ts index cc72bb49812..72cf5f2f9f5 100644 --- a/src/vs/workbench/contrib/localization/electron-sandbox/localization.contribution.ts +++ b/src/vs/workbench/contrib/localization/electron-sandbox/localization.contribution.ts @@ -25,11 +25,11 @@ import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/b import { ViewContainerLocation } from 'vs/workbench/common/views'; import { registerAction2 } from 'vs/platform/actions/common/actions'; import { ClearDisplayLanguageAction, ConfigureDisplayLanguageAction } from 'vs/workbench/contrib/localization/browser/localizationsActions'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILocaleService } from 'vs/workbench/contrib/localization/common/locale'; import { NativeLocaleService } from 'vs/workbench/contrib/localization/electron-sandbox/localeService'; -registerSingleton(ILocaleService, NativeLocaleService, true); +registerSingleton(ILocaleService, NativeLocaleService, InstantiationType.Delayed); // Register action to configure locale and related settings registerAction2(ConfigureDisplayLanguageAction); @@ -123,11 +123,11 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo const loc = manifest && manifest.contributes && manifest.contributes.localizations && manifest.contributes.localizations.filter(x => x.languageId.toLowerCase() === locale)[0]; const languageName = loc ? (loc.languageName || locale) : locale; const languageDisplayName = loc ? (loc.localizedLanguageName || loc.languageName || locale) : locale; - const translationsFromPack: any = translation && translation.contents ? translation.contents['vs/workbench/contrib/localization/electron-sandbox/minimalTranslations'] : {}; + const translationsFromPack: { [key: string]: string } = translation?.contents?.['vs/workbench/contrib/localization/electron-sandbox/minimalTranslations'] ?? {}; const promptMessageKey = extensionToInstall ? 'installAndRestartMessage' : 'showLanguagePackExtensions'; const useEnglish = !translationsFromPack[promptMessageKey]; - const translations: any = {}; + const translations: { [key: string]: string } = {}; Object.keys(minimumTranslatedStrings).forEach(key => { if (!translationsFromPack[key] || useEnglish) { translations[key] = minimumTranslatedStrings[key].replace('{0}', languageName); @@ -220,7 +220,7 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo } const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(LocalizationWorkbenchContribution, 'LocalizationWorkbenchContribution', LifecyclePhase.Eventually); +workbenchRegistry.registerWorkbenchContribution(LocalizationWorkbenchContribution, LifecyclePhase.Eventually); ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'localizations', diff --git a/src/vs/workbench/contrib/logs/browser/logs.contribution.ts b/src/vs/workbench/contrib/logs/browser/logs.contribution.ts index 91600e0b61d..b42615bdd09 100644 --- a/src/vs/workbench/contrib/logs/browser/logs.contribution.ts +++ b/src/vs/workbench/contrib/logs/browser/logs.contribution.ts @@ -4,14 +4,18 @@ *--------------------------------------------------------------------------------------------*/ import { Registry } from 'vs/platform/registry/common/platform'; -import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions, CATEGORIES } from 'vs/workbench/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; +import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { OpenWindowSessionLogFileAction } from 'vs/workbench/contrib/logs/common/logsActions'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { Disposable } from 'vs/base/common/lifecycle'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { LogsDataCleaner } from 'vs/workbench/contrib/logs/common/logsDataCleaner'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ILogLevelService, LogLevelService } from 'vs/workbench/contrib/logs/common/logLevelService'; + +registerSingleton(ILogLevelService, LogLevelService, InstantiationType.Delayed); class WebLogOutputChannels extends Disposable implements IWorkbenchContribution { @@ -25,10 +29,22 @@ class WebLogOutputChannels extends Disposable implements IWorkbenchContribution private registerWebContributions(): void { this.instantiationService.createInstance(LogsDataCleaner); - const workbenchActionsRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); - workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(OpenWindowSessionLogFileAction), 'Developer: Open Window Log File (Session)...', CATEGORIES.Developer.value); + registerAction2(class extends Action2 { + constructor() { + super({ + id: OpenWindowSessionLogFileAction.ID, + title: OpenWindowSessionLogFileAction.TITLE, + category: Categories.Developer, + f1: true + }); + } + run(servicesAccessor: ServicesAccessor): Promise { + return servicesAccessor.get(IInstantiationService).createInstance(OpenWindowSessionLogFileAction, OpenWindowSessionLogFileAction.ID, OpenWindowSessionLogFileAction.TITLE.value).run(); + } + }); + } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(WebLogOutputChannels, 'WebLogOutputChannels', LifecyclePhase.Restored); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(WebLogOutputChannels, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/logs/common/logConstants.ts b/src/vs/workbench/contrib/logs/common/logConstants.ts index 0224f0d95e2..a9869d06fc7 100644 --- a/src/vs/workbench/contrib/logs/common/logConstants.ts +++ b/src/vs/workbench/contrib/logs/common/logConstants.ts @@ -6,10 +6,12 @@ export const mainLogChannelId = 'mainLog'; export const sharedLogChannelId = 'sharedLog'; export const rendererLogChannelId = 'rendererLog'; -export const extHostLogChannelId = 'extHostLog'; export const telemetryLogChannelId = 'telemetryLog'; export const extensionTelemetryLogChannelId = 'extensionTelemetryLog'; export const userDataSyncLogChannelId = 'userDataSyncLog'; export const editSessionsLogChannelId = 'editSessionsSyncLog'; +export const remoteServerLog = 'remoteServerLog'; +export const remotePtyHostLog = 'remotePtyHostLog'; + export const showWindowLogActionId = 'workbench.action.showWindowLog'; diff --git a/src/vs/workbench/contrib/logs/common/logLevelService.ts b/src/vs/workbench/contrib/logs/common/logLevelService.ts new file mode 100644 index 00000000000..51e60fc9e0d --- /dev/null +++ b/src/vs/workbench/contrib/logs/common/logLevelService.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ILoggerService, LogLevel } from 'vs/platform/log/common/log'; +import { Emitter, Event } from 'vs/base/common/event'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IOutputService } from 'vs/workbench/services/output/common/output'; + +export const ILogLevelService = createDecorator('ILogLevelService'); +export interface ILogLevelService { + readonly _serviceBrand: undefined; + readonly onDidChangeLogLevel: Event<{ readonly id: string; logLevel: LogLevel }>; + setLogLevel(id: string, logLevel: LogLevel): void; + getLogLevel(id: string): LogLevel | undefined; +} + +export class LogLevelService extends Disposable implements ILogLevelService { + readonly _serviceBrand: undefined; + + private readonly _onDidChangeLogLevel = this._register(new Emitter<{ readonly id: string; logLevel: LogLevel }>()); + readonly onDidChangeLogLevel = this._onDidChangeLogLevel.event; + + private readonly logLevels = new Map(); + + constructor( + @IOutputService protected readonly outputService: IOutputService, + @ILoggerService private readonly loggerService: ILoggerService + ) { + super(); + } + + getLogLevel(id: string): LogLevel | undefined { + return this.logLevels.get(id); + } + + setLogLevel(id: string, logLevel: LogLevel): boolean { + if (this.getLogLevel(id) === logLevel) { + return false; + } + + this.logLevels.set(id, logLevel); + const channel = this.outputService.getChannelDescriptor(id); + const resource = channel?.log ? channel.file : undefined; + if (resource) { + this.loggerService.setLevel(resource, logLevel); + } + this._onDidChangeLogLevel.fire({ id, logLevel }); + return true; + } + +} + diff --git a/src/vs/workbench/contrib/logs/common/logs.contribution.ts b/src/vs/workbench/contrib/logs/common/logs.contribution.ts index d4e9518e352..ee34e86d564 100644 --- a/src/vs/workbench/contrib/logs/common/logs.contribution.ts +++ b/src/vs/workbench/contrib/logs/common/logs.contribution.ts @@ -5,8 +5,8 @@ import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions, CATEGORIES } from 'vs/workbench/common/actions'; -import { Action2, registerAction2, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; +import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { SetLogLevelAction } from 'vs/workbench/contrib/logs/common/logsActions'; import * as Constants from 'vs/workbench/contrib/logs/common/logConstants'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; @@ -16,13 +16,24 @@ import { IOutputService, registerLogChannel } from 'vs/workbench/services/output import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { supportsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; import { IProductService } from 'vs/platform/product/common/productService'; import { URI } from 'vs/base/common/uri'; -const workbenchActionsRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); -workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(SetLogLevelAction), 'Developer: Set Log Level...', CATEGORIES.Developer.value); +registerAction2(class extends Action2 { + constructor() { + super({ + id: SetLogLevelAction.ID, + title: SetLogLevelAction.TITLE, + category: Categories.Developer, + f1: true + }); + } + run(servicesAccessor: ServicesAccessor): Promise { + return servicesAccessor.get(IInstantiationService).createInstance(SetLogLevelAction, SetLogLevelAction.ID, SetLogLevelAction.TITLE.value).run(); + } +}); class LogOutputChannels extends Disposable implements IWorkbenchContribution { @@ -44,7 +55,7 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { const registerTelemetryChannel = () => { if (supportsTelemetry(this.productService, this.environmentService) && this.logService.getLevel() === LogLevel.Trace) { this.registerLogChannel(Constants.telemetryLogChannelId, nls.localize('telemetryLog', "Telemetry"), this.environmentService.telemetryLogResource); - this.registerLogChannel(Constants.extensionTelemetryLogChannelId, nls.localize('extensionTelemetryLog', "Extension Telemetry"), this.environmentService.extensionTelemetryLogResource); + this.registerLogChannel(Constants.extensionTelemetryLogChannelId, nls.localize('extensionTelemetryLog', "Extension Telemetry"), this.environmentService.extHostTelemetryLogFile); return true; } return false; @@ -62,7 +73,7 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { super({ id: Constants.showWindowLogActionId, title: { value: nls.localize('show window log', "Show Window Log"), original: 'Show Window Log' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true }); } @@ -80,4 +91,4 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(LogOutputChannels, 'LogOutputChannels', LifecyclePhase.Restored); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(LogOutputChannels, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/logs/common/logsActions.ts b/src/vs/workbench/contrib/logs/common/logsActions.ts index a53f539aa00..3e9559f30ac 100644 --- a/src/vs/workbench/contrib/logs/common/logsActions.ts +++ b/src/vs/workbench/contrib/logs/common/logsActions.ts @@ -5,63 +5,120 @@ import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; -import { ILogService, LogLevel, DEFAULT_LOG_LEVEL } from 'vs/platform/log/common/log'; -import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; +import { ILogService, LogLevel, getLogLevel, parseLogLevel } from 'vs/platform/log/common/log'; +import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { URI } from 'vs/base/common/uri'; import { IFileService } from 'vs/platform/files/common/files'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { dirname, basename, isEqual } from 'vs/base/common/resources'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IOutputChannelDescriptor, IOutputService } from 'vs/workbench/services/output/common/output'; +import { isUndefined } from 'vs/base/common/types'; +import { ILogLevelService } from 'vs/workbench/contrib/logs/common/logLevelService'; +import { extensionTelemetryLogChannelId, telemetryLogChannelId } from 'vs/workbench/contrib/logs/common/logConstants'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; export class SetLogLevelAction extends Action { static readonly ID = 'workbench.action.setLogLevel'; - static readonly LABEL = nls.localize('setLogLevel', "Set Log Level..."); + static readonly TITLE = { value: nls.localize('setLogLevel', "Set Log Level..."), original: 'Set Log Level...' }; constructor(id: string, label: string, @IQuickInputService private readonly quickInputService: IQuickInputService, - @ILogService private readonly logService: ILogService + @ILogService private readonly logService: ILogService, + @ILogLevelService private readonly logLevelService: ILogLevelService, + @IOutputService private readonly outputService: IOutputService, + @IEnvironmentService private readonly environmentService: IEnvironmentService, ) { super(id, label); } - override run(): Promise { - const current = this.logService.getLevel(); - const entries = [ - { label: nls.localize('trace', "Trace"), level: LogLevel.Trace, description: this.getDescription(LogLevel.Trace, current) }, - { label: nls.localize('debug', "Debug"), level: LogLevel.Debug, description: this.getDescription(LogLevel.Debug, current) }, - { label: nls.localize('info', "Info"), level: LogLevel.Info, description: this.getDescription(LogLevel.Info, current) }, - { label: nls.localize('warn', "Warning"), level: LogLevel.Warning, description: this.getDescription(LogLevel.Warning, current) }, - { label: nls.localize('err', "Error"), level: LogLevel.Error, description: this.getDescription(LogLevel.Error, current) }, - { label: nls.localize('critical', "Critical"), level: LogLevel.Critical, description: this.getDescription(LogLevel.Critical, current) }, - { label: nls.localize('off', "Off"), level: LogLevel.Off, description: this.getDescription(LogLevel.Off, current) }, - ]; - - return this.quickInputService.pick(entries, { placeHolder: nls.localize('selectLogLevel', "Select log level"), activeItem: entries[this.logService.getLevel()] }).then(entry => { - if (entry) { - this.logService.setLevel(entry.level); - } - }); + override async run(): Promise { + const logChannel = await this.selectLogChannel(); + if (!isUndefined(logChannel)) { + await this.selectLogLevel(logChannel); + } } - private getDescription(level: LogLevel, current: LogLevel): string | undefined { - if (DEFAULT_LOG_LEVEL === level && current === level) { - return nls.localize('default and current', "Default & Current"); + private async selectLogChannel(): Promise { + const extensionLogs = [], logs = []; + for (const channel of this.outputService.getChannelDescriptors()) { + if (!channel.log || channel.id === telemetryLogChannelId || channel.id === extensionTelemetryLogChannelId) { + continue; + } + if (channel.extensionId) { + extensionLogs.push(channel); + } else { + logs.push(channel); + } } - if (DEFAULT_LOG_LEVEL === level) { - return nls.localize('default', "Default"); + const entries: ({ label: string; channel?: IOutputChannelDescriptor } | IQuickPickSeparator)[] = []; + entries.push({ label: nls.localize('all', "All") }); + entries.push({ type: 'separator', label: nls.localize('loggers', "Logs") }); + for (const channel of logs.sort((a, b) => a.label.localeCompare(b.label))) { + entries.push({ label: channel.label, channel }); } - if (current === level) { - return nls.localize('current', "Current"); + if (extensionLogs.length && logs.length) { + entries.push({ type: 'separator', label: nls.localize('extensionLogs', "Extension Logs") }); } - return undefined; + for (const channel of extensionLogs.sort((a, b) => a.label.localeCompare(b.label))) { + entries.push({ label: channel.label, channel }); + } + const entry = await this.quickInputService.pick(entries, { placeHolder: nls.localize('selectlog', "Select Log") }); + return entry ? entry.channel ?? null : undefined; + } + + private async selectLogLevel(logChannel: IOutputChannelDescriptor | null): Promise { + const defaultLogLevel = this.getDefaultLogLevel(logChannel); + const current = logChannel ? this.logLevelService.getLogLevel(logChannel.id) ?? defaultLogLevel : this.logService.getLevel(); + const entries = [ + { label: this.getLabel(nls.localize('trace', "Trace"), LogLevel.Trace, current), level: LogLevel.Trace, description: this.getDescription(LogLevel.Trace, defaultLogLevel) }, + { label: this.getLabel(nls.localize('debug', "Debug"), LogLevel.Debug, current), level: LogLevel.Debug, description: this.getDescription(LogLevel.Debug, defaultLogLevel) }, + { label: this.getLabel(nls.localize('info', "Info"), LogLevel.Info, current), level: LogLevel.Info, description: this.getDescription(LogLevel.Info, defaultLogLevel) }, + { label: this.getLabel(nls.localize('warn', "Warning"), LogLevel.Warning, current), level: LogLevel.Warning, description: this.getDescription(LogLevel.Warning, defaultLogLevel) }, + { label: this.getLabel(nls.localize('err', "Error"), LogLevel.Error, current), level: LogLevel.Error, description: this.getDescription(LogLevel.Error, defaultLogLevel) }, + { label: this.getLabel(nls.localize('critical', "Critical"), LogLevel.Critical, current), level: LogLevel.Critical, description: this.getDescription(LogLevel.Critical, defaultLogLevel) }, + { label: this.getLabel(nls.localize('off', "Off"), LogLevel.Off, current), level: LogLevel.Off, description: this.getDescription(LogLevel.Off, defaultLogLevel) }, + ]; + + const entry = await this.quickInputService.pick(entries, { placeHolder: logChannel ? nls.localize('selectLogLevelFor', " {0}: Select log level", logChannel?.label) : nls.localize('selectLogLevel', "Select log level"), activeItem: entries[this.logService.getLevel()] }); + if (entry) { + if (logChannel) { + this.logLevelService.setLogLevel(logChannel.id, entry.level); + } else { + this.logService.setLevel(entry.level); + } + } + } + + private getLabel(label: string, level: LogLevel, current: LogLevel): string { + if (level === current) { + return `$(check) ${label}`; + } + return label; + } + + private getDescription(level: LogLevel, defaultLogLevel: LogLevel): string | undefined { + return defaultLogLevel === level ? nls.localize('default', "Default") : undefined; + } + + private getDefaultLogLevel(outputChannel: IOutputChannelDescriptor | null): LogLevel { + let logLevel: LogLevel | undefined; + if (outputChannel?.extensionId) { + const logLevelValue = this.environmentService.extensionLogLevel?.find(([id]) => areSameExtensions({ id }, { id: outputChannel.extensionId! }))?.[1]; + if (logLevelValue) { + logLevel = parseLogLevel(logLevelValue); + } + } + return logLevel ?? getLogLevel(this.environmentService); } } export class OpenWindowSessionLogFileAction extends Action { static readonly ID = 'workbench.action.openSessionLogFile'; - static readonly LABEL = nls.localize('openSessionLogFile', "Open Window Log File (Session)..."); + static readonly TITLE = { value: nls.localize('openSessionLogFile', "Open Window Log File (Session)..."), original: 'Open Window Log File (Session)...' }; constructor(id: string, label: string, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, diff --git a/src/vs/workbench/contrib/logs/electron-sandbox/logLevelService.ts b/src/vs/workbench/contrib/logs/electron-sandbox/logLevelService.ts new file mode 100644 index 00000000000..de269ec1db2 --- /dev/null +++ b/src/vs/workbench/contrib/logs/electron-sandbox/logLevelService.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 { ILoggerService, LogLevel } from 'vs/platform/log/common/log'; +import { IOutputService } from 'vs/workbench/services/output/common/output'; +import { IMainProcessService, ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services'; +import { LogLevelService as CommonLogLevelService } from 'vs/workbench/contrib/logs/common/logLevelService'; +import { remotePtyHostLog, remoteServerLog, sharedLogChannelId, userDataSyncLogChannelId } from 'vs/workbench/contrib/logs/common/logConstants'; +import { LogLevelChannelClient } from 'vs/platform/log/common/logIpc'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; + +export class LogLevelService extends CommonLogLevelService { + + constructor( + @IOutputService outputService: IOutputService, + @ILoggerService loggerService: ILoggerService, + @ISharedProcessService private readonly sharedProcessService: ISharedProcessService, + @IMainProcessService private readonly mainProcessService: IMainProcessService, + @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, + ) { + super(outputService, loggerService); + } + + override setLogLevel(id: string, logLevel: LogLevel): boolean { + if (!super.setLogLevel(id, logLevel)) { + return false; + } + + const channel = this.outputService.getChannelDescriptor(id); + const resource = channel?.log ? channel.file : undefined; + + LogLevelChannelClient.setLevel(this.mainProcessService.getChannel('logLevel'), logLevel, resource); + if (id === sharedLogChannelId || id === userDataSyncLogChannelId) { + LogLevelChannelClient.setLevel(this.sharedProcessService.getChannel('logLevel'), logLevel, resource); + return true; + } + + const connection = this.remoteAgentService.getConnection(); + if ((id === remoteServerLog || id === remotePtyHostLog) && connection) { + connection.withChannel('logger', (channel) => LogLevelChannelClient.setLevel(channel, logLevel, resource)); + return true; + } + + return true; + } + +} + diff --git a/src/vs/workbench/contrib/logs/electron-sandbox/logs.contribution.ts b/src/vs/workbench/contrib/logs/electron-sandbox/logs.contribution.ts index 5032ac87a6b..57f5dadd802 100644 --- a/src/vs/workbench/contrib/logs/electron-sandbox/logs.contribution.ts +++ b/src/vs/workbench/contrib/logs/electron-sandbox/logs.contribution.ts @@ -5,8 +5,8 @@ import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions, CATEGORIES } from 'vs/workbench/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; +import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { OpenLogsFolderAction, OpenExtensionLogsFolderAction } from 'vs/workbench/contrib/logs/electron-sandbox/logsActions'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import * as Constants from 'vs/workbench/contrib/logs/common/logConstants'; @@ -18,6 +18,13 @@ import { URI } from 'vs/base/common/uri'; import { join } from 'vs/base/common/path'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { registerLogChannel } from 'vs/workbench/services/output/common/output'; +import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ILogLevelService } from 'vs/workbench/contrib/logs/common/logLevelService'; +import { LogLevelService } from 'vs/workbench/contrib/logs/electron-sandbox/logLevelService'; + +registerSingleton(ILogLevelService, LogLevelService, InstantiationType.Delayed); class NativeLogOutputChannels extends Disposable implements IWorkbenchContribution { @@ -42,8 +49,32 @@ class NativeLogOutputChannels extends Disposable implements IWorkbenchContributi } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(NativeLogOutputChannels, 'NativeLogOutputChannels', LifecyclePhase.Restored); +registerAction2(class extends Action2 { + constructor() { + super({ + id: OpenLogsFolderAction.ID, + title: OpenLogsFolderAction.TITLE, + category: Categories.Developer, + f1: true + }); + } + run(servicesAccessor: ServicesAccessor): Promise { + return servicesAccessor.get(IInstantiationService).createInstance(OpenLogsFolderAction, OpenLogsFolderAction.ID, OpenLogsFolderAction.TITLE.value).run(); + } +}); -const workbenchActionsRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); -workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(OpenLogsFolderAction), 'Developer: Open Logs Folder', CATEGORIES.Developer.value); -workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(OpenExtensionLogsFolderAction), 'Developer: Open Extension Logs Folder', CATEGORIES.Developer.value); +registerAction2(class extends Action2 { + constructor() { + super({ + id: OpenExtensionLogsFolderAction.ID, + title: OpenExtensionLogsFolderAction.TITLE, + category: Categories.Developer, + f1: true + }); + } + run(servicesAccessor: ServicesAccessor): Promise { + return servicesAccessor.get(IInstantiationService).createInstance(OpenExtensionLogsFolderAction, OpenExtensionLogsFolderAction.ID, OpenExtensionLogsFolderAction.TITLE.value).run(); + } +}); + +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(NativeLogOutputChannels, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/logs/electron-sandbox/logsActions.ts b/src/vs/workbench/contrib/logs/electron-sandbox/logsActions.ts index 1ea86c56d77..25a15ac8d9a 100644 --- a/src/vs/workbench/contrib/logs/electron-sandbox/logsActions.ts +++ b/src/vs/workbench/contrib/logs/electron-sandbox/logsActions.ts @@ -14,7 +14,7 @@ import { IFileService } from 'vs/platform/files/common/files'; export class OpenLogsFolderAction extends Action { static readonly ID = 'workbench.action.openLogsFolder'; - static readonly LABEL = nls.localize('openLogsFolder', "Open Logs Folder"); + static readonly TITLE = { value: nls.localize('openLogsFolder', "Open Logs Folder"), original: 'Open Logs Folder' }; constructor(id: string, label: string, @INativeWorkbenchEnvironmentService private readonly environmentService: INativeWorkbenchEnvironmentService, @@ -31,7 +31,7 @@ export class OpenLogsFolderAction extends Action { export class OpenExtensionLogsFolderAction extends Action { static readonly ID = 'workbench.action.openExtensionLogsFolder'; - static readonly LABEL = nls.localize('openExtensionLogsFolder', "Open Extension Logs Folder"); + static readonly TITLE = { value: nls.localize('openExtensionLogsFolder', "Open Extension Logs Folder"), original: 'Open Extension Logs Folder' }; constructor(id: string, label: string, @INativeWorkbenchEnvironmentService private readonly environmentSerice: INativeWorkbenchEnvironmentService, diff --git a/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts b/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts index f45f3a3c145..be11c3073ba 100644 --- a/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts +++ b/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts @@ -6,6 +6,7 @@ import { hookDomPurifyHrefAndSrcSanitizer, basicMarkupHtmlTags } from 'vs/base/browser/dom'; import * as dompurify from 'vs/base/browser/dompurify/dompurify'; import { allowedMarkdownAttr } from 'vs/base/browser/markdownRenderer'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { marked } from 'vs/base/common/marked/marked'; import { Schemas } from 'vs/base/common/network'; import { ILanguageService } from 'vs/editor/common/languages/language'; @@ -189,6 +190,7 @@ export async function renderMarkdownDocument( languageService: ILanguageService, shouldSanitize: boolean = true, allowUnknownProtocols: boolean = false, + token?: CancellationToken, ): Promise { const highlight = (code: string, lang: string | undefined, callback: ((error: any, code: string) => void) | undefined): any => { @@ -202,6 +204,11 @@ export async function renderMarkdownDocument( } extensionService.whenInstalledExtensionsRegistered().then(async () => { + if (token?.isCancellationRequested) { + callback(null, ''); + return; + } + const languageId = languageService.getLanguageIdByLanguageName(lang); const html = await tokenizeToString(languageService, code, languageId); callback(null, `${html}`); diff --git a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts index f19aa0a7492..275a84b8c9a 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts @@ -6,7 +6,7 @@ import 'vs/workbench/contrib/markers/browser/markersFileDecorations'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { localize } from 'vs/nls'; @@ -32,6 +32,7 @@ import { Codicon } from 'vs/base/common/codicons'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { ViewAction } from 'vs/workbench/browser/parts/views/viewPane'; import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; +import { viewFilterSubmenu } from 'vs/workbench/browser/parts/views/viewFilter'; KeybindingsRegistry.registerCommandAndKeybindingRule({ id: Markers.MARKER_OPEN_ACTION_ID, @@ -196,12 +197,137 @@ registerAction2(class extends ViewAction { } }); +registerAction2(class extends ViewAction { + constructor() { + super({ + id: `workbench.actions.${Markers.MARKERS_VIEW_ID}.toggleErrors`, + title: localize('toggle errors', "Toggle Errors"), + category: localize('problems', "Problems"), + toggled: { + condition: MarkersContextKeys.ShowErrorsFilterContextKey, + title: localize('errors', "Show Errors") + }, + menu: { + id: viewFilterSubmenu, + group: '1_filter', + when: ContextKeyExpr.equals('view', Markers.MARKERS_VIEW_ID), + order: 1 + }, + viewId: Markers.MARKERS_VIEW_ID + }); + } + + async runInView(serviceAccessor: ServicesAccessor, view: IMarkersView): Promise { + view.filters.showErrors = !view.filters.showErrors; + } +}); + +registerAction2(class extends ViewAction { + constructor() { + super({ + id: `workbench.actions.${Markers.MARKERS_VIEW_ID}.toggleWarnings`, + title: localize('toggle warnings', "Toggle Warnings"), + category: localize('problems', "Problems"), + toggled: { + condition: MarkersContextKeys.ShowWarningsFilterContextKey, + title: localize('warnings', "Show Warnings") + }, + menu: { + id: viewFilterSubmenu, + group: '1_filter', + when: ContextKeyExpr.equals('view', Markers.MARKERS_VIEW_ID), + order: 2 + }, + viewId: Markers.MARKERS_VIEW_ID + }); + } + + async runInView(serviceAccessor: ServicesAccessor, view: IMarkersView): Promise { + view.filters.showWarnings = !view.filters.showWarnings; + } +}); + +registerAction2(class extends ViewAction { + constructor() { + super({ + id: `workbench.actions.${Markers.MARKERS_VIEW_ID}.toggleInfos`, + title: localize('toggle infos', "Toggle Infos"), + category: localize('problems', "Problems"), + toggled: { + condition: MarkersContextKeys.ShowInfoFilterContextKey, + title: localize('Infos', "Show Infos") + }, + menu: { + id: viewFilterSubmenu, + group: '1_filter', + when: ContextKeyExpr.equals('view', Markers.MARKERS_VIEW_ID), + order: 3 + }, + viewId: Markers.MARKERS_VIEW_ID + }); + } + + async runInView(serviceAccessor: ServicesAccessor, view: IMarkersView): Promise { + view.filters.showInfos = !view.filters.showInfos; + } +}); + +registerAction2(class extends ViewAction { + constructor() { + super({ + id: `workbench.actions.${Markers.MARKERS_VIEW_ID}.toggleActiveFile`, + title: localize('toggle active file', "Toggle Active File"), + category: localize('problems', "Problems"), + toggled: { + condition: MarkersContextKeys.ShowActiveFileFilterContextKey, + title: localize('Active File', "Show Active File Only") + }, + menu: { + id: viewFilterSubmenu, + group: '2_filter', + when: ContextKeyExpr.equals('view', Markers.MARKERS_VIEW_ID), + order: 1 + }, + viewId: Markers.MARKERS_VIEW_ID + }); + } + + async runInView(serviceAccessor: ServicesAccessor, view: IMarkersView): Promise { + view.filters.activeFile = !view.filters.activeFile; + } +}); + +registerAction2(class extends ViewAction { + constructor() { + super({ + id: `workbench.actions.${Markers.MARKERS_VIEW_ID}.toggleExcludedFiles`, + title: localize('toggle Excluded Files', "Toggle Excluded Files"), + category: localize('problems', "Problems"), + toggled: { + condition: MarkersContextKeys.ShowExcludedFilesFilterContextKey, + title: localize('Excluded Files', "Hide Excluded Files") + }, + menu: { + id: viewFilterSubmenu, + group: '2_filter', + when: ContextKeyExpr.equals('view', Markers.MARKERS_VIEW_ID), + order: 2 + }, + viewId: Markers.MARKERS_VIEW_ID + }); + } + + async runInView(serviceAccessor: ServicesAccessor, view: IMarkersView): Promise { + view.filters.excludedFiles = !view.filters.excludedFiles; + } +}); + registerAction2(class extends Action2 { constructor() { super({ id: 'workbench.action.problems.focus', title: { value: Messages.MARKERS_PANEL_SHOW_LABEL, original: 'Focus Problems (Errors, Warnings, Infos)' }, - category: CATEGORIES.View, + category: Categories.View, f1: true, }); } @@ -406,22 +532,6 @@ registerAction2(class extends ViewAction { } }); -registerAction2(class extends Action2 { - constructor() { - super({ - id: `workbench.actions.treeView.${Markers.MARKERS_VIEW_ID}.filter`, - title: localize('filter', "Filter"), - menu: { - id: MenuId.ViewTitle, - when: ContextKeyExpr.and(ContextKeyExpr.equals('view', Markers.MARKERS_VIEW_ID), MarkersContextKeys.MarkersViewSmallLayoutContextKey.negate()), - group: 'navigation', - order: 1, - }, - }); - } - async run(): Promise { } -}); - registerAction2(class extends Action2 { constructor() { super({ @@ -513,7 +623,7 @@ class MarkersStatusBarContributions extends Disposable implements IWorkbenchCont } } -workbenchRegistry.registerWorkbenchContribution(MarkersStatusBarContributions, 'MarkersStatusBarContributions', LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(MarkersStatusBarContributions, LifecyclePhase.Restored); class ActivityUpdater extends Disposable implements IWorkbenchContribution { @@ -536,4 +646,4 @@ class ActivityUpdater extends Disposable implements IWorkbenchContribution { } } -workbenchRegistry.registerWorkbenchContribution(ActivityUpdater, 'ActivityUpdater', LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(ActivityUpdater, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/markers/browser/markers.ts b/src/vs/workbench/contrib/markers/browser/markers.ts index 0969177554d..d1d3f5cf7f6 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.ts @@ -4,17 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { MarkersFilters } from 'vs/workbench/contrib/markers/browser/markersViewActions'; -import { Event } from 'vs/base/common/event'; import { IView } from 'vs/workbench/common/views'; import { MarkerElement, ResourceMarkers } from 'vs/workbench/contrib/markers/browser/markersModel'; import { MarkersViewMode } from 'vs/workbench/contrib/markers/common/markers'; export interface IMarkersView extends IView { - readonly onDidFocusFilter: Event; - readonly onDidClearFilterText: Event; readonly filters: MarkersFilters; - readonly onDidChangeFilterStats: Event<{ total: number; filtered: number }>; focusFilter(): void; clearFilterText(): void; getFilterStats(): { total: number; filtered: number }; diff --git a/src/vs/workbench/contrib/markers/browser/markersFileDecorations.ts b/src/vs/workbench/contrib/markers/browser/markersFileDecorations.ts index 88b5c1a57fd..8ca2e6bce00 100644 --- a/src/vs/workbench/contrib/markers/browser/markersFileDecorations.ts +++ b/src/vs/workbench/contrib/markers/browser/markersFileDecorations.ts @@ -107,4 +107,4 @@ Registry.as(ConfigurationExtensions.Configuration).regis // register file decorations Registry.as(WorkbenchExtensions.Workbench) - .registerWorkbenchContribution(MarkersFileDecorations, 'MarkersFileDecorations', LifecyclePhase.Restored); + .registerWorkbenchContribution(MarkersFileDecorations, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/markers/browser/markersView.ts b/src/vs/workbench/contrib/markers/browser/markersView.ts index 826270e143a..f54ff823334 100644 --- a/src/vs/workbench/contrib/markers/browser/markersView.ts +++ b/src/vs/workbench/contrib/markers/browser/markersView.ts @@ -7,12 +7,12 @@ import 'vs/css!./media/markers'; import { URI } from 'vs/base/common/uri'; import * as dom from 'vs/base/browser/dom'; -import { IAction, Action, Separator } from 'vs/base/common/actions'; +import { IAction, Separator } from 'vs/base/common/actions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { Marker, ResourceMarkers, RelatedInformation, MarkerChangesEvent, MarkersModel, compareMarkersByUri, MarkerElement, MarkerTableItem } from 'vs/workbench/contrib/markers/browser/markersModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { MarkersFilterActionViewItem, MarkersFilters, IMarkersFiltersChangeEvent } from 'vs/workbench/contrib/markers/browser/markersViewActions'; +import { MarkersFilters, IMarkersFiltersChangeEvent } from 'vs/workbench/contrib/markers/browser/markersViewActions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import Messages from 'vs/workbench/contrib/markers/browser/messages'; import { RangeHighlightDecorations } from 'vs/workbench/browser/codeeditor'; @@ -23,7 +23,7 @@ import { localize } from 'vs/nls'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { Iterable } from 'vs/base/common/iterator'; import { ITreeElement, ITreeNode, ITreeContextMenuEvent, ITreeRenderer, ITreeEvent } from 'vs/base/browser/ui/tree/tree'; -import { Relay, Event, Emitter } from 'vs/base/common/event'; +import { Relay, Event } from 'vs/base/common/event'; import { WorkbenchObjectTree, IListService, IWorkbenchObjectTreeOptions, IOpenEvent } from 'vs/platform/list/browser/listService'; import { FilterOptions } from 'vs/workbench/contrib/markers/browser/markersFilterOptions'; import { IExpression } from 'vs/base/common/glob'; @@ -31,8 +31,7 @@ import { deepClone } from 'vs/base/common/objects'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { FilterData, Filter, VirtualDelegate, ResourceMarkersRenderer, MarkerRenderer, RelatedInformationRenderer, MarkersWidgetAccessibilityProvider, MarkersViewModel } from 'vs/workbench/contrib/markers/browser/markersTreeViewer'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { ActionBar, IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { MenuId } from 'vs/platform/actions/common/actions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ResourceLabels } from 'vs/workbench/browser/labels'; @@ -42,7 +41,7 @@ import { MementoObject, Memento } from 'vs/workbench/common/memento'; import { IIdentityProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { KeyCode } from 'vs/base/common/keyCodes'; import { editorLightBulbForeground, editorLightBulbAutoFixForeground } from 'vs/platform/theme/common/colorRegistry'; -import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPane'; +import { IViewPaneOptions, FilterViewPane } from 'vs/workbench/browser/parts/views/viewPane'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService, withSelection } from 'vs/platform/opener/common/opener'; import { Codicon } from 'vs/base/common/codicons'; @@ -53,7 +52,6 @@ import { groupBy } from 'vs/base/common/arrays'; import { ResourceMap } from 'vs/base/common/map'; import { EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor'; import { IMarkersView } from 'vs/workbench/contrib/markers/browser/markers'; -import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { ResourceListDnDHandler } from 'vs/workbench/browser/dnd'; import { ITableContextMenuEvent, ITableEvent } from 'vs/base/browser/ui/table/table'; import { MarkersTable } from 'vs/workbench/contrib/markers/browser/markersTable'; @@ -95,7 +93,7 @@ export interface IProblemsWidget { updateMarker(marker: Marker): void; } -export class MarkersView extends ViewPane implements IMarkersView { +export class MarkersView extends FilterViewPane implements IMarkersView { private lastSelectedRelativeTop: number = 0; private currentActiveResource: URI | null = null; @@ -110,7 +108,6 @@ export class MarkersView extends ViewPane implements IMarkersView { private widgetContainer!: HTMLElement; private widgetIdentityProvider: IIdentityProvider; private widgetAccessibilityProvider: MarkersWidgetAccessibilityProvider; - private filterActionBar: ActionBar | undefined; private messageBoxContainer: HTMLElement | undefined; private ariaLabelElement: HTMLElement | undefined; readonly filters: MarkersFilters; @@ -119,24 +116,13 @@ export class MarkersView extends ViewPane implements IMarkersView { private currentWidth = 0; private readonly panelState: MementoObject; - private _onDidChangeFilterStats = this._register(new Emitter<{ total: number; filtered: number }>()); - readonly onDidChangeFilterStats: Event<{ total: number; filtered: number }> = this._onDidChangeFilterStats.event; private cachedFilterStats: { total: number; filtered: number } | undefined = undefined; private currentResourceGotAddedToMarkersData: boolean = false; private readonly markersViewModel: MarkersViewModel; - private readonly smallLayoutContextKey: IContextKey; - private get smallLayout(): boolean { return !!this.smallLayoutContextKey.get(); } - private set smallLayout(smallLayout: boolean) { this.smallLayoutContextKey.set(smallLayout); } readonly onDidChangeVisibility = this.onDidChangeBodyVisibility; - private readonly _onDidFocusFilter: Emitter = this._register(new Emitter()); - readonly onDidFocusFilter: Event = this._onDidFocusFilter.event; - - private readonly _onDidClearFilterText: Emitter = this._register(new Emitter()); - readonly onDidClearFilterText: Event = this._onDidClearFilterText.event; - constructor( options: IViewPaneOptions, @IInstantiationService instantiationService: IInstantiationService, @@ -148,16 +134,24 @@ export class MarkersView extends ViewPane implements IMarkersView { @IContextKeyService contextKeyService: IContextKeyService, @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @IContextMenuService contextMenuService: IContextMenuService, - @IMenuService private readonly menuService: IMenuService, @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, @IKeybindingService keybindingService: IKeybindingService, @IStorageService storageService: IStorageService, @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); - this.smallLayoutContextKey = MarkersContextKeys.MarkersViewSmallLayoutContextKey.bindTo(this.contextKeyService); - this.panelState = new Memento(Markers.MARKERS_VIEW_STORAGE_ID, storageService).getMemento(StorageScope.WORKSPACE, StorageTarget.USER); + const panelState = new Memento(Markers.MARKERS_VIEW_STORAGE_ID, storageService).getMemento(StorageScope.WORKSPACE, StorageTarget.USER); + super({ + ...options, + filterOptions: { + ariaLabel: Messages.MARKERS_PANEL_FILTER_ARIA_LABEL, + placeholder: Messages.MARKERS_PANEL_FILTER_PLACEHOLDER, + focusContextKey: MarkersContextKeys.MarkerViewFilterFocusContextKey.key, + text: panelState['filter'] || '', + history: panelState['filterHistory'] || [] + } + }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); + this.panelState = panelState; this.markersModel = this._register(instantiationService.createInstance(MarkersModel)); this.markersViewModel = this._register(instantiationService.createInstance(MarkersViewModel, this.panelState['multiline'], this.panelState['viewMode'] ?? this.getDefaultViewMode())); @@ -173,15 +167,13 @@ export class MarkersView extends ViewPane implements IMarkersView { this.rangeHighlightDecorations = this._register(this.instantiationService.createInstance(RangeHighlightDecorations)); this.filters = this._register(new MarkersFilters({ - filterText: this.panelState['filter'] || '', filterHistory: this.panelState['filterHistory'] || [], showErrors: this.panelState['showErrors'] !== false, showWarnings: this.panelState['showWarnings'] !== false, showInfos: this.panelState['showInfos'] !== false, excludedFiles: !!this.panelState['useFilesExclude'], activeFile: !!this.panelState['activeFile'], - layout: new dom.Dimension(0, 0) - })); + }, this.contextKeyService)); // Update filter, whenever the "files.exclude" setting is changed this._register(this.configurationService.onDidChangeConfiguration(e => { @@ -205,9 +197,6 @@ export class MarkersView extends ViewPane implements IMarkersView { this.createArialLabelElement(panelContainer); - this.createFilterActionBar(panelContainer); - this.filterActionBar!.push(new Action(`workbench.actions.treeView.${this.id}.filter`)); - this.createMessageBox(panelContainer); this.widgetContainer = dom.append(panelContainer, dom.$('.widget-container')); @@ -221,19 +210,11 @@ export class MarkersView extends ViewPane implements IMarkersView { return Messages.MARKERS_PANEL_TITLE_PROBLEMS; } - public override layoutBody(height: number = this.currentHeight, width: number = this.currentWidth): void { - super.layoutBody(height, width); - const wasSmallLayout = this.smallLayout; - this.smallLayout = width < 600 && height > 100; - if (this.smallLayout !== wasSmallLayout) { - this.filterActionBar?.getContainer().classList.toggle('hide', !this.smallLayout); - } - const contentHeight = this.smallLayout ? height - 44 : height; + public override layoutBodyContent(height: number = this.currentHeight, width: number = this.currentWidth): void { if (this.messageBoxContainer) { - this.messageBoxContainer.style.height = `${contentHeight}px`; + this.messageBoxContainer.style.height = `${height}px`; } - this.widget.layout(contentHeight, width); - this.filters.layout = new dom.Dimension(this.smallLayout ? width : width - 200, height); + this.widget.layout(height, width); this.currentHeight = height; this.currentWidth = width; @@ -253,11 +234,19 @@ export class MarkersView extends ViewPane implements IMarkersView { } public focusFilter(): void { - this._onDidFocusFilter.fire(); + this.filterWidget.focus(); + } + + public updateBadge(total: number, filtered: number): void { + this.filterWidget.updateBadge(total === filtered || total === 0 ? undefined : localize('showing filtered problems', "Showing {0} of {1}", filtered, total)); + } + + public checkMoreFilters(): void { + this.filterWidget.checkMoreFilters(!this.filters.showErrors || !this.filters.showWarnings || !this.filters.showInfos || this.filters.excludedFiles || this.filters.activeFile); } public clearFilterText(): void { - this._onDidClearFilterText.fire(); + this.filterWidget.setFilterText(''); } public showQuickFixes(marker: Marker): void { @@ -325,7 +314,8 @@ export class MarkersView extends ViewPane implements IMarkersView { this.toggleVisibility(total === 0 || filtered === 0); this.renderMessage(); - this._onDidChangeFilterStats.fire(this.getFilterStats()); + this.updateBadge(total, filtered); + this.checkMoreFilters(); } } @@ -338,7 +328,7 @@ export class MarkersView extends ViewPane implements IMarkersView { } private updateFilter() { - this.filter.options = new FilterOptions(this.filters.filterText, this.getFilesExcludeExpressions(), this.filters.showWarnings, this.filters.showErrors, this.filters.showInfos, this.uriIdentityService); + this.filter.options = new FilterOptions(this.filterWidget.getFilterText(), this.getFilesExcludeExpressions(), this.filters.showWarnings, this.filters.showErrors, this.filters.showInfos, this.uriIdentityService); this.widget.filterMarkers(this.getResourceMarkers(), this.filter.options); this.cachedFilterStats = undefined; @@ -346,7 +336,8 @@ export class MarkersView extends ViewPane implements IMarkersView { this.toggleVisibility(total === 0 || filtered === 0); this.renderMessage(); - this._onDidChangeFilterStats.fire(this.getFilterStats()); + this.updateBadge(total, filtered); + this.checkMoreFilters(); } private getDefaultViewMode(): MarkersViewMode { @@ -391,12 +382,6 @@ export class MarkersView extends ViewPane implements IMarkersView { return resourceMarkers; } - private createFilterActionBar(parent: HTMLElement): void { - this.filterActionBar = this._register(new ActionBar(parent, { actionViewItemProvider: action => this.getActionViewItem(action) })); - this.filterActionBar.getContainer().classList.add('markers-panel-filter-container'); - this.filterActionBar.getContainer().classList.toggle('hide', !this.smallLayout); - } - private createMessageBox(parent: HTMLElement): void { this.messageBoxContainer = dom.append(parent, dom.$('.message-box-container')); this.messageBoxContainer.setAttribute('aria-labelledby', 'markers-panel-arialabel'); @@ -560,10 +545,11 @@ export class MarkersView extends ViewPane implements IMarkersView { disposables.push(this.filters.onDidChange((event: IMarkersFiltersChangeEvent) => { if (event.activeFile) { this.refreshPanel(); - } else if (event.filterText || event.excludedFiles || event.showWarnings || event.showErrors || event.showInfos) { + } else if (event.excludedFiles || event.showWarnings || event.showErrors || event.showInfos) { this.updateFilter(); } })); + disposables.push(this.filterWidget.onDidChangeFilterText(e => this.updateFilter())); disposables.push(toDisposable(() => { this.cachedFilterStats = undefined; })); disposables.push(toDisposable(() => this.rangeHighlightDecorations.removeHighlightRange())); @@ -746,7 +732,7 @@ export class MarkersView extends ViewPane implements IMarkersView { } private clearFilters(): void { - this.filters.filterText = ''; + this.filterWidget.setFilterText(''); this.filters.excludedFiles = false; this.filters.showErrors = true; this.filters.showWarnings = true; @@ -803,6 +789,7 @@ export class MarkersView extends ViewPane implements IMarkersView { this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor!, + menuId: MenuId.ProblemsPanelContext, getActions: () => this.getMenuActions(element), getActionViewItem: (action) => { const keybinding = this.keybindingService.lookupKeybinding(action.id); @@ -833,9 +820,6 @@ export class MarkersView extends ViewPane implements IMarkersView { } } - const menu = this.menuService.createMenu(MenuId.ProblemsPanelContext, this.widget.contextKeyService); - createAndFillInContextMenuActions(menu, undefined, result); - menu.dispose(); return result; } @@ -866,13 +850,6 @@ export class MarkersView extends ViewPane implements IMarkersView { return this.markersModel.resourceMarkers; } - public override getActionViewItem(action: IAction): IActionViewItem | undefined { - if (action.id === `workbench.actions.treeView.${this.id}.filter`) { - return this.instantiationService.createInstance(MarkersFilterActionViewItem, action, this); - } - return super.getActionViewItem(action); - } - getFilterStats(): { total: number; filtered: number } { if (!this.cachedFilterStats) { this.cachedFilterStats = { @@ -886,11 +863,11 @@ export class MarkersView extends ViewPane implements IMarkersView { private toggleVisibility(hide: boolean): void { this.widget.toggleVisibility(hide); - this.layoutBody(); + this.layoutBodyContent(); } override saveState(): void { - this.panelState['filter'] = this.filters.filterText; + this.panelState['filter'] = this.filterWidget.getFilterText(); this.panelState['filterHistory'] = this.filters.filterHistory; this.panelState['showErrors'] = this.filters.showErrors; this.panelState['showWarnings'] = this.filters.showWarnings; diff --git a/src/vs/workbench/contrib/markers/browser/markersViewActions.ts b/src/vs/workbench/contrib/markers/browser/markersViewActions.ts index 3b0da4b071d..65d23126b72 100644 --- a/src/vs/workbench/contrib/markers/browser/markersViewActions.ts +++ b/src/vs/workbench/contrib/markers/browser/markersViewActions.ts @@ -3,54 +3,35 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Delayer } from 'vs/base/common/async'; import * as DOM from 'vs/base/browser/dom'; -import { Action, IAction, IActionRunner, Separator } from 'vs/base/common/actions'; -import { HistoryInputBox } from 'vs/base/browser/ui/inputbox/inputBox'; -import { KeyCode } from 'vs/base/common/keyCodes'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { Action, IAction } from 'vs/base/common/actions'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import Messages from 'vs/workbench/contrib/markers/browser/messages'; -import { IThemeService, registerThemingParticipant, ICssStyleCollector, IColorTheme, ThemeIcon } from 'vs/platform/theme/common/themeService'; -import { attachInputBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; -import { toDisposable, Disposable } from 'vs/base/common/lifecycle'; -import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { badgeBackground, badgeForeground, contrastBorder, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground } from 'vs/platform/theme/common/colorRegistry'; -import { localize } from 'vs/nls'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ContextScopedHistoryInputBox } from 'vs/platform/history/browser/contextScopedHistoryWidget'; +import { registerThemingParticipant, ICssStyleCollector, IColorTheme } from 'vs/platform/theme/common/themeService'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground } from 'vs/platform/theme/common/colorRegistry'; import { Marker } from 'vs/workbench/contrib/markers/browser/markersModel'; -import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { Event, Emitter } from 'vs/base/common/event'; -import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { Codicon } from 'vs/base/common/codicons'; -import { BaseActionViewItem, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; -import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; -import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; -import { IMarkersView } from 'vs/workbench/contrib/markers/browser/markers'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { showHistoryKeybindingHint } from 'vs/platform/history/browser/historyWidgetKeybindingHint'; +import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { MarkersContextKeys } from 'vs/workbench/contrib/markers/common/markers'; export interface IMarkersFiltersChangeEvent { - filterText?: boolean; excludedFiles?: boolean; showWarnings?: boolean; showErrors?: boolean; showInfos?: boolean; activeFile?: boolean; - layout?: boolean; } export interface IMarkersFiltersOptions { - filterText: string; filterHistory: string[]; showErrors: boolean; showWarnings: boolean; showInfos: boolean; excludedFiles: boolean; activeFile: boolean; - layout: DOM.Dimension; } export class MarkersFilters extends Disposable { @@ -58,384 +39,74 @@ export class MarkersFilters extends Disposable { private readonly _onDidChange: Emitter = this._register(new Emitter()); readonly onDidChange: Event = this._onDidChange.event; - constructor(options: IMarkersFiltersOptions) { + constructor(options: IMarkersFiltersOptions, private readonly contextKeyService: IContextKeyService) { super(); - this._filterText = options.filterText; - this._showErrors = options.showErrors; - this._showWarnings = options.showWarnings; - this._showInfos = options.showInfos; - this._excludedFiles = options.excludedFiles; - this._activeFile = options.activeFile; - this.filterHistory = options.filterHistory; - this._layout = options.layout; - } - private _filterText: string; - get filterText(): string { - return this._filterText; - } - set filterText(filterText: string) { - if (this._filterText !== filterText) { - this._filterText = filterText; - this._onDidChange.fire({ filterText: true }); - } + this._showErrors.set(options.showErrors); + this._showWarnings.set(options.showWarnings); + this._showInfos.set(options.showInfos); + this._excludedFiles.set(options.excludedFiles); + this._activeFile.set(options.activeFile); + this.filterHistory = options.filterHistory; } filterHistory: string[]; - private _excludedFiles: boolean; + private readonly _excludedFiles = MarkersContextKeys.ShowExcludedFilesFilterContextKey.bindTo(this.contextKeyService); get excludedFiles(): boolean { - return this._excludedFiles; + return !!this._excludedFiles.get(); } set excludedFiles(filesExclude: boolean) { - if (this._excludedFiles !== filesExclude) { - this._excludedFiles = filesExclude; + if (this._excludedFiles.get() !== filesExclude) { + this._excludedFiles.set(filesExclude); this._onDidChange.fire({ excludedFiles: true }); } } - private _activeFile: boolean; + private readonly _activeFile = MarkersContextKeys.ShowActiveFileFilterContextKey.bindTo(this.contextKeyService); get activeFile(): boolean { - return this._activeFile; + return !!this._activeFile.get(); } set activeFile(activeFile: boolean) { - if (this._activeFile !== activeFile) { - this._activeFile = activeFile; + if (this._activeFile.get() !== activeFile) { + this._activeFile.set(activeFile); this._onDidChange.fire({ activeFile: true }); } } - private _showWarnings: boolean = true; + private readonly _showWarnings = MarkersContextKeys.ShowWarningsFilterContextKey.bindTo(this.contextKeyService); get showWarnings(): boolean { - return this._showWarnings; + return !!this._showWarnings.get(); } set showWarnings(showWarnings: boolean) { - if (this._showWarnings !== showWarnings) { - this._showWarnings = showWarnings; + if (this._showWarnings.get() !== showWarnings) { + this._showWarnings.set(showWarnings); this._onDidChange.fire({ showWarnings: true }); } } - private _showErrors: boolean = true; + private readonly _showErrors = MarkersContextKeys.ShowErrorsFilterContextKey.bindTo(this.contextKeyService); get showErrors(): boolean { - return this._showErrors; + return !!this._showErrors.get(); } set showErrors(showErrors: boolean) { - if (this._showErrors !== showErrors) { - this._showErrors = showErrors; + if (this._showErrors.get() !== showErrors) { + this._showErrors.set(showErrors); this._onDidChange.fire({ showErrors: true }); } } - private _showInfos: boolean = true; + private readonly _showInfos = MarkersContextKeys.ShowInfoFilterContextKey.bindTo(this.contextKeyService); get showInfos(): boolean { - return this._showInfos; + return !!this._showInfos.get(); } set showInfos(showInfos: boolean) { - if (this._showInfos !== showInfos) { - this._showInfos = showInfos; + if (this._showInfos.get() !== showInfos) { + this._showInfos.set(showInfos); this._onDidChange.fire({ showInfos: true }); } } - private _layout: DOM.Dimension = new DOM.Dimension(0, 0); - get layout(): DOM.Dimension { - return this._layout; - } - set layout(layout: DOM.Dimension) { - if (this._layout.width !== layout.width || this._layout.height !== layout.height) { - this._layout = layout; - this._onDidChange.fire({ layout: true }); - } - } -} - -class FiltersDropdownMenuActionViewItem extends DropdownMenuActionViewItem { - - constructor( - action: IAction, private filters: MarkersFilters, actionRunner: IActionRunner, - @IContextMenuService contextMenuService: IContextMenuService - ) { - super(action, - { getActions: () => this.getActions() }, - contextMenuService, - { - actionRunner, - classNames: action.class, - anchorAlignmentProvider: () => AnchorAlignment.RIGHT, - menuAsChild: true - } - ); - } - - override render(container: HTMLElement): void { - super.render(container); - this.updateChecked(); - } - - private getActions(): IAction[] { - return [ - { - checked: this.filters.showErrors, - class: undefined, - enabled: true, - id: 'showErrors', - label: Messages.MARKERS_PANEL_FILTER_LABEL_SHOW_ERRORS, - run: async () => this.filters.showErrors = !this.filters.showErrors, - tooltip: '' - }, - { - checked: this.filters.showWarnings, - class: undefined, - enabled: true, - id: 'showWarnings', - label: Messages.MARKERS_PANEL_FILTER_LABEL_SHOW_WARNINGS, - run: async () => this.filters.showWarnings = !this.filters.showWarnings, - tooltip: '' - }, - { - checked: this.filters.showInfos, - class: undefined, - enabled: true, - id: 'showInfos', - label: Messages.MARKERS_PANEL_FILTER_LABEL_SHOW_INFOS, - run: async () => this.filters.showInfos = !this.filters.showInfos, - tooltip: '' - }, - new Separator(), - { - checked: this.filters.activeFile, - class: undefined, - enabled: true, - id: 'activeFile', - label: Messages.MARKERS_PANEL_FILTER_LABEL_ACTIVE_FILE, - run: async () => this.filters.activeFile = !this.filters.activeFile, - tooltip: '' - }, - { - checked: this.filters.excludedFiles, - class: undefined, - enabled: true, - id: 'useFilesExclude', - label: Messages.MARKERS_PANEL_FILTER_LABEL_EXCLUDED_FILES, - run: async () => this.filters.excludedFiles = !this.filters.excludedFiles, - tooltip: '' - }, - ]; - } - - override updateChecked(): void { - this.element!.classList.toggle('checked', this._action.checked); - } - -} - - -const filterIcon = registerIcon('markers-view-filter', Codicon.filter, localize('filterIcon', 'Icon for the filter configuration in the markers view.')); - -export class MarkersFilterActionViewItem extends BaseActionViewItem { - - private delayedFilterUpdate: Delayer; - private container: HTMLElement | null = null; - private filterInputBox: HistoryInputBox | null = null; - private filterBadge: HTMLElement | null = null; - private focusContextKey: IContextKey; - private readonly filtersAction: IAction; - private actionbar: ActionBar | null = null; - private keybindingService; - - constructor( - action: IAction, - private markersView: IMarkersView, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IContextViewService private readonly contextViewService: IContextViewService, - @IThemeService private readonly themeService: IThemeService, - @IContextKeyService contextKeyService: IContextKeyService, - @IKeybindingService keybindingService: IKeybindingService - ) { - super(null, action); - this.keybindingService = keybindingService; - this.focusContextKey = MarkersContextKeys.MarkerViewFilterFocusContextKey.bindTo(contextKeyService); - this.delayedFilterUpdate = new Delayer(400); - this._register(toDisposable(() => this.delayedFilterUpdate.cancel())); - this._register(markersView.onDidFocusFilter(() => this.focus())); - this._register(markersView.onDidClearFilterText(() => this.clearFilterText())); - this.filtersAction = new Action('markersFiltersAction', Messages.MARKERS_PANEL_ACTION_TOOLTIP_MORE_FILTERS, 'markers-filters ' + ThemeIcon.asClassName(filterIcon)); - this.filtersAction.checked = this.hasFiltersChanged(); - this._register(markersView.filters.onDidChange(e => this.onDidFiltersChange(e))); - } - - override render(container: HTMLElement): void { - this.container = container; - this.container.classList.add('markers-panel-action-filter-container'); - - this.element = DOM.append(this.container, DOM.$('')); - this.element.className = this.class; - this.createInput(this.element); - this.createControls(this.element); - this.updateClass(); - - this.adjustInputBox(); - } - - override focus(): void { - this.filterInputBox?.focus(); - } - - override blur(): void { - this.filterInputBox?.blur(); - } - - override setFocusable(): void { - // noop input elements are focusable by default - } - - override get trapsArrowNavigation(): boolean { - return true; - } - - private clearFilterText(): void { - if (this.filterInputBox) { - this.filterInputBox.value = ''; - } - } - - private onDidFiltersChange(e: IMarkersFiltersChangeEvent): void { - this.filtersAction.checked = this.hasFiltersChanged(); - if (e.layout) { - this.updateClass(); - } - } - - private hasFiltersChanged(): boolean { - return !this.markersView.filters.showErrors || !this.markersView.filters.showWarnings || !this.markersView.filters.showInfos || this.markersView.filters.excludedFiles || this.markersView.filters.activeFile; - } - - private createInput(container: HTMLElement): void { - this.filterInputBox = this._register(this.instantiationService.createInstance(ContextScopedHistoryInputBox, container, this.contextViewService, { - placeholder: Messages.MARKERS_PANEL_FILTER_PLACEHOLDER, - ariaLabel: Messages.MARKERS_PANEL_FILTER_ARIA_LABEL, - history: this.markersView.filters.filterHistory, - showHistoryHint: () => showHistoryKeybindingHint(this.keybindingService) - })); - this._register(attachInputBoxStyler(this.filterInputBox, this.themeService)); - this.filterInputBox.value = this.markersView.filters.filterText; - this._register(this.filterInputBox.onDidChange(filter => this.delayedFilterUpdate.trigger(() => this.onDidInputChange(this.filterInputBox!)))); - this._register(this.markersView.filters.onDidChange((event: IMarkersFiltersChangeEvent) => { - if (event.filterText) { - this.filterInputBox!.value = this.markersView.filters.filterText; - } - })); - this._register(DOM.addStandardDisposableListener(this.filterInputBox.inputElement, DOM.EventType.KEY_DOWN, (e: any) => this.onInputKeyDown(e, this.filterInputBox!))); - this._register(DOM.addStandardDisposableListener(container, DOM.EventType.KEY_DOWN, this.handleKeyboardEvent)); - this._register(DOM.addStandardDisposableListener(container, DOM.EventType.KEY_UP, this.handleKeyboardEvent)); - this._register(DOM.addStandardDisposableListener(this.filterInputBox.inputElement, DOM.EventType.CLICK, (e) => { - e.stopPropagation(); - e.preventDefault(); - })); - - const focusTracker = this._register(DOM.trackFocus(this.filterInputBox.inputElement)); - this._register(focusTracker.onDidFocus(() => this.focusContextKey.set(true))); - this._register(focusTracker.onDidBlur(() => this.focusContextKey.set(false))); - this._register(toDisposable(() => this.focusContextKey.reset())); - } - - private createControls(container: HTMLElement): void { - const controlsContainer = DOM.append(container, DOM.$('.markers-panel-filter-controls')); - this.createBadge(controlsContainer); - this.createFilters(controlsContainer); - } - - private createBadge(container: HTMLElement): void { - const filterBadge = this.filterBadge = DOM.append(container, DOM.$('.markers-panel-filter-badge')); - this._register(attachStylerCallback(this.themeService, { badgeBackground, badgeForeground, contrastBorder }, colors => { - const background = colors.badgeBackground ? colors.badgeBackground.toString() : ''; - const foreground = colors.badgeForeground ? colors.badgeForeground.toString() : ''; - const border = colors.contrastBorder ? colors.contrastBorder.toString() : ''; - - filterBadge.style.backgroundColor = background; - - filterBadge.style.borderWidth = border ? '1px' : ''; - filterBadge.style.borderStyle = border ? 'solid' : ''; - filterBadge.style.borderColor = border; - filterBadge.style.color = foreground; - })); - this.updateBadge(); - this._register(this.markersView.onDidChangeFilterStats(() => this.updateBadge())); - } - - private createFilters(container: HTMLElement): void { - this.actionbar = this._register(new ActionBar(container, { - actionViewItemProvider: action => { - if (action.id === this.filtersAction.id) { - return this.instantiationService.createInstance(FiltersDropdownMenuActionViewItem, action, this.markersView.filters, this.actionRunner); - } - return undefined; - } - })); - this.actionbar.push(this.filtersAction, { icon: true, label: false }); - } - - private onDidInputChange(inputbox: HistoryInputBox) { - inputbox.addToHistory(); - this.markersView.filters.filterText = inputbox.value; - this.markersView.filters.filterHistory = inputbox.getHistory(); - } - - private updateBadge(): void { - if (this.filterBadge) { - const { total, filtered } = this.markersView.getFilterStats(); - this.filterBadge.classList.toggle('hidden', total === filtered || total === 0); - this.filterBadge.textContent = localize('showing filtered problems', "Showing {0} of {1}", filtered, total); - this.adjustInputBox(); - } - } - - private adjustInputBox(): void { - if (this.element && this.filterInputBox && this.filterBadge) { - this.filterInputBox.inputElement.style.paddingRight = this.element.classList.contains('small') || this.filterBadge.classList.contains('hidden') ? '25px' : '150px'; - } - } - - // Action toolbar is swallowing some keys for action items which should not be for an input box - private handleKeyboardEvent(event: StandardKeyboardEvent) { - if (event.equals(KeyCode.Space) - || event.equals(KeyCode.LeftArrow) - || event.equals(KeyCode.RightArrow) - ) { - event.stopPropagation(); - } - } - - private onInputKeyDown(event: StandardKeyboardEvent, filterInputBox: HistoryInputBox) { - let handled = false; - if (event.equals(KeyCode.Tab)) { - this.actionbar?.focus(); - handled = true; - } - if (handled) { - event.stopPropagation(); - event.preventDefault(); - } - } - - protected override updateClass(): void { - if (this.element && this.container) { - this.element.className = this.class; - this.container.classList.toggle('grow', this.element.classList.contains('grow')); - this.adjustInputBox(); - } - } - - protected get class(): string { - if (this.markersView.filters.layout.width > 600) { - return 'markers-panel-action-filter grow'; - } else if (this.markersView.filters.layout.width < 400) { - return 'markers-panel-action-filter small'; - } else { - return 'markers-panel-action-filter'; - } - } } export class QuickFixAction extends Action { diff --git a/src/vs/workbench/contrib/markers/browser/media/markers.css b/src/vs/workbench/contrib/markers/browser/media/markers.css index 4a47972350e..c03346efbd4 100644 --- a/src/vs/workbench/contrib/markers/browser/media/markers.css +++ b/src/vs/workbench/contrib/markers/browser/media/markers.css @@ -3,67 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-action-bar .action-item.markers-panel-action-filter-container { - cursor: default; - display: flex; -} - -.monaco-action-bar .markers-panel-action-filter { - display: flex; - align-items: center; - flex: 1; -} - -.monaco-action-bar .markers-panel-action-filter .monaco-inputbox { - height: 24px; - font-size: 12px; - flex: 1; -} - -.pane-header .monaco-action-bar .markers-panel-action-filter .monaco-inputbox { - height: 20px; - line-height: 18px; -} - -.monaco-workbench.vs .monaco-action-bar .markers-panel-action-filter .monaco-inputbox { - height: 25px; -} - -.markers-panel-action-filter > .markers-panel-filter-controls { - position: absolute; - top: 0px; - bottom: 0; - right: 0px; - display: flex; - align-items: center; -} - -.markers-panel-action-filter > .markers-panel-filter-controls > .markers-panel-filter-badge { - margin: 4px 0px; - padding: 0px 8px; - border-radius: 2px; -} - -.markers-panel-action-filter > .markers-panel-filter-controls > .markers-panel-filter-badge.hidden, -.markers-panel-action-filter.small > .markers-panel-filter-controls > .markers-panel-filter-badge { - display: none; -} - -.markers-panel-action-filter > .markers-panel-filter-controls > .monaco-action-bar .action-item .action-label.codicon.markers-filters { - padding: 2px; -} - -.panel > .title .monaco-action-bar .action-item.markers-panel-action-filter-container { - max-width: 400px; - min-width: 300px; - margin-right: 10px; -} - -.markers-panel-container .monaco-action-bar.markers-panel-filter-container .action-item.markers-panel-action-filter-container, -.panel > .title .monaco-action-bar .action-item.markers-panel-action-filter-container.grow { - flex: 1; -} - .markers-panel .markers-panel-container { height: 100%; } @@ -72,11 +11,6 @@ display: none; } -.markers-panel-container .monaco-action-bar.markers-panel-filter-container { - margin: 10px 20px; - height: initial; -} - .markers-panel .markers-panel-container .message-box-container { line-height: 22px; padding-left: 20px; diff --git a/src/vs/workbench/contrib/markers/common/markers.ts b/src/vs/workbench/contrib/markers/common/markers.ts index 6f3efbbeec6..17496243cb3 100644 --- a/src/vs/workbench/contrib/markers/common/markers.ts +++ b/src/vs/workbench/contrib/markers/common/markers.ts @@ -31,9 +31,13 @@ export namespace Markers { export namespace MarkersContextKeys { export const MarkersViewModeContextKey = new RawContextKey('problemsViewMode', MarkersViewMode.Tree); - export const MarkersViewSmallLayoutContextKey = new RawContextKey(`problemsView.smallLayout`, false); export const MarkersTreeVisibilityContextKey = new RawContextKey('problemsVisibility', false); export const MarkerFocusContextKey = new RawContextKey('problemFocus', false); export const MarkerViewFilterFocusContextKey = new RawContextKey('problemsFilterFocus', false); export const RelatedInformationFocusContextKey = new RawContextKey('relatedInformationFocus', false); + export const ShowErrorsFilterContextKey = new RawContextKey('problems.filter.errors', true); + export const ShowWarningsFilterContextKey = new RawContextKey('problems.filter.warnings', true); + export const ShowInfoFilterContextKey = new RawContextKey('problems.filter.info', true); + export const ShowActiveFileFilterContextKey = new RawContextKey('problems.filter.activeFile', false); + export const ShowExcludedFilesFilterContextKey = new RawContextKey('problems.filter.excludedFiles', true); } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/commands/commands.ts b/src/vs/workbench/contrib/mergeEditor/browser/commands/commands.ts index 95f83325e3f..0f8742e3189 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/commands/commands.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/commands/commands.ts @@ -4,10 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { Codicon } from 'vs/base/common/codicons'; +import { basename } from 'vs/base/common/resources'; import { URI, UriComponents } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { ILocalizedString } from 'vs/platform/action/common/action'; import { Action2, IAction2Options, MenuId } from 'vs/platform/actions/common/actions'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; @@ -17,7 +19,7 @@ import { MergeEditorInput, MergeEditorInputData } from 'vs/workbench/contrib/mer import { IMergeEditorInputModel } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel'; import { MergeEditor } from 'vs/workbench/contrib/mergeEditor/browser/view/mergeEditor'; import { MergeEditorViewModel } from 'vs/workbench/contrib/mergeEditor/browser/view/viewModel'; -import { ctxIsMergeEditor, ctxMergeEditorLayout, ctxMergeEditorShowBase, ctxMergeEditorShowNonConflictingChanges } from 'vs/workbench/contrib/mergeEditor/common/mergeEditor'; +import { ctxIsMergeEditor, ctxMergeEditorLayout, ctxMergeEditorShowBase, ctxMergeEditorShowBaseAtTop, ctxMergeEditorShowNonConflictingChanges } from 'vs/workbench/contrib/mergeEditor/common/mergeEditor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; abstract class MergeEditorAction extends Action2 { @@ -259,12 +261,11 @@ export class ShowHideBase extends Action2 { menu: [ { id: MenuId.EditorTitle, - when: ctxIsMergeEditor, + when: ContextKeyExpr.and(ctxIsMergeEditor, ctxMergeEditorLayout.isEqualTo('columns')), group: '2_merge', order: 9, }, - ], - precondition: ctxIsMergeEditor, + ] }); } @@ -276,6 +277,62 @@ export class ShowHideBase extends Action2 { } } +export class ShowHideTopBase extends Action2 { + constructor() { + super({ + id: 'merge.showBaseTop', + title: { + value: localize('layout.showBaseTop', 'Show Base Top'), + original: 'Show Base Top', + }, + toggled: ContextKeyExpr.and(ctxMergeEditorShowBase, ctxMergeEditorShowBaseAtTop), + menu: [ + { + id: MenuId.EditorTitle, + when: ContextKeyExpr.and(ctxIsMergeEditor, ctxMergeEditorLayout.isEqualTo('mixed')), + group: '2_merge', + order: 10, + }, + ], + }); + } + + run(accessor: ServicesAccessor): void { + const { activeEditorPane } = accessor.get(IEditorService); + if (activeEditorPane instanceof MergeEditor) { + activeEditorPane.toggleShowBaseTop(); + } + } +} + +export class ShowHideCenterBase extends Action2 { + constructor() { + super({ + id: 'merge.showBaseCenter', + title: { + value: localize('layout.showBaseCenter', 'Show Base Center'), + original: 'Show Base Center', + }, + toggled: ContextKeyExpr.and(ctxMergeEditorShowBase, ctxMergeEditorShowBaseAtTop.negate()), + menu: [ + { + id: MenuId.EditorTitle, + when: ContextKeyExpr.and(ctxIsMergeEditor, ctxMergeEditorLayout.isEqualTo('mixed')), + group: '2_merge', + order: 11, + }, + ], + }); + } + + run(accessor: ServicesAccessor): void { + const { activeEditorPane } = accessor.get(IEditorService); + if (activeEditorPane instanceof MergeEditor) { + activeEditorPane.toggleShowBaseCenter(); + } + } +} + const mergeEditorCategory: ILocalizedString = { value: localize('mergeEditor', 'Merge Editor'), original: 'Merge Editor', @@ -573,31 +630,7 @@ export class ResetToBaseAndAutoMergeCommand extends MergeEditorAction { } override runWithViewModel(viewModel: MergeEditorViewModel, accessor: ServicesAccessor): void { - viewModel.model.resetResultToBaseAndAutoMerge(); - } -} - -export class ResetDirtyConflictsToBaseCommand extends MergeEditorAction { - constructor() { - super({ - id: 'mergeEditor.resetDirtyConflictsToBase', - category: mergeEditorCategory, - title: { - value: localize( - 'mergeEditor.resetDirtyConflictsToBase', - 'Reset Dirty Conflicts In Result To Base' - ), - original: 'Reset Dirty Conflicts In Result To Base', - }, - shortTitle: localize('mergeEditor.resetDirtyConflictsToBase.short', 'Reset Dirty Conflicts To Base'), - f1: true, - precondition: ctxIsMergeEditor, - menu: { id: MenuId.MergeInputResultToolbar } - }); - } - - override runWithViewModel(viewModel: MergeEditorViewModel, accessor: ServicesAccessor): void { - viewModel.model.resetDirtyConflictsToBase(); + viewModel.model.reset(); } } @@ -610,9 +643,9 @@ export class AcceptMerge extends MergeEditorAction2 { title: { value: localize( 'mergeEditor.acceptMerge', - 'Accept Merge' + 'Complete Merge' ), - original: 'Accept Merge', + original: 'Complete Merge', }, f1: false, precondition: ctxIsMergeEditor @@ -626,8 +659,9 @@ export class AcceptMerge extends MergeEditorAction2 { if (viewModel.model.unhandledConflictsCount.get() > 0) { const confirmResult = await dialogService.confirm({ type: 'info', - message: localize('mergeEditor.acceptMerge.unhandledConflicts', "There are still unhandled conflicts. Are you sure you want to accept the merge?"), - primaryButton: localize('mergeEditor.acceptMerge.unhandledConflicts.accept', "Accept merge with unhandled conflicts"), + message: localize('mergeEditor.acceptMerge.unhandledConflicts.message', "Do you want to complete the merge of {0}?", basename(inputModel.resultUri)), + detail: localize('mergeEditor.acceptMerge.unhandledConflicts.detail', "The file contains unhandled conflicts."), + primaryButton: localize('mergeEditor.acceptMerge.unhandledConflicts.accept', "Complete with Conflicts"), secondaryButton: localize('mergeEditor.acceptMerge.unhandledConflicts.cancel', "Cancel"), }); diff --git a/src/vs/workbench/contrib/mergeEditor/browser/commands/devCommands.ts b/src/vs/workbench/contrib/mergeEditor/browser/commands/devCommands.ts index 1c28237d1bf..0f2b6b56183 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/commands/devCommands.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/commands/devCommands.ts @@ -8,6 +8,7 @@ import { Codicon } from 'vs/base/common/codicons'; import { URI } from 'vs/base/common/uri'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { localize } from 'vs/nls'; +import { ILocalizedString } from 'vs/platform/action/common/action'; import { Action2 } from 'vs/platform/actions/common/actions'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; @@ -20,11 +21,13 @@ import { MergeEditor } from 'vs/workbench/contrib/mergeEditor/browser/view/merge import { ctxIsMergeEditor, MergeEditorContents } from 'vs/workbench/contrib/mergeEditor/common/mergeEditor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +const MERGE_EDITOR_CATEGORY: ILocalizedString = { value: localize('mergeEditor', "Merge Editor (Dev)"), original: 'Merge Editor (Dev)' }; + export class MergeEditorCopyContentsToJSON extends Action2 { constructor() { super({ id: 'merge.dev.copyContentsJson', - category: 'Merge Editor (Dev)', + category: MERGE_EDITOR_CATEGORY, title: { value: localize( 'merge.dev.copyState', @@ -76,7 +79,7 @@ export class MergeEditorSaveContentsToFolder extends Action2 { constructor() { super({ id: 'merge.dev.saveContentsToFolder', - category: 'Merge Editor (Dev)', + category: MERGE_EDITOR_CATEGORY, title: { value: localize( 'merge.dev.saveContentsToFolder', @@ -145,7 +148,7 @@ export class MergeEditorLoadContentsFromFolder extends Action2 { constructor() { super({ id: 'merge.dev.loadContentsFromFolder', - category: 'Merge Editor (Dev)', + category: MERGE_EDITOR_CATEGORY, title: { value: localize( 'merge.dev.loadContentsFromFolder', diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts index 0e8385ea1d0..7218c3cf281 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts @@ -11,7 +11,12 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { EditorPaneDescriptor, IEditorPaneRegistry } from 'vs/workbench/browser/editor'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { EditorExtensions, IEditorFactoryRegistry } from 'vs/workbench/common/editor'; -import { AcceptAllInput1, AcceptAllInput2, AcceptMerge, CompareInput1WithBaseCommand, CompareInput2WithBaseCommand, GoToNextUnhandledConflict, GoToPreviousUnhandledConflict, OpenBaseFile, OpenMergeEditor, OpenResultResource, ResetDirtyConflictsToBaseCommand, ResetToBaseAndAutoMergeCommand, SetColumnLayout, SetMixedLayout, ShowHideBase, ShowNonConflictingChanges, ToggleActiveConflictInput1, ToggleActiveConflictInput2 } from 'vs/workbench/contrib/mergeEditor/browser/commands/commands'; +import { + AcceptAllInput1, AcceptAllInput2, AcceptMerge, CompareInput1WithBaseCommand, + CompareInput2WithBaseCommand, GoToNextUnhandledConflict, GoToPreviousUnhandledConflict, OpenBaseFile, OpenMergeEditor, + OpenResultResource, ResetToBaseAndAutoMergeCommand, SetColumnLayout, SetMixedLayout, ShowHideTopBase, ShowHideCenterBase, ShowHideBase, + ShowNonConflictingChanges, ToggleActiveConflictInput1, ToggleActiveConflictInput2 +} from 'vs/workbench/contrib/mergeEditor/browser/commands/commands'; import { MergeEditorCopyContentsToJSON, MergeEditorLoadContentsFromFolder, MergeEditorSaveContentsToFolder } from 'vs/workbench/contrib/mergeEditor/browser/commands/devCommands'; import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; import { MergeEditor, MergeEditorOpenHandlerContribution, MergeEditorResolverContribution } from 'vs/workbench/contrib/mergeEditor/browser/view/mergeEditor'; @@ -39,22 +44,29 @@ Registry.as(Extensions.Configuration).registerConfigurat 'mergeEditor.diffAlgorithm': { type: 'string', enum: ['smart', 'experimental'], - default: 'smart', + default: 'experimental', markdownEnumDescriptions: [ localize('diffAlgorithm.smart', "Uses the default diffing algorithm."), localize('diffAlgorithm.experimental', "Uses an experimental diffing algorithm."), ] }, + 'mergeEditor.showDeletionMarkers': { + type: 'boolean', + default: true, + description: 'Controls if deletions in base or one of the inputs should be indicated by a vertical bar.', + }, } }); registerAction2(OpenResultResource); registerAction2(SetMixedLayout); registerAction2(SetColumnLayout); -registerAction2(ShowHideBase); registerAction2(OpenMergeEditor); registerAction2(OpenBaseFile); registerAction2(ShowNonConflictingChanges); +registerAction2(ShowHideBase); +registerAction2(ShowHideTopBase); +registerAction2(ShowHideCenterBase); registerAction2(GoToNextUnhandledConflict); registerAction2(GoToPreviousUnhandledConflict); @@ -69,7 +81,6 @@ registerAction2(AcceptAllInput1); registerAction2(AcceptAllInput2); registerAction2(ResetToBaseAndAutoMergeCommand); -registerAction2(ResetDirtyConflictsToBaseCommand); registerAction2(AcceptMerge); @@ -80,28 +91,8 @@ registerAction2(MergeEditorLoadContentsFromFolder); Registry .as(WorkbenchExtensions.Workbench) - .registerWorkbenchContribution(MergeEditorOpenHandlerContribution, 'MergeEditorOpenHandlerContribution', LifecyclePhase.Restored); + .registerWorkbenchContribution(MergeEditorOpenHandlerContribution, LifecyclePhase.Restored); Registry .as(WorkbenchExtensions.Workbench) - .registerWorkbenchContribution(MergeEditorResolverContribution, 'MergeEditorResolverContribution', LifecyclePhase.Starting); -/* -class MergeEditorWorkbenchContribution extends Disposable implements IWorkbenchContribution { - constructor(@IWorkingCopyEditorService private readonly _workingCopyEditorService: IWorkingCopyEditorService) { - super(); - - this._register( - _workingCopyEditorService.registerHandler({ - createEditor(workingCopy) { - throw new BugIndicatingError('not supported'); - }, - handles(workingCopy) { - return workingCopy.typeId === ''; - }, - isOpen(workingCopy, editor) { - return workingCopy.resource.toString() === that._model?.resultTextModel.uri.toString(); - }, - })); - } -} -*/ + .registerWorkbenchContribution(MergeEditorResolverContribution, LifecyclePhase.Starting); diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts index 6f41a7f3632..d0e202909aa 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts @@ -66,7 +66,7 @@ export class MergeEditorInput extends AbstractTextResourceEditorInput implements super.dispose(); } - get typeId(): string { + override get typeId(): string { return MergeEditorInput.ID; } @@ -76,11 +76,9 @@ export class MergeEditorInput extends AbstractTextResourceEditorInput implements override get capabilities(): EditorInputCapabilities { let capabilities = super.capabilities | EditorInputCapabilities.MultipleEditors; - if (this.useWorkingCopy) { capabilities |= EditorInputCapabilities.Untitled; } - return capabilities; } @@ -171,6 +169,5 @@ export class MergeEditorInput extends AbstractTextResourceEditorInput implements this._inputModel?.model.setLanguageId(languageId, source); } - // implement get/set languageId // implement get/set encoding } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel.ts index 4ef67a9d65a..2bd5c9b00c9 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel.ts @@ -18,12 +18,12 @@ import { localize } from 'vs/nls'; import { ConfirmResult, IDialogOptions, IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IEditorModel } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IRevertOptions } from 'vs/workbench/common/editor'; +import { IRevertOptions, SaveSourceRegistry } from 'vs/workbench/common/editor'; import { EditorModel } from 'vs/workbench/common/editor/editorModel'; import { MergeEditorInputData } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; +import { conflictMarkers } from 'vs/workbench/contrib/mergeEditor/browser/mergeMarkers/mergeMarkersController'; import { MergeDiffComputer } from 'vs/workbench/contrib/mergeEditor/browser/model/diffComputer'; import { InputData, MergeEditorModel } from 'vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel'; -import { ProjectedDiffComputer } from 'vs/workbench/contrib/mergeEditor/browser/model/projectedDocumentDiffProvider'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITextFileEditorModel, ITextFileSaveOptions, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; @@ -109,7 +109,7 @@ export class TempFileMergeEditorModeFactory implements IMergeEditorInputModelFac input2Data, temporaryResultModel, this._instantiationService.createInstance(MergeDiffComputer, diffProvider), - this._instantiationService.createInstance(MergeDiffComputer, this._instantiationService.createInstance(ProjectedDiffComputer, diffProvider)), + this._instantiationService.createInstance(MergeDiffComputer, diffProvider), { resetResult: true, } @@ -269,6 +269,8 @@ export class WorkspaceMergeEditorModeFactory implements IMergeEditorInputModelFa ) { } + private static readonly FILE_SAVED_SOURCE = SaveSourceRegistry.registerSource('merge-editor.source', localize('merge-editor.source', "Before Resolving Conflicts In Merge Editor")); + public async createInputModel(args: MergeEditorArgs): Promise { const store = new DisposableStore(); @@ -302,7 +304,11 @@ export class WorkspaceMergeEditorModeFactory implements IMergeEditorInputModelFa throw new BugIndicatingError(); } // So that "Don't save" does revert the file - await resultTextFileModel.save(); + await resultTextFileModel.save({ source: WorkspaceMergeEditorModeFactory.FILE_SAVED_SOURCE }); + + const lines = resultTextFileModel.textEditorModel!.getLinesContent(); + const hasConflictMarkers = lines.some(l => l.startsWith(conflictMarkers.start)); + const resetResult = hasConflictMarkers; const diffProvider = this._instantiationService.createInstance(WorkerBasedDocumentDiffProvider); const model = this._instantiationService.createInstance( @@ -312,9 +318,9 @@ export class WorkspaceMergeEditorModeFactory implements IMergeEditorInputModelFa input2Data, result.object.textEditorModel, this._instantiationService.createInstance(MergeDiffComputer, diffProvider), - this._instantiationService.createInstance(MergeDiffComputer, this._instantiationService.createInstance(ProjectedDiffComputer, diffProvider)), + this._instantiationService.createInstance(MergeDiffComputer, diffProvider), { - resetResult: true + resetResult } ); store.add(model); @@ -365,7 +371,6 @@ class WorkspaceMergeEditorInputModel extends EditorModel implements IMergeEditor shouldConfirmClose(): boolean { // Always confirm return true; - //return this.resultTextFileModel.isDirty(); } async confirmClose(inputModels: IMergeEditorInputModel[]): Promise { @@ -394,7 +399,6 @@ class WorkspaceMergeEditorInputModel extends EditorModel implements IMergeEditor ConfirmResult.SAVE, ], [localize('workspace.doNotSave', "Don't Save"), ConfirmResult.DONT_SAVE], - // TODO [localize('workspace.discard', "Discard changes"), ConfirmResult.DONT_SAVE], [localize('workspace.cancel', 'Cancel'), ConfirmResult.CANCEL], ]; @@ -420,7 +424,6 @@ class WorkspaceMergeEditorInputModel extends EditorModel implements IMergeEditor : localize('workspace.close', 'Close'), ConfirmResult.SAVE, ], - // TODO [localize('workspace.discard', "Discard changes"), ConfirmResult.DONT_SAVE], [localize('workspace.cancel', 'Cancel'), ConfirmResult.CANCEL], ]; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts index 365d11269b8..f386c1cb21e 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts @@ -181,7 +181,7 @@ export class MergeEditorModel extends EditorModel { appendLinesToResult(baseLines, LineRange.fromLineNumbers(baseStartLineNumber, baseLines.length + 1)); - return resultLines.join('\n'); + return resultLines.join(this.resultTextModel.getEOL()); } public hasBaseRange(baseRange: ModifiedBaseRange): boolean { @@ -437,34 +437,6 @@ export class MergeEditorModel extends EditorModel { }); } - public acceptNonConflictingDiffs(): void { - transaction((tx) => { - /** @description Merge None Conflicting Diffs */ - this.resultTextModel.pushStackElement(); - for (const m of this.modifiedBaseRanges.get()) { - if (m.isConflicting) { - continue; - } - this.setState( - m, - m.input1Diffs.length > 0 - ? ModifiedBaseRangeState.default.withInput1(true) - : ModifiedBaseRangeState.default.withInput2(true), - true, - tx, - false - ); - } - this.resultTextModel.pushStackElement(); - }); - } - - public async resetResultToBaseAndAutoMerge() { - await waitForState(this.inputDiffComputingState, state => state === MergeEditorModelState.upToDate); - this.resultTextModel.setValue(this.base.getValue()); - this.acceptNonConflictingDiffs(); - } - public isHandled(baseRange: ModifiedBaseRange): IObservable { return this.modifiedBaseRangeResultStates.get().get(baseRange)!.handled; } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/colors.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/colors.ts index 6e94f727eb0..78991abe8d6 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/colors.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/colors.ts @@ -18,6 +18,18 @@ export const diffWord = registerColor( localize('mergeEditor.change.word.background', 'The background color for word changes.') ); +export const diffBase = registerColor( + 'mergeEditor.changeBase.background', + { dark: '#4B1818FF', light: '#FFCCCCFF', hcDark: '#4B1818FF', hcLight: '#FFCCCCFF', }, + localize('mergeEditor.changeBase.background', 'The background color for changes in base.') +); + +export const diffWordBase = registerColor( + 'mergeEditor.changeBase.word.background', + { dark: '#6F1313FF', light: '#FFA3A3FF', hcDark: '#6F1313FF', hcLight: '#FFA3A3FF', }, + localize('mergeEditor.changeBase.word.background', 'The background color for word changes in base.') +); + export const conflictBorderUnhandledUnfocused = registerColor( 'mergeEditor.conflict.unhandledUnfocused.border', { dark: '#ffa6007a', light: '#ffa6007a', hcDark: '#ffa6007a', hcLight: '#ffa6007a', }, diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/conflictActions.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/conflictActions.ts new file mode 100644 index 00000000000..6e5658bee15 --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/conflictActions.ts @@ -0,0 +1,341 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { $, createStyleSheet, h, isInShadowDOM, reset } from 'vs/base/browser/dom'; +import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; +import { hash } from 'vs/base/common/hash'; +import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { autorun, derived, IObservable, transaction } from 'vs/base/common/observable'; +import { ICodeEditor, IViewZoneChangeAccessor } from 'vs/editor/browser/editorBrowser'; +import { EditorOption, EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; +import { localize } from 'vs/nls'; +import { ModifiedBaseRange, ModifiedBaseRangeState } from 'vs/workbench/contrib/mergeEditor/browser/model/modifiedBaseRange'; +import { FixedZoneWidget } from 'vs/workbench/contrib/mergeEditor/browser/view/fixedZoneWidget'; +import { MergeEditorViewModel } from 'vs/workbench/contrib/mergeEditor/browser/view/viewModel'; + +export class ConflictActionsFactory extends Disposable { + private readonly _styleClassName: string; + private readonly _styleElement: HTMLStyleElement; + + constructor(private readonly _editor: ICodeEditor) { + super(); + + this._register(this._editor.onDidChangeConfiguration((e) => { + if (e.hasChanged(EditorOption.fontInfo) || e.hasChanged(EditorOption.codeLensFontSize) || e.hasChanged(EditorOption.codeLensFontFamily)) { + this._updateLensStyle(); + } + })); + + this._styleClassName = '_conflictActionsFactory_' + hash(this._editor.getId()).toString(16); + this._styleElement = createStyleSheet( + isInShadowDOM(this._editor.getContainerDomNode()) + ? this._editor.getContainerDomNode() + : undefined + ); + + this._register(toDisposable(() => { + this._styleElement.remove(); + })); + + this._updateLensStyle(); + } + + private _updateLensStyle(): void { + const { codeLensHeight, fontSize } = this._getLayoutInfo(); + const fontFamily = this._editor.getOption(EditorOption.codeLensFontFamily); + const editorFontInfo = this._editor.getOption(EditorOption.fontInfo); + + const fontFamilyVar = `--codelens-font-family${this._styleClassName}`; + const fontFeaturesVar = `--codelens-font-features${this._styleClassName}`; + + let newStyle = ` + .${this._styleClassName} { line-height: ${codeLensHeight}px; font-size: ${fontSize}px; padding-right: ${Math.round(fontSize * 0.5)}px; font-feature-settings: var(${fontFeaturesVar}) } + .monaco-workbench .${this._styleClassName} span.codicon { line-height: ${codeLensHeight}px; font-size: ${fontSize}px; } + `; + if (fontFamily) { + newStyle += `${this._styleClassName} { font-family: var(${fontFamilyVar}), ${EDITOR_FONT_DEFAULTS.fontFamily}}`; + } + this._styleElement.textContent = newStyle; + this._editor.getContainerDomNode().style.setProperty(fontFamilyVar, fontFamily ?? 'inherit'); + this._editor.getContainerDomNode().style.setProperty(fontFeaturesVar, editorFontInfo.fontFeatureSettings); + } + + private _getLayoutInfo() { + const lineHeightFactor = Math.max(1.3, this._editor.getOption(EditorOption.lineHeight) / this._editor.getOption(EditorOption.fontSize)); + let fontSize = this._editor.getOption(EditorOption.codeLensFontSize); + if (!fontSize || fontSize < 5) { + fontSize = (this._editor.getOption(EditorOption.fontSize) * .9) | 0; + } + return { + fontSize, + codeLensHeight: (fontSize * lineHeightFactor) | 0, + }; + } + + public createWidget(viewZoneChangeAccessor: IViewZoneChangeAccessor, lineNumber: number, items: IObservable, viewZoneIdsToCleanUp: string[]): IDisposable { + const layoutInfo = this._getLayoutInfo(); + return new ActionsContentWidget( + this._editor, + viewZoneChangeAccessor, + lineNumber, + layoutInfo.codeLensHeight + 2, + this._styleClassName, + items, + viewZoneIdsToCleanUp, + ); + } +} + +export class ActionsSource { + constructor( + private readonly viewModel: MergeEditorViewModel, + private readonly modifiedBaseRange: ModifiedBaseRange, + ) { + } + + private getItemsInput(inputNumber: 1 | 2): IObservable { + return derived('items', reader => { + const viewModel = this.viewModel; + const modifiedBaseRange = this.modifiedBaseRange; + + if (!viewModel.model.hasBaseRange(modifiedBaseRange)) { + return []; + } + + const state = viewModel.model.getState(modifiedBaseRange).read(reader); + const handled = viewModel.model.isHandled(modifiedBaseRange).read(reader); + const model = viewModel.model; + + const result: IContentWidgetAction[] = []; + + const inputData = inputNumber === 1 ? viewModel.model.input1 : viewModel.model.input2; + const showNonConflictingChanges = viewModel.showNonConflictingChanges.read(reader); + + if (!modifiedBaseRange.isConflicting && handled && !showNonConflictingChanges) { + return []; + } + + const otherInputNumber = inputNumber === 1 ? 2 : 1; + + if (!state.conflicting && !state.isInputIncluded(inputNumber)) { + result.push( + !state.isInputIncluded(inputNumber) + ? command(localize('accept', "Accept {0}", inputData.title), async () => { + transaction((tx) => { + model.setState( + modifiedBaseRange, + state.withInputValue(inputNumber, true), + true, + tx + ); + }); + }, localize('acceptTooltip', "Accept {0} in the result document.", inputData.title)) + : command(localize('remove', "Remove {0}", inputData.title), async () => { + transaction((tx) => { + model.setState( + modifiedBaseRange, + state.withInputValue(inputNumber, false), + true, + tx + ); + }); + }, localize('removeTooltip', "Remove {0} from the result document.", inputData.title)), + ); + + if (modifiedBaseRange.canBeCombined && state.isEmpty) { + result.push( + state.input1 && state.input2 + ? command(localize('removeBoth', "Remove Both"), async () => { + transaction((tx) => { + model.setState( + modifiedBaseRange, + ModifiedBaseRangeState.default, + true, + tx + ); + }); + }, localize('removeBothTooltip', "Remove both changes from the result document.")) + : command(localize('acceptBoth', "Accept Both"), async () => { + transaction((tx) => { + model.setState( + modifiedBaseRange, + state + .withInputValue(inputNumber, true) + .withInputValue(otherInputNumber, true), + true, + tx + ); + }); + }, localize('acceptBothTooltip', "Accept an automatic combination of both sides in the result document.")), + ); + } + } + return result; + }); + } + + public readonly itemsInput1 = this.getItemsInput(1); + public readonly itemsInput2 = this.getItemsInput(2); + + public readonly resultItems = derived('items', reader => { + const viewModel = this.viewModel; + const modifiedBaseRange = this.modifiedBaseRange; + + const state = viewModel.model.getState(modifiedBaseRange).read(reader); + const model = viewModel.model; + + const result: IContentWidgetAction[] = []; + + + if (state.conflicting) { + result.push({ + text: localize('manualResolution', "Manual Resolution"), + tooltip: localize('manualResolutionTooltip', "This conflict has been resolved manually."), + }); + } else if (state.isEmpty) { + result.push({ + text: localize('noChangesAccepted', 'No Changes Accepted'), + tooltip: localize( + 'noChangesAcceptedTooltip', + 'The current resolution of this conflict equals the common ancestor of both the right and left changes.' + ), + }); + + } else { + const labels = []; + if (state.input1) { + labels.push(model.input1.title); + } + if (state.input2) { + labels.push(model.input2.title); + } + if (state.input2First) { + labels.reverse(); + } + result.push({ + text: `${labels.join(' + ')}` + }); + } + + const stateToggles: IContentWidgetAction[] = []; + if (state.input1) { + result.push(command(localize('remove', "Remove {0}", model.input1.title), async () => { + transaction((tx) => { + model.setState( + modifiedBaseRange, + state.withInputValue(1, false), + true, + tx + ); + }); + }, localize('removeTooltip', "Remove {0} from the result document.", model.input1.title)), + ); + } + if (state.input2) { + result.push(command(localize('remove', "Remove {0}", model.input2.title), async () => { + transaction((tx) => { + model.setState( + modifiedBaseRange, + state.withInputValue(2, false), + true, + tx + ); + }); + }, localize('removeTooltip', "Remove {0} from the result document.", model.input2.title)), + ); + } + if (state.input2First) { + stateToggles.reverse(); + } + result.push(...stateToggles); + + + + if (state.conflicting) { + result.push( + command(localize('resetToBase', "Reset to base"), async () => { + transaction((tx) => { + model.setState( + modifiedBaseRange, + ModifiedBaseRangeState.default, + true, + tx + ); + }); + }, localize('resetToBaseTooltip', "Reset this conflict to the common ancestor of both the right and left changes.")), + ); + } + return result; + }); + + public readonly isEmpty = derived('isEmpty', reader => { + return this.itemsInput1.read(reader).length + this.itemsInput2.read(reader).length + this.resultItems.read(reader).length === 0; + }); + + public readonly inputIsEmpty = derived('inputIsEmpty', reader => { + return this.itemsInput1.read(reader).length + this.itemsInput2.read(reader).length === 0; + }); +} + +function command(title: string, action: () => Promise, tooltip?: string): IContentWidgetAction { + return { + text: title, + action, + tooltip, + }; +} + +export interface IContentWidgetAction { + text: string; + tooltip?: string; + action?: () => Promise; +} + +class ActionsContentWidget extends FixedZoneWidget { + private readonly _domNode = h('div.merge-editor-conflict-actions').root; + + constructor( + editor: ICodeEditor, + viewZoneAccessor: IViewZoneChangeAccessor, + afterLineNumber: number, + height: number, + + className: string, + items: IObservable, + viewZoneIdsToCleanUp: string[], + ) { + super(editor, viewZoneAccessor, afterLineNumber, height, viewZoneIdsToCleanUp); + + this.widgetDomNode.appendChild(this._domNode); + + this._domNode.classList.add(className); + + this._register(autorun('update commands', (reader) => { + const i = items.read(reader); + this.setState(i); + })); + } + + private setState(items: IContentWidgetAction[]) { + const children: HTMLElement[] = []; + let isFirst = true; + for (const item of items) { + if (isFirst) { + isFirst = false; + } else { + children.push($('span', undefined, '\u00a0|\u00a0')); + } + const title = renderLabelWithIcons(item.text); + + if (item.action) { + children.push($('a', { title: item.tooltip, role: 'button', onclick: () => item.action!() }, ...title)); + } else { + children.push($('span', { title: item.tooltip }, ...title)); + } + } + + reset(this._domNode, ...children); + } +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/baseCodeEditorView.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/baseCodeEditorView.ts index b69a2d49b26..2086fc1dcc9 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/baseCodeEditorView.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/baseCodeEditorView.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { reset } from 'vs/base/browser/dom'; +import { h, reset } from 'vs/base/browser/dom'; import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { BugIndicatingError } from 'vs/base/common/errors'; import { autorun, autorunWithStore, derived, IObservable } from 'vs/base/common/observable'; @@ -55,6 +55,16 @@ export class BaseCodeEditorView extends CodeEditorView { } this.editor.setModel(vm.model.base); reset(this.htmlElements.title, ...renderLabelWithIcons(localize('base', 'Base'))); + + const baseShowDiffAgainst = vm.baseShowDiffAgainst.read(reader); + + let node: Node | undefined = undefined; + if (baseShowDiffAgainst) { + const label = localize('compareWith', 'Comparing with {0}', baseShowDiffAgainst === 1 ? vm.model.input1.title : vm.model.input2.title); + const tooltip = localize('compareWithTooltip', 'Differences are highlighted with a background color.'); + node = h('span', { title: tooltip }, [label]).root; + } + reset(this.htmlElements.description, ...(node ? [node] : [])); }) ); @@ -67,9 +77,11 @@ export class BaseCodeEditorView extends CodeEditorView { return []; } const model = viewModel.model; + const textModel = model.base; const activeModifiedBaseRange = viewModel.activeModifiedBaseRange.read(reader); const showNonConflictingChanges = viewModel.showNonConflictingChanges.read(reader); + const showDeletionMarkers = this.showDeletionMarkers.read(reader); const result: IModelDeltaDecoration[] = []; for (const modifiedBaseRange of model.modifiedBaseRanges.read(reader)) { @@ -93,11 +105,43 @@ export class BaseCodeEditorView extends CodeEditorView { } blockClassNames.push('base'); + const inputToDiffAgainst = viewModel.baseShowDiffAgainst.read(reader); + + if (inputToDiffAgainst) { + for (const diff of modifiedBaseRange.getInputDiffs(inputToDiffAgainst)) { + const range = diff.inputRange.toInclusiveRange(); + if (range) { + result.push({ + range, + options: { + className: `merge-editor-diff base`, + description: 'Merge Editor', + isWholeLine: true, + } + }); + } + + for (const diff2 of diff.rangeMappings) { + if (showDeletionMarkers || !diff2.inputRange.isEmpty()) { + result.push({ + range: diff2.inputRange, + options: { + className: diff2.inputRange.isEmpty() ? `merge-editor-diff-empty-word base` : `merge-editor-diff-word base`, + description: 'Merge Editor', + showIfCollapsed: true, + }, + }); + } + } + } + } + result.push({ range: range.toInclusiveRangeOrEmpty(), options: { showIfCollapsed: true, blockClassName: blockClassNames.join(' '), + blockIsAfterEnd: range.startLineNumber > textModel.getLineCount(), description: 'Merge Editor', minimap: { position: MinimapPosition.Gutter, diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/codeEditorView.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/codeEditorView.ts index 27b27b276fc..4710408ef07 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/codeEditorView.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/codeEditorView.ts @@ -61,12 +61,12 @@ export abstract class CodeEditorView extends Disposable { protected readonly checkboxesVisible = observableFromEvent( this.configurationService.onDidChangeConfiguration, - () => /** @description checkboxesVisible */ this.configurationService.getValue('mergeEditor.showCheckboxes') ?? true + () => /** @description checkboxesVisible */ this.configurationService.getValue('mergeEditor.showCheckboxes') ?? false ); - protected readonly codeLensesVisible = observableFromEvent( + protected readonly showDeletionMarkers = observableFromEvent( this.configurationService.onDidChangeConfiguration, - () => /** @description codeLensesVisible */ this.configurationService.getValue('mergeEditor.showCodeLenses') ?? false + () => /** @description showDeletionMarkers */ this.configurationService.getValue('mergeEditor.showDeletionMarkers') ); public readonly editor = this.instantiationService.createInstance( diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts index 9ad16bf2105..9b61b9c9279 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts @@ -8,21 +8,16 @@ import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { Toggle } from 'vs/base/browser/ui/toggle/toggle'; import { Action, IAction, Separator } from 'vs/base/common/actions'; import { Codicon } from 'vs/base/common/codicons'; -import { Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { clamp } from 'vs/base/common/numbers'; import { autorun, autorunWithStore, derived, IObservable, ISettableObservable, ITransaction, observableValue, transaction } from 'vs/base/common/observable'; import { noBreakWhitespace } from 'vs/base/common/strings'; import { isDefined } from 'vs/base/common/types'; -import { URI } from 'vs/base/common/uri'; import { EditorExtensionsRegistry, IEditorContributionDescription } from 'vs/editor/browser/editorExtensions'; -import { CodeLens, CodeLensProvider, Command } from 'vs/editor/common/languages'; import { IModelDeltaDecoration, MinimapPosition, OverviewRulerLane } from 'vs/editor/common/model'; -import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { CodeLensContribution } from 'vs/editor/contrib/codelens/browser/codelensController'; import { localize } from 'vs/nls'; import { MenuId } from 'vs/platform/actions/common/actions'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -65,14 +60,6 @@ export class InputCodeEditorView extends CodeEditorView { }, 'update checkboxes') ); - this._register( - autorunWithStore((reader, store) => { - if (this.codeLensesVisible.read(reader)) { - store.add(instantiationService.createInstance(CodeLensPart, this)); - } - }, 'update code lens part') - ); - this._register( createSelectionsAutorun(this, (baseRange, viewModel) => viewModel.model.translateBaseRangeToInput(this.inputNumber, baseRange) @@ -135,12 +122,14 @@ export class InputCodeEditorView extends CodeEditorView { return []; } const model = viewModel.model; + const textModel = (this.inputNumber === 1 ? model.input1 : model.input2).textModel; const activeModifiedBaseRange = viewModel.activeModifiedBaseRange.read(reader); const result = new Array(); const showNonConflictingChanges = viewModel.showNonConflictingChanges.read(reader); + const showDeletionMarkers = this.showDeletionMarkers.read(reader); for (const modifiedBaseRange of model.modifiedBaseRanges.read(reader)) { const range = modifiedBaseRange.getInputRange(this.inputNumber); @@ -159,7 +148,7 @@ export class InputCodeEditorView extends CodeEditorView { if (modifiedBaseRange.isConflicting) { blockClassNames.push('conflicting'); } - const inputClassName = this.inputNumber === 1 ? 'input1' : 'input2'; + const inputClassName = this.inputNumber === 1 ? 'input 1' : 'input 2'; blockClassNames.push(inputClassName); if (!modifiedBaseRange.isConflicting && !showNonConflictingChanges && isHandled) { @@ -171,6 +160,7 @@ export class InputCodeEditorView extends CodeEditorView { options: { showIfCollapsed: true, blockClassName: blockClassNames.join(' '), + blockIsAfterEnd: range.startLineNumber > textModel.getLineCount(), description: 'Merge Editor', minimap: { position: MinimapPosition.Gutter, @@ -200,13 +190,16 @@ export class InputCodeEditorView extends CodeEditorView { if (diff.rangeMappings) { for (const d of diff.rangeMappings) { - result.push({ - range: d.outputRange, - options: { - className: `merge-editor-diff-word ${inputClassName}`, - description: 'Merge Editor' - } - }); + if (showDeletionMarkers || !d.outputRange.isEmpty()) { + result.push({ + range: d.outputRange, + options: { + className: d.outputRange.isEmpty() ? `merge-editor-diff-empty-word ${inputClassName}` : `merge-editor-diff-word ${inputClassName}`, + description: 'Merge Editor', + showIfCollapsed: true, + } + }); + } } } } @@ -216,143 +209,10 @@ export class InputCodeEditorView extends CodeEditorView { }); protected override getEditorContributions(): IEditorContributionDescription[] | undefined { - if (this.codeLensesVisible.get()) { - return undefined; - } return EditorExtensionsRegistry.getEditorContributions().filter(c => c.id !== CodeLensContribution.ID); } } -class CodeLensPart extends Disposable { - public static commandCounter = 0; - - constructor( - inputCodeEditorView: InputCodeEditorView, - @ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService, - ) { - super(); - - const codeLensCommandId = `mergeEditor.codeLensCommandInput${CodeLensPart.commandCounter++}`; - this._register(CommandsRegistry.registerCommand(codeLensCommandId, (accessor, arg) => { - arg(); - })); - - function command(title: string, callback: () => Promise): Command { - return { - title, - id: codeLensCommandId, - arguments: [callback], - }; - } - - const codeLenses = derived<{ codeLenses: CodeLens[]; uri: URI } | undefined>('codeLenses', reader => { - const viewModel = inputCodeEditorView.viewModel.read(reader); - if (!viewModel) { - return undefined; - } - const model = viewModel.model; - const inputData = inputCodeEditorView.inputNumber === 1 ? viewModel.model.input1 : viewModel.model.input2; - - const showNonConflictingChanges = viewModel.showNonConflictingChanges.read(reader); - - return { - codeLenses: viewModel.model.modifiedBaseRanges.read(reader).flatMap(r => { - const range = r.getInputRange(inputCodeEditorView.inputNumber).toRange(); - - const handled = model.isHandled(r).read(reader); - const state = model.getState(r).read(reader); - const result: CodeLens[] = []; - - if (!r.isConflicting && handled && !showNonConflictingChanges) { - return []; - } - - if (!state.conflicting && !state.isInputIncluded(inputCodeEditorView.inputNumber)) { - result.push( - { - range, - command: - !state.isInputIncluded(inputCodeEditorView.inputNumber) - ? command(`$(pass) Accept ${inputData.title}`, async () => { - transaction((tx) => { - model.setState( - r, - state.withInputValue(inputCodeEditorView.inputNumber, true), - true, - tx - ); - }); - }) - : command(`$(error) Remove ${inputData.title}`, async () => { - transaction((tx) => { - model.setState( - r, - state.withInputValue(inputCodeEditorView.inputNumber, false), - true, - tx - ); - }); - }), - } - ); - - if (r.canBeCombined && state.isEmpty) { - result.push({ - range, - command: - state.input1 && state.input2 - ? command(`$(error) Remove Both`, async () => { - transaction((tx) => { - model.setState( - r, - ModifiedBaseRangeState.default, - true, - tx - ); - }); - }) - : command(`$(pass) Accept Both`, async () => { - transaction((tx) => { - model.setState( - r, - state - .withInputValue(inputCodeEditorView.inputNumber, true) - .withInputValue(inputCodeEditorView.otherInputNumber, true), - true, - tx - ); - }); - }), - }); - } - } - if (result.length === 0) { - result.push({ - range: range, - command: command(` `, async () => { }) - }); - } - return result; - }), - uri: inputData.textModel.uri - }; - }); - - const codeLensProvider: CodeLensProvider = { - onDidChange: Event.map(Event.fromObservable(codeLenses), () => codeLensProvider), - async provideCodeLenses(model, token) { - const result = codeLenses.get(); - if (!result || result.uri.toString() !== model.uri.toString()) { - return { lenses: [], dispose: () => { } }; - } - return { lenses: result.codeLenses, dispose: () => { } }; - } - }; - - this._register(languageFeaturesService.codeLensProvider.register({ pattern: '**/*' }, codeLensProvider)); - } -} - export class ModifiedBaseRangeGutterItemModel implements IGutterItemInfo { private readonly model = this.viewModel.model; public readonly range = this.baseRange.getInputRange(this.inputNumber); diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts index 82c2ff6d315..dd5641d28b5 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts @@ -4,26 +4,22 @@ *--------------------------------------------------------------------------------------------*/ import { reset } from 'vs/base/browser/dom'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { CompareResult } from 'vs/base/common/arrays'; import { BugIndicatingError } from 'vs/base/common/errors'; -import { Event } from 'vs/base/common/event'; -import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { autorun, autorunWithStore, derived, IObservable, transaction } from 'vs/base/common/observable'; -import { URI } from 'vs/base/common/uri'; -import { CodeLens, CodeLensProvider, Command } from 'vs/editor/common/languages'; +import { toDisposable } from 'vs/base/common/lifecycle'; +import { autorun, autorunWithStore, derived, IObservable } from 'vs/base/common/observable'; +import { IEditorContributionDescription, EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; import { IModelDeltaDecoration, MinimapPosition, OverviewRulerLane } from 'vs/editor/common/model'; -import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; +import { CodeLensContribution } from 'vs/editor/contrib/codelens/browser/codelensController'; import { localize } from 'vs/nls'; import { MenuId } from 'vs/platform/actions/common/actions'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; -import { MergeMarkersController } from 'vs/workbench/contrib/mergeEditor/browser/mergeMarkers/mergeMarkersController'; import { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model/lineRange'; -import { ModifiedBaseRangeState } from 'vs/workbench/contrib/mergeEditor/browser/model/modifiedBaseRange'; import { applyObservableDecorations, join } from 'vs/workbench/contrib/mergeEditor/browser/utils'; import { handledConflictMinimapOverViewRulerColor, unhandledConflictMinimapOverViewRulerColor } from 'vs/workbench/contrib/mergeEditor/browser/view/colors'; import { EditorGutter } from 'vs/workbench/contrib/mergeEditor/browser/view/editorGutter'; @@ -47,16 +43,6 @@ export class ResultCodeEditorView extends CodeEditorView { this._register(toDisposable(() => isMergeResultEditor.reset())); }); - this._register(new MergeMarkersController(this.editor, this.viewModel)); - - this._register( - autorunWithStore((reader, store) => { - if (this.codeLensesVisible.read(reader)) { - store.add(instantiationService.createInstance(CodeLensPart, this)); - } - }, 'update code lens part') - ); - this.htmlElements.gutterDiv.style.width = '5px'; this._register( @@ -81,18 +67,21 @@ export class ResultCodeEditorView extends CodeEditorView { })); - this._register(autorun('update remainingConflicts label', reader => { - // this is a bit of a hack, but it's the easiest way to get the label to update - // when the view model updates, as the the base class resets the label in the setModel call. - this.viewModel.read(reader); + const remainingConflictsActionBar = this._register(new ActionBar(this.htmlElements.detail)); - const model = this.model.read(reader); + this._register(autorun('update remainingConflicts label', reader => { + const vm = this.viewModel.read(reader); + if (!vm) { + return; + } + + const model = vm.model; if (!model) { return; } const count = model.unhandledConflictsCount.read(reader); - this.htmlElements.detail.innerText = count === 1 + const text = count === 1 ? localize( 'mergeEditor.remainingConflicts', '{0} Conflict Remaining', @@ -104,6 +93,19 @@ export class ResultCodeEditorView extends CodeEditorView { count ); + remainingConflictsActionBar.clear(); + remainingConflictsActionBar.push({ + class: undefined, + enabled: count > 0, + id: 'nextConflict', + label: text, + run() { + vm.goToNextModifiedBaseRange(m => !model.isHandled(m).get()); + }, + tooltip: count > 0 + ? localize('goToNextConflict', 'Go to next conflict') + : localize('allConflictHandled', 'All conflicts handled, the merge can be completed now.'), + }); })); @@ -130,6 +132,7 @@ export class ResultCodeEditorView extends CodeEditorView { return []; } const model = viewModel.model; + const textModel = model.resultTextModel; const result = new Array(); const baseRangeWithStoreAndTouchingDiffs = join( @@ -168,11 +171,13 @@ export class ResultCodeEditorView extends CodeEditorView { continue; } + const range = model.getLineRangeInResult(modifiedBaseRange.baseRange, reader); result.push({ - range: model.getLineRangeInResult(modifiedBaseRange.baseRange, reader).toInclusiveRangeOrEmpty(), + range: range.toInclusiveRangeOrEmpty(), options: { showIfCollapsed: true, blockClassName: blockClassNames.join(' '), + blockIsAfterEnd: range.startLineNumber > textModel.getLineCount(), description: 'Result Diff', minimap: { position: MinimapPosition.Gutter, @@ -184,10 +189,8 @@ export class ResultCodeEditorView extends CodeEditorView { } : undefined } }); - } - if (!modifiedBaseRange || modifiedBaseRange.isConflicting) { for (const diff of m.rights) { const range = diff.outputRange.toInclusiveRange(); @@ -218,148 +221,8 @@ export class ResultCodeEditorView extends CodeEditorView { } return result; }); -} -class CodeLensPart extends Disposable { - public static commandCounter = 0; - - constructor( - resultCodeEditorView: ResultCodeEditorView, - @ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService, - ) { - super(); - - const codeLensCommandId = `mergeEditor.codeLensCommandResult${CodeLensPart.commandCounter++}`; - this._register(CommandsRegistry.registerCommand(codeLensCommandId, (accessor, arg) => { - arg(); - })); - - function command(title: string, callback: () => Promise): Command { - return { - title, - id: codeLensCommandId, - arguments: [callback], - }; - } - - const codeLenses = derived<{ codeLenses: CodeLens[]; uri: URI } | undefined>('codeLenses', reader => { - const viewModel = resultCodeEditorView.viewModel.read(reader); - if (!viewModel) { - return undefined; - } - const model = viewModel.model; - const showNonConflictingChanges = viewModel.showNonConflictingChanges.read(reader); - - return { - codeLenses: viewModel.model.modifiedBaseRanges.read(reader).flatMap(r => { - const range = model.getLineRangeInResult(r.baseRange, reader).toRange(); - - const handled = model.isHandled(r).read(reader); - const state = model.getState(r).read(reader); - const result: CodeLens[] = []; - - if (!r.isConflicting && handled && !showNonConflictingChanges) { - return []; - } - - const stateLabel = ((state: ModifiedBaseRangeState): string => { - if (state.conflicting) { - return '= Manual Resolution'; - } else if (state.isEmpty) { - return '= Base'; - } else { - const labels = []; - if (state.input1) { - labels.push(model.input1.title); - } - if (state.input2) { - labels.push(model.input2.title); - } - return `= ${labels.join(' + ')}`; - } - })(state); - - result.push({ - range, - command: { - title: stateLabel, - id: 'notSupported', - } - }); - - - const stateToggles: CodeLens[] = []; - if (state.input1) { - result.push({ - range, - command: command(`$(error) Remove ${model.input1.title}`, async () => { - transaction((tx) => { - model.setState( - r, - state.withInputValue(1, false), - true, - tx - ); - }); - }), - }); - } - if (state.input2) { - result.push({ - range, - command: command(`$(error) Remove ${model.input2.title}`, async () => { - transaction((tx) => { - model.setState( - r, - state.withInputValue(2, false), - true, - tx - ); - }); - }), - }); - } - if (state.input2First) { - stateToggles.reverse(); - } - result.push(...stateToggles); - - - - if (state.conflicting) { - result.push( - { - range, - command: command(`$(error) Reset to base`, async () => { - transaction((tx) => { - model.setState( - r, - ModifiedBaseRangeState.default, - true, - tx - ); - }); - }) - } - ); - } - return result; - }), - uri: model.resultTextModel.uri, - }; - }); - - const codeLensProvider: CodeLensProvider = { - onDidChange: Event.map(Event.fromObservable(codeLenses), () => codeLensProvider), - async provideCodeLenses(model, token) { - const result = codeLenses.get(); - if (!result || result.uri.toString() !== model.uri.toString()) { - return { lenses: [], dispose: () => { } }; - } - return { lenses: result.codeLenses, dispose: () => { } }; - } - }; - - this._register(languageFeaturesService.codeLensProvider.register({ pattern: '**/*' }, codeLensProvider)); + protected override getEditorContributions(): IEditorContributionDescription[] | undefined { + return EditorExtensionsRegistry.getEditorContributions().filter(c => c.id !== CodeLensContribution.ID); } } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/fixedZoneWidget.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/fixedZoneWidget.ts new file mode 100644 index 00000000000..e761905679d --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/fixedZoneWidget.ts @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { h } from 'vs/base/browser/dom'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { ICodeEditor, IOverlayWidget, IViewZoneChangeAccessor } from 'vs/editor/browser/editorBrowser'; + +export abstract class FixedZoneWidget extends Disposable { + private static counter = 0; + private readonly overlayWidgetId = `fixedZoneWidget-${FixedZoneWidget.counter++}`; + private readonly viewZoneId: string; + + protected readonly widgetDomNode = h('div.fixed-zone-widget').root; + private readonly overlayWidget: IOverlayWidget = { + getId: () => this.overlayWidgetId, + getDomNode: () => this.widgetDomNode, + getPosition: () => null + }; + + constructor( + private readonly editor: ICodeEditor, + viewZoneAccessor: IViewZoneChangeAccessor, + afterLineNumber: number, + height: number, + viewZoneIdsToCleanUp: string[], + ) { + super(); + + this.viewZoneId = viewZoneAccessor.addZone({ + domNode: document.createElement('div'), + afterLineNumber: afterLineNumber, + heightInPx: height, + onComputedHeight: (height) => { + this.widgetDomNode.style.height = `${height}px`; + }, + onDomNodeTop: (top) => { + this.widgetDomNode.style.top = `${top}px`; + } + }); + viewZoneIdsToCleanUp.push(this.viewZoneId); + + this.widgetDomNode.style.left = this.editor.getLayoutInfo().contentLeft + 'px'; + + this.editor.addOverlayWidget(this.overlayWidget); + + this._register({ + dispose: () => { + this.editor.removeOverlayWidget(this.overlayWidget); + }, + }); + } +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/media/mergeEditor.css b/src/vs/workbench/contrib/mergeEditor/browser/view/media/mergeEditor.css index c105dd827fb..63172058846 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/media/mergeEditor.css +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/media/mergeEditor.css @@ -226,6 +226,63 @@ border: 1px solid var(--vscode-mergeEditor-conflict-unhandledFocused-border); } -.merge-conflict-input-selection { - background-color: red; +.merge-editor-conflict-actions { + margin: 0px 3px; + overflow: hidden; + display: inline-block; + text-overflow: ellipsis; + white-space: nowrap; + color: var(--vscode-editorCodeLens-foreground) +} + +.merge-editor-conflict-actions>span, +.merge-editor-conflict-actions>a { + user-select: none; + -webkit-user-select: none; + white-space: nowrap; +} + +.merge-editor-conflict-actions>a { + text-decoration: none; +} + +.merge-editor-conflict-actions>a:hover { + cursor: pointer; + color: var(--vscode-editorLink-activeForeground) !important; +} + +.merge-editor-conflict-actions>a:hover .codicon { + color: var(--vscode-editorLink-activeForeground) !important; +} + +.merge-editor-conflict-actions .codicon { + vertical-align: middle; + color: currentColor !important; + color: var(--vscode-editorCodeLens-foreground); +} + +.merge-editor-conflict-actions>a:hover .codicon::before { + cursor: pointer; +} + +.fixed-zone-widget { + width: 100%; +} + +.merge-editor-diff-empty-word.base { + margin-left: 3px; + border-left: solid var(--vscode-mergeEditor-changeBase-word-background) 3px; +} + +.merge-editor-diff-empty-word.input { + margin-left: 3px; + border-left: solid var(--vscode-mergeEditor-change-word-background) 3px; +} + +.merge-editor-diff-word.base { + background-color: var(--vscode-mergeEditor-changeBase-word-background); +} + +.merge-editor-diff.base { + background-color: var(--vscode-mergeEditor-changeBase-background); } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts index 27d137a9be4..6934099796b 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts @@ -3,16 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { $, Dimension, reset } from 'vs/base/browser/dom'; +import { Dimension, reset } from 'vs/base/browser/dom'; import { Grid, GridNodeDescriptor, IView, SerializableGrid } from 'vs/base/browser/ui/grid/grid'; import { Orientation } from 'vs/base/browser/ui/splitview/splitview'; -import { CompareResult, lastOrDefault } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Color } from 'vs/base/common/color'; import { BugIndicatingError, onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { autorun, autorunWithStore, IObservable, IReader, observableValue, transaction } from 'vs/base/common/observable'; +import { Disposable, DisposableStore, IDisposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { autorun, autorunWithStore, IObservable, IReader, observableFromEvent, observableValue, transaction } from 'vs/base/common/observable'; import { basename, isEqual } from 'vs/base/common/resources'; import { isDefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; @@ -38,15 +37,13 @@ import { applyTextEditorOptions } from 'vs/workbench/common/editor/editorOptions import { readTransientState, writeTransientState } from 'vs/workbench/contrib/codeEditor/browser/toggleWordWrap'; import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; import { IMergeEditorInputModel } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel'; -import { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model/lineRange'; -import { DetailedLineRangeMapping } from 'vs/workbench/contrib/mergeEditor/browser/model/mapping'; import { MergeEditorModel } from 'vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel'; -import { ModifiedBaseRange } from 'vs/workbench/contrib/mergeEditor/browser/model/modifiedBaseRange'; -import { deepMerge, join, PersistentStore, thenIfNotDisposed } from 'vs/workbench/contrib/mergeEditor/browser/utils'; +import { deepMerge, PersistentStore, thenIfNotDisposed } from 'vs/workbench/contrib/mergeEditor/browser/utils'; import { BaseCodeEditorView } from 'vs/workbench/contrib/mergeEditor/browser/view/editors/baseCodeEditorView'; import { ScrollSynchronizer } from 'vs/workbench/contrib/mergeEditor/browser/view/scrollSynchronizer'; import { MergeEditorViewModel } from 'vs/workbench/contrib/mergeEditor/browser/view/viewModel'; -import { ctxIsMergeEditor, ctxMergeBaseUri, ctxMergeEditorLayout, ctxMergeEditorShowBase, ctxMergeEditorShowNonConflictingChanges, ctxMergeResultUri, MergeEditorLayoutKind } from 'vs/workbench/contrib/mergeEditor/common/mergeEditor'; +import { ViewZoneComputer } from 'vs/workbench/contrib/mergeEditor/browser/view/viewZones'; +import { ctxIsMergeEditor, ctxMergeBaseUri, ctxMergeEditorLayout, ctxMergeEditorShowBase, ctxMergeEditorShowBaseAtTop, ctxMergeEditorShowNonConflictingChanges, ctxMergeResultUri, MergeEditorLayoutKind } from 'vs/workbench/contrib/mergeEditor/common/mergeEditor'; import { settingsSashBorder } from 'vs/workbench/contrib/preferences/common/settingsEditorColorRegistry'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorResolverService, MergeEditorInputFactoryFunction, RegisteredEditorPriority } from 'vs/workbench/services/editor/common/editorResolverService'; @@ -54,7 +51,6 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import './colors'; import { InputCodeEditorView } from './editors/inputCodeEditorView'; import { ResultCodeEditorView } from './editors/resultCodeEditorView'; -import { getAlignments } from './lineAlignment'; export class MergeEditor extends AbstractTextEditor { @@ -77,12 +73,13 @@ export class MergeEditor extends AbstractTextEditor { private readonly inputResultView = this._register(this.instantiationService.createInstance(ResultCodeEditorView, this._viewModel)); private readonly _layoutMode = this.instantiationService.createInstance(MergeEditorLayoutStore); private readonly _layoutModeObs = observableValue('layoutMode', this._layoutMode.value); - private readonly _ctxIsMergeEditor: IContextKey; - private readonly _ctxUsesColumnLayout: IContextKey; - private readonly _ctxShowBase: IContextKey; - private readonly _ctxResultUri: IContextKey; - private readonly _ctxBaseUri: IContextKey; - private readonly _ctxShowNonConflictingChanges: IContextKey; + private readonly _ctxIsMergeEditor: IContextKey = ctxIsMergeEditor.bindTo(this.contextKeyService); + private readonly _ctxUsesColumnLayout: IContextKey = ctxMergeEditorLayout.bindTo(this.contextKeyService); + private readonly _ctxShowBase: IContextKey = ctxMergeEditorShowBase.bindTo(this.contextKeyService); + private readonly _ctxShowBaseAtTop = ctxMergeEditorShowBaseAtTop.bindTo(this.contextKeyService); + private readonly _ctxResultUri: IContextKey = ctxMergeResultUri.bindTo(this.contextKeyService); + private readonly _ctxBaseUri: IContextKey = ctxMergeBaseUri.bindTo(this.contextKeyService); + private readonly _ctxShowNonConflictingChanges: IContextKey = ctxMergeEditorShowNonConflictingChanges.bindTo(this.contextKeyService); private readonly _inputModel = observableValue('inputModel', undefined); public get inputModel(): IObservable { return this._inputModel; @@ -95,9 +92,22 @@ export class MergeEditor extends AbstractTextEditor { return !!this._configurationService.getValue('mergeEditor.writableInputs'); } + private readonly viewZoneComputer = new ViewZoneComputer( + this.input1View.editor, + this.input2View.editor, + this.inputResultView.editor, + ); + + protected readonly codeLensesVisible = observableFromEvent( + this.configurationService.onDidChangeConfiguration, + () => /** @description codeLensesVisible */ this.configurationService.getValue('mergeEditor.showCodeLenses') ?? true + ); + + private readonly scrollSynchronizer = this._register(new ScrollSynchronizer(this._viewModel, this.input1View, this.input2View, this.baseView, this.inputResultView, this._layoutModeObs)); + constructor( @IInstantiationService instantiation: IInstantiationService, - @IContextKeyService contextKeyService: IContextKeyService, + @IContextKeyService private readonly contextKeyService: IContextKeyService, @ITelemetryService telemetryService: ITelemetryService, @IStorageService storageService: IStorageService, @IThemeService themeService: IThemeService, @@ -107,17 +117,9 @@ export class MergeEditor extends AbstractTextEditor { @IEditorGroupsService editorGroupService: IEditorGroupsService, @IFileService fileService: IFileService, @ICodeEditorService private readonly _codeEditorService: ICodeEditorService, + @IConfigurationService private readonly configurationService: IConfigurationService ) { super(MergeEditor.ID, telemetryService, instantiation, storageService, textResourceConfigurationService, themeService, editorService, editorGroupService, fileService); - - this._ctxIsMergeEditor = ctxIsMergeEditor.bindTo(contextKeyService); - this._ctxUsesColumnLayout = ctxMergeEditorLayout.bindTo(contextKeyService); - this._ctxBaseUri = ctxMergeBaseUri.bindTo(contextKeyService); - this._ctxResultUri = ctxMergeResultUri.bindTo(contextKeyService); - this._ctxShowBase = ctxMergeEditorShowBase.bindTo(contextKeyService); - this._ctxShowNonConflictingChanges = ctxMergeEditorShowNonConflictingChanges.bindTo(contextKeyService); - - this._register(new ScrollSynchronizer(this._viewModel, this.input1View, this.input2View, this.baseView, this.inputResultView)); } override dispose(): void { @@ -212,69 +214,55 @@ export class MergeEditor extends AbstractTextEditor { this._ctxResultUri.reset(); })); + // Set the view zones before restoring view state! // Otherwise scrolling will be off this._sessionDisposables.add(autorunWithStore((reader, store) => { - const input1ViewZoneIds: string[] = []; - const input2ViewZoneIds: string[] = []; - const baseViewZoneIds: string[] = []; - const resultViewZoneIds: string[] = []; const baseView = this.baseView.read(reader); this.inputResultView.editor.changeViewZones(resultViewZoneAccessor => { - const actualResultViewZoneAccessor = this._layoutModeObs.read(reader).kind === 'columns' ? resultViewZoneAccessor : undefined; + const layout = this._layoutModeObs.read(reader); + const shouldAlignResult = layout.kind === 'columns'; + const shouldAlignBase = layout.kind === 'mixed' && !layout.showBaseAtTop; this.input1View.editor.changeViewZones(input1ViewZoneAccessor => { this.input2View.editor.changeViewZones(input2ViewZoneAccessor => { if (baseView) { baseView.editor.changeViewZones(baseViewZoneAccessor => { - setViewZones(reader, - input1ViewZoneIds, input1ViewZoneAccessor, - input2ViewZoneIds, input2ViewZoneAccessor, - baseViewZoneIds, /*baseViewZoneAccessor,*/ undefined, - resultViewZoneIds, actualResultViewZoneAccessor, - ); + store.add(this.setViewZones(reader, + viewModel, + this.input1View.editor, + input1ViewZoneAccessor, + this.input2View.editor, + input2ViewZoneAccessor, + baseView.editor, + baseViewZoneAccessor, + shouldAlignBase, + this.inputResultView.editor, + resultViewZoneAccessor, + shouldAlignResult + )); }); } else { - setViewZones(reader, - input1ViewZoneIds, + store.add(this.setViewZones(reader, + viewModel, + this.input1View.editor, input1ViewZoneAccessor, - input2ViewZoneIds, + this.input2View.editor, input2ViewZoneAccessor, - baseViewZoneIds, undefined, - resultViewZoneIds, actualResultViewZoneAccessor, - ); + undefined, + false, + this.inputResultView.editor, + resultViewZoneAccessor, + shouldAlignResult + )); } }); }); }); - store.add({ - dispose: () => { - this.input1View.editor.changeViewZones(a => { - for (const zone of input1ViewZoneIds) { - a.removeZone(zone); - } - }); - this.input2View.editor.changeViewZones(a => { - for (const zone of input2ViewZoneIds) { - a.removeZone(zone); - } - }); - this.baseView.get()?.editor.changeViewZones(a => { - for (const zone of baseViewZoneIds) { - a.removeZone(zone); - } - }); - this.inputResultView.editor.changeViewZones(a => { - for (const zone of resultViewZoneIds) { - a.removeZone(zone); - } - }); - } - }); - + this.scrollSynchronizer.updateScrolling(); }, 'update alignment view zones')); const viewState = this.loadEditorViewState(input, context); @@ -287,6 +275,10 @@ export class MergeEditor extends AbstractTextEditor { return; } this.input1View.editor.revealLineInCenter(firstConflict.input1Range.startLineNumber); + transaction(tx => { + /** @description setActiveModifiedBaseRange */ + viewModel.setActiveModifiedBaseRange(firstConflict, tx); + }); })); } @@ -340,146 +332,80 @@ export class MergeEditor extends AbstractTextEditor { ); } }); + } - function setViewZones( - reader: IReader, - input1ViewZoneIds: string[], - input1ViewZoneAccessor: IViewZoneChangeAccessor, - input2ViewZoneIds: string[], - input2ViewZoneAccessor: IViewZoneChangeAccessor, - baseViewZoneIds: string[], - baseViewZoneAccessor: IViewZoneChangeAccessor | undefined, - resultViewZoneIds: string[], - resultViewZoneAccessor: IViewZoneChangeAccessor | undefined, - ) { - let input1LinesAdded = 0; - let input2LinesAdded = 0; - let baseLinesAdded = 0; - let resultLinesAdded = 0; + private setViewZones( + reader: IReader, + viewModel: MergeEditorViewModel, + input1Editor: ICodeEditor, + input1ViewZoneAccessor: IViewZoneChangeAccessor, + input2Editor: ICodeEditor, + input2ViewZoneAccessor: IViewZoneChangeAccessor, + baseEditor: ICodeEditor | undefined, + baseViewZoneAccessor: IViewZoneChangeAccessor | undefined, + shouldAlignBase: boolean, + resultEditor: ICodeEditor, + resultViewZoneAccessor: IViewZoneChangeAccessor, + shouldAlignResult: boolean, + ): IDisposable { + const input1ViewZoneIds: string[] = []; + const input2ViewZoneIds: string[] = []; + const baseViewZoneIds: string[] = []; + const resultViewZoneIds: string[] = []; - const resultDiffs = model.baseResultDiffs.read(reader); - const baseRangeWithStoreAndTouchingDiffs = join( - model.modifiedBaseRanges.read(reader), - resultDiffs, - (baseRange, diff) => - baseRange.baseRange.touches(diff.inputRange) - ? CompareResult.neitherLessOrGreaterThan - : LineRange.compareByStart( - baseRange.baseRange, - diff.inputRange - ) - ); + const viewZones = this.viewZoneComputer.computeViewZones(reader, viewModel, { + codeLensesVisible: this.codeLensesVisible.read(reader), + showNonConflictingChanges: this.showNonConflictingChanges.read(reader), + shouldAlignBase, + shouldAlignResult, + }); - let lastModifiedBaseRange: ModifiedBaseRange | undefined = undefined; - let lastBaseResultDiff: DetailedLineRangeMapping | undefined = undefined; - for (const m of baseRangeWithStoreAndTouchingDiffs) { - interface LineAlignment { - baseLine: number; - input1Line?: number; - input2Line?: number; - resultLine?: number; - } + const disposableStore = new DisposableStore(); - const lastResultDiff = lastOrDefault(m.rights)!; - if (lastResultDiff) { - lastBaseResultDiff = lastResultDiff; - } - let alignedLines: LineAlignment[]; - if (m.left) { - alignedLines = getAlignments(m.left).map(a => ({ - input1Line: a[0], - baseLine: a[1], - input2Line: a[2], - resultLine: undefined, - })); - - lastModifiedBaseRange = m.left; - // This is a total hack. - alignedLines[alignedLines.length - 1].resultLine = - m.left.baseRange.endLineNumberExclusive - + (lastBaseResultDiff ? lastBaseResultDiff.resultingDeltaFromOriginalToModified : 0); - - } else { - alignedLines = [{ - baseLine: lastResultDiff.inputRange.endLineNumberExclusive, - input1Line: lastResultDiff.inputRange.endLineNumberExclusive + (lastModifiedBaseRange ? (lastModifiedBaseRange.input1Range.endLineNumberExclusive - lastModifiedBaseRange.baseRange.endLineNumberExclusive) : 0), - input2Line: lastResultDiff.inputRange.endLineNumberExclusive + (lastModifiedBaseRange ? (lastModifiedBaseRange.input2Range.endLineNumberExclusive - lastModifiedBaseRange.baseRange.endLineNumberExclusive) : 0), - resultLine: lastResultDiff.outputRange.endLineNumberExclusive, - }]; - } - - for (const { input1Line, baseLine, input2Line, resultLine } of alignedLines) { - if (!baseViewZoneAccessor && (input1Line === undefined || input2Line === undefined)) { - continue; - } - - const input1Line_ = - input1Line !== undefined ? input1Line + input1LinesAdded : -1; - const input2Line_ = - input2Line !== undefined ? input2Line + input2LinesAdded : -1; - const baseLine_ = baseLine + baseLinesAdded; - const resultLine_ = resultLine !== undefined ? resultLine + resultLinesAdded : -1; - - const max = Math.max(baseViewZoneAccessor ? baseLine_ : 0, input1Line_, input2Line_, resultLine_); - - if (input1Line !== undefined) { - const diffInput1 = max - input1Line_; - if (diffInput1 > 0) { - input1ViewZoneIds.push( - input1ViewZoneAccessor.addZone({ - afterLineNumber: input1Line - 1, - heightInLines: diffInput1, - domNode: $('div.diagonal-fill'), - }) - ); - input1LinesAdded += diffInput1; - } - } - - if (input2Line !== undefined) { - const diffInput2 = max - input2Line_; - if (diffInput2 > 0) { - input2ViewZoneIds.push( - input2ViewZoneAccessor.addZone({ - afterLineNumber: input2Line - 1, - heightInLines: diffInput2, - domNode: $('div.diagonal-fill'), - }) - ); - input2LinesAdded += diffInput2; - } - } - - if (baseViewZoneAccessor) { - const diffBase = max - baseLine_; - if (diffBase > 0) { - baseViewZoneIds.push( - baseViewZoneAccessor.addZone({ - afterLineNumber: baseLine - 1, - heightInLines: diffBase, - domNode: $('div.diagonal-fill'), - }) - ); - baseLinesAdded += diffBase; - } - } - - if (resultViewZoneAccessor && resultLine !== undefined) { - const diffResult = max - resultLine_; - if (diffResult > 0) { - resultViewZoneIds.push( - resultViewZoneAccessor.addZone({ - afterLineNumber: resultLine - 1, - heightInLines: diffResult, - domNode: $('div.diagonal-fill'), - }) - ); - resultLinesAdded += diffResult; - } - } - } + if (baseViewZoneAccessor) { + for (const v of viewZones.baseViewZones) { + v.create(baseViewZoneAccessor, baseViewZoneIds, disposableStore); } } + + for (const v of viewZones.resultViewZones) { + v.create(resultViewZoneAccessor, resultViewZoneIds, disposableStore); + } + + for (const v of viewZones.input1ViewZones) { + v.create(input1ViewZoneAccessor, input1ViewZoneIds, disposableStore); + } + + for (const v of viewZones.input2ViewZones) { + v.create(input2ViewZoneAccessor, input2ViewZoneIds, disposableStore); + } + + disposableStore.add({ + dispose: () => { + input1Editor.changeViewZones(a => { + for (const zone of input1ViewZoneIds) { + a.removeZone(zone); + } + }); + input2Editor.changeViewZones(a => { + for (const zone of input2ViewZoneIds) { + a.removeZone(zone); + } + }); + baseEditor?.changeViewZones(a => { + for (const zone of baseViewZoneIds) { + a.removeZone(zone); + } + }); + resultEditor.changeViewZones(a => { + for (const zone of resultViewZoneIds) { + a.removeZone(zone); + } + }); + } + }); + + return disposableStore; } override setOptions(options: ITextEditorOptions | undefined): void { @@ -547,6 +473,24 @@ export class MergeEditor extends AbstractTextEditor { }); } + public toggleShowBaseTop(): void { + const showBaseTop = this._layoutMode.value.showBase && this._layoutMode.value.showBaseAtTop; + this.setLayout({ + ...this._layoutMode.value, + showBaseAtTop: true, + showBase: !showBaseTop, + }); + } + + public toggleShowBaseCenter(): void { + const showBaseCenter = this._layoutMode.value.showBase && !this._layoutMode.value.showBaseAtTop; + this.setLayout({ + ...this._layoutMode.value, + showBaseAtTop: false, + showBase: !showBaseCenter, + }); + } + public setLayoutKind(kind: MergeEditorLayoutKind): void { this.setLayout({ ...this._layoutMode.value, @@ -590,13 +534,17 @@ export class MergeEditor extends AbstractTextEditor { if (layout.kind === 'mixed') { this.setGrid([ - layout.showBase ? { + layout.showBaseAtTop && layout.showBase ? { size: 38, data: this.baseView.get()!.view } : undefined, { size: 38, - groups: [{ data: this.input1View.view }, { data: this.input2View.view }] + groups: [ + { data: this.input1View.view }, + !layout.showBaseAtTop && layout.showBase ? { data: this.baseView.get()!.view } : undefined, + { data: this.input2View.view } + ].filter(isDefined) }, { size: 62, @@ -619,6 +567,7 @@ export class MergeEditor extends AbstractTextEditor { this._layoutMode.value = layout; this._ctxUsesColumnLayout.set(layout.kind); this._ctxShowBase.set(layout.showBase); + this._ctxShowBaseAtTop.set(layout.showBaseAtTop); this._onDidChangeSizeConstraints.fire(); this._layoutModeObs.set(layout, tx); }); @@ -693,21 +642,22 @@ export class MergeEditor extends AbstractTextEditor { } } -interface IMergeEditorLayout { +export interface IMergeEditorLayout { readonly kind: MergeEditorLayoutKind; readonly showBase: boolean; + readonly showBaseAtTop: boolean; } // TODO use PersistentStore class MergeEditorLayoutStore { private static readonly _key = 'mergeEditor/layout'; - private _value: IMergeEditorLayout = { kind: 'mixed', showBase: false }; + private _value: IMergeEditorLayout = { kind: 'mixed', showBase: false, showBaseAtTop: true }; constructor(@IStorageService private _storageService: IStorageService) { const value = _storageService.get(MergeEditorLayoutStore._key, StorageScope.PROFILE, 'mixed'); if (value === 'mixed' || value === 'columns') { - this._value = { kind: value, showBase: false }; + this._value = { kind: value, showBase: false, showBaseAtTop: true }; } else if (value) { try { this._value = JSON.parse(value); diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/scrollSynchronizer.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/scrollSynchronizer.ts index 854418a297b..1fd54b6b7e6 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/scrollSynchronizer.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/scrollSynchronizer.ts @@ -10,6 +10,7 @@ import { ScrollType } from 'vs/editor/common/editorCommon'; import { DocumentLineRangeMap } from 'vs/workbench/contrib/mergeEditor/browser/model/mapping'; import { ReentrancyBarrier } from 'vs/workbench/contrib/mergeEditor/browser/utils'; import { BaseCodeEditorView } from 'vs/workbench/contrib/mergeEditor/browser/view/editors/baseCodeEditorView'; +import { IMergeEditorLayout } from 'vs/workbench/contrib/mergeEditor/browser/view/mergeEditor'; import { MergeEditorViewModel } from 'vs/workbench/contrib/mergeEditor/browser/view/viewModel'; import { InputCodeEditorView } from './editors/inputCodeEditorView'; import { ResultCodeEditorView } from './editors/resultCodeEditorView'; @@ -19,21 +20,44 @@ export class ScrollSynchronizer extends Disposable { private readonly reentrancyBarrier = new ReentrancyBarrier(); + public readonly updateScrolling: () => void; + + private get shouldAlignResult() { return this.layout.get().kind === 'columns'; } + private get shouldAlignBase() { return this.layout.get().kind === 'mixed' && !this.layout.get().showBaseAtTop; } + constructor( private readonly viewModel: IObservable, private readonly input1View: InputCodeEditorView, private readonly input2View: InputCodeEditorView, private readonly baseView: IObservable, - private readonly inputResultView: ResultCodeEditorView + private readonly inputResultView: ResultCodeEditorView, + private readonly layout: IObservable, ) { super(); - const handleInput1OnScroll = () => { - const mapping = this.model?.input1ResultMapping.get(); - this.synchronizeScrolling(this.input1View.editor, this.inputResultView.editor, mapping); + const handleInput1OnScroll = this.updateScrolling = () => { + if (!this.model) { + return; + } + this.input2View.editor.setScrollTop(this.input1View.editor.getScrollTop(), ScrollType.Immediate); - this.baseView.get()?.editor.setScrollTop(this.input1View.editor.getScrollTop(), ScrollType.Immediate); + if (this.shouldAlignResult) { + this.inputResultView.editor.setScrollTop(this.input1View.editor.getScrollTop(), ScrollType.Immediate); + } else { + const mappingInput1Result = this.model!.input1ResultMapping.get(); + this.synchronizeScrolling(this.input1View.editor, this.inputResultView.editor, mappingInput1Result); + } + + const baseView = this.baseView.get(); + if (baseView) { + if (this.shouldAlignBase) { + this.baseView.get()?.editor.setScrollTop(this.input1View.editor.getScrollTop(), ScrollType.Immediate); + } else { + const mapping = new DocumentLineRangeMap(this.model!.baseInput1Diffs.get(), -1).reverse(); + this.synchronizeScrolling(this.input1View.editor, baseView.editor, mapping); + } + } }; this._store.add( @@ -54,12 +78,29 @@ export class ScrollSynchronizer extends Disposable { this._store.add( this.input2View.editor.onDidScrollChange( this.reentrancyBarrier.makeExclusive((c) => { + if (!this.model) { + return; + } + if (c.scrollTopChanged) { - const mapping = this.model?.input2ResultMapping.get(); - this.synchronizeScrolling(this.input2View.editor, this.inputResultView.editor, mapping); this.input1View.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); - this.baseView.get()?.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); + if (this.shouldAlignResult) { + this.inputResultView.editor.setScrollTop(this.input2View.editor.getScrollTop(), ScrollType.Immediate); + } else { + const mappingInput2Result = this.model!.input2ResultMapping.get(); + this.synchronizeScrolling(this.input2View.editor, this.inputResultView.editor, mappingInput2Result); + } + + const baseView = this.baseView.get(); + if (baseView && this.model) { + if (this.shouldAlignBase) { + this.baseView.get()?.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); + } else { + const mapping = new DocumentLineRangeMap(this.model!.baseInput2Diffs.get(), -1).reverse(); + this.synchronizeScrolling(this.input2View.editor, baseView.editor, mapping); + } + } } if (c.scrollLeftChanged) { this.baseView.get()?.editor.setScrollLeft(c.scrollLeft, ScrollType.Immediate); @@ -73,14 +114,20 @@ export class ScrollSynchronizer extends Disposable { this.inputResultView.editor.onDidScrollChange( this.reentrancyBarrier.makeExclusive((c) => { if (c.scrollTopChanged) { - const mapping1 = this.model?.resultInput1Mapping.get(); - this.synchronizeScrolling(this.inputResultView.editor, this.input1View.editor, mapping1); - const mapping2 = this.model?.resultInput2Mapping.get(); - this.synchronizeScrolling(this.inputResultView.editor, this.input2View.editor, mapping2); + if (this.shouldAlignResult) { + this.input1View.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); + this.input2View.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); + } else { + const mapping1 = this.model?.resultInput1Mapping.get(); + this.synchronizeScrolling(this.inputResultView.editor, this.input1View.editor, mapping1); + + const mapping2 = this.model?.resultInput2Mapping.get(); + this.synchronizeScrolling(this.inputResultView.editor, this.input2View.editor, mapping2); + } const baseMapping = this.model?.resultBaseMapping.get(); const baseView = this.baseView.get(); - if (baseView) { + if (baseView && this.model) { this.synchronizeScrolling(this.inputResultView.editor, baseView.editor, baseMapping); } } @@ -100,8 +147,19 @@ export class ScrollSynchronizer extends Disposable { store.add(baseView.editor.onDidScrollChange( this.reentrancyBarrier.makeExclusive((c) => { if (c.scrollTopChanged) { - this.input1View.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); - this.input2View.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); + if (!this.model) { + return; + } + if (this.shouldAlignBase) { + this.input1View.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); + this.input2View.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); + } else { + const baseInput1Mapping = new DocumentLineRangeMap(this.model!.baseInput1Diffs.get(), -1); + this.synchronizeScrolling(baseView.editor, this.input1View.editor, baseInput1Mapping); + + const baseInput2Mapping = new DocumentLineRangeMap(this.model!.baseInput2Diffs.get(), -1); + this.synchronizeScrolling(baseView.editor, this.input2View.editor, baseInput2Mapping); + } const baseMapping = this.model?.baseResultMapping.get(); this.synchronizeScrolling(baseView.editor, this.inputResultView.editor, baseMapping); @@ -116,20 +174,6 @@ export class ScrollSynchronizer extends Disposable { } }, 'set baseViewEditor.onDidScrollChange') ); - - this._store.add( - autorunWithStore((reader, store) => { - const vm = this.viewModel.read(reader); - if (!vm) { - return; - } - this.baseView.read(reader); - - this.reentrancyBarrier.runExclusively(() => { - handleInput1OnScroll(); - }); - }, 'update scroll when base view changes') - ); } private synchronizeScrolling(scrollingEditor: CodeEditorWidget, targetEditor: CodeEditorWidget, mapping: DocumentLineRangeMap | undefined) { diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts index d16dc21b25d..570e27015f4 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts @@ -59,6 +59,16 @@ export class MergeEditorViewModel extends Disposable { return view ? { view, counter: this.counter++ } : lastValue || { view: undefined, counter: this.counter++ }; }); + public readonly baseShowDiffAgainst = derived<1 | 2 | undefined>('baseShowDiffAgainst', reader => { + const lastFocusedEditor = this.lastFocusedEditor.read(reader); + if (lastFocusedEditor.view === this.inputCodeEditorView1) { + return 1; + } else if (lastFocusedEditor.view === this.inputCodeEditorView2) { + return 2; + } + return undefined; + }); + public readonly selectionInBase = derived('selectionInBase', (reader) => { const sourceEditor = this.lastFocusedEditor.read(reader).view; if (!sourceEditor) { @@ -124,6 +134,10 @@ export class MergeEditorViewModel extends Disposable { } ); + public setActiveModifiedBaseRange(range: ModifiedBaseRange | undefined, tx: ITransaction): void { + this.manuallySetActiveModifiedBaseRange.set({ range, counter: this.counter++ }, tx); + } + public setState( baseRange: ModifiedBaseRange, state: ModifiedBaseRangeState, @@ -146,11 +160,21 @@ export class MergeEditorViewModel extends Disposable { if (modifiedBaseRange) { const range = this.getRangeOfModifiedBaseRange(editor, modifiedBaseRange, undefined); editor.editor.focus(); + + let startLineNumber = range.startLineNumber; + let endLineNumberExclusive = range.endLineNumberExclusive; + if (range.startLineNumber > editor.editor.getModel()!.getLineCount()) { + transaction(tx => { + this.setActiveModifiedBaseRange(modifiedBaseRange, tx); + }); + startLineNumber = endLineNumberExclusive = editor.editor.getModel()!.getLineCount(); + } + editor.editor.setPosition({ - lineNumber: range.startLineNumber, - column: editor.editor.getModel()!.getLineFirstNonWhitespaceColumn(range.startLineNumber), + lineNumber: startLineNumber, + column: editor.editor.getModel()!.getLineFirstNonWhitespaceColumn(startLineNumber), }); - editor.editor.revealLinesNearTop(range.startLineNumber, range.endLineNumberExclusive, ScrollType.Smooth); + editor.editor.revealLinesNearTop(startLineNumber, endLineNumberExclusive, ScrollType.Smooth); } } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/viewZones.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/viewZones.ts new file mode 100644 index 00000000000..ce3ec967375 --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/viewZones.ts @@ -0,0 +1,253 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { $ } from 'vs/base/browser/dom'; +import { CompareResult, lastOrDefault } from 'vs/base/common/arrays'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { IObservable, IReader } from 'vs/base/common/observable'; +import { ICodeEditor, IViewZoneChangeAccessor } from 'vs/editor/browser/editorBrowser'; +import { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model/lineRange'; +import { DetailedLineRangeMapping } from 'vs/workbench/contrib/mergeEditor/browser/model/mapping'; +import { ModifiedBaseRange } from 'vs/workbench/contrib/mergeEditor/browser/model/modifiedBaseRange'; +import { join } from 'vs/workbench/contrib/mergeEditor/browser/utils'; +import { ActionsSource, ConflictActionsFactory, IContentWidgetAction } from 'vs/workbench/contrib/mergeEditor/browser/view/conflictActions'; +import { getAlignments } from 'vs/workbench/contrib/mergeEditor/browser/view/lineAlignment'; +import { MergeEditorViewModel } from 'vs/workbench/contrib/mergeEditor/browser/view/viewModel'; + +export class ViewZoneComputer { + private readonly conflictActionsFactoryInput1 = new ConflictActionsFactory(this.input1Editor); + private readonly conflictActionsFactoryInput2 = new ConflictActionsFactory(this.input2Editor); + private readonly conflictActionsFactoryResult = new ConflictActionsFactory(this.resultEditor); + + constructor( + private readonly input1Editor: ICodeEditor, + private readonly input2Editor: ICodeEditor, + private readonly resultEditor: ICodeEditor, + ) { } + + public computeViewZones( + reader: IReader, + viewModel: MergeEditorViewModel, + options: { + shouldAlignResult: boolean; + shouldAlignBase: boolean; + codeLensesVisible: boolean; + showNonConflictingChanges: boolean; + } + ): MergeEditorViewZones { + let input1LinesAdded = 0; + let input2LinesAdded = 0; + let baseLinesAdded = 0; + let resultLinesAdded = 0; + + const input1ViewZones: MergeEditorViewZone[] = []; + const input2ViewZones: MergeEditorViewZone[] = []; + const baseViewZones: MergeEditorViewZone[] = []; + const resultViewZones: MergeEditorViewZone[] = []; + + const model = viewModel.model; + + const resultDiffs = model.baseResultDiffs.read(reader); + const baseRangeWithStoreAndTouchingDiffs = join( + model.modifiedBaseRanges.read(reader), + resultDiffs, + (baseRange, diff) => + baseRange.baseRange.touches(diff.inputRange) + ? CompareResult.neitherLessOrGreaterThan + : LineRange.compareByStart( + baseRange.baseRange, + diff.inputRange + ) + ); + + const shouldShowCodeLenses = options.codeLensesVisible; + const showNonConflictingChanges = options.showNonConflictingChanges; + + let lastModifiedBaseRange: ModifiedBaseRange | undefined = undefined; + let lastBaseResultDiff: DetailedLineRangeMapping | undefined = undefined; + for (const m of baseRangeWithStoreAndTouchingDiffs) { + if (shouldShowCodeLenses && m.left && (m.left.isConflicting || showNonConflictingChanges || !model.isHandled(m.left).read(reader))) { + const actions = new ActionsSource(viewModel, m.left); + if (options.shouldAlignResult || !actions.inputIsEmpty.read(reader)) { + input1ViewZones.push(new CommandViewZone(this.conflictActionsFactoryInput1, m.left.input1Range.startLineNumber - 1, actions.itemsInput1)); + input2ViewZones.push(new CommandViewZone(this.conflictActionsFactoryInput2, m.left.input2Range.startLineNumber - 1, actions.itemsInput2)); + if (options.shouldAlignBase) { + baseViewZones.push(new Placeholder(m.left.baseRange.startLineNumber - 1, 16)); + } + } + const afterLineNumber = m.left.baseRange.startLineNumber + (lastBaseResultDiff?.resultingDeltaFromOriginalToModified ?? 0) - 1; + resultViewZones.push(new CommandViewZone(this.conflictActionsFactoryResult, afterLineNumber, actions.resultItems)); + + } + + const lastResultDiff = lastOrDefault(m.rights)!; + if (lastResultDiff) { + lastBaseResultDiff = lastResultDiff; + } + let alignedLines: LineAlignment[]; + if (m.left) { + alignedLines = getAlignments(m.left).map(a => ({ + input1Line: a[0], + baseLine: a[1], + input2Line: a[2], + resultLine: undefined, + })); + + lastModifiedBaseRange = m.left; + // This is a total hack. + alignedLines[alignedLines.length - 1].resultLine = + m.left.baseRange.endLineNumberExclusive + + (lastBaseResultDiff ? lastBaseResultDiff.resultingDeltaFromOriginalToModified : 0); + + } else { + alignedLines = [{ + baseLine: lastResultDiff.inputRange.endLineNumberExclusive, + input1Line: lastResultDiff.inputRange.endLineNumberExclusive + (lastModifiedBaseRange ? (lastModifiedBaseRange.input1Range.endLineNumberExclusive - lastModifiedBaseRange.baseRange.endLineNumberExclusive) : 0), + input2Line: lastResultDiff.inputRange.endLineNumberExclusive + (lastModifiedBaseRange ? (lastModifiedBaseRange.input2Range.endLineNumberExclusive - lastModifiedBaseRange.baseRange.endLineNumberExclusive) : 0), + resultLine: lastResultDiff.outputRange.endLineNumberExclusive, + }]; + } + + for (const { input1Line, baseLine, input2Line, resultLine } of alignedLines) { + if (!options.shouldAlignBase && (input1Line === undefined || input2Line === undefined)) { + continue; + } + + const input1Line_ = + input1Line !== undefined ? input1Line + input1LinesAdded : -1; + const input2Line_ = + input2Line !== undefined ? input2Line + input2LinesAdded : -1; + const baseLine_ = baseLine + baseLinesAdded; + const resultLine_ = resultLine !== undefined ? resultLine + resultLinesAdded : -1; + + const max = Math.max(options.shouldAlignBase ? baseLine_ : 0, input1Line_, input2Line_, options.shouldAlignResult ? resultLine_ : 0); + + if (input1Line !== undefined) { + const diffInput1 = max - input1Line_; + if (diffInput1 > 0) { + input1ViewZones.push(new Spacer(input1Line - 1, diffInput1)); + input1LinesAdded += diffInput1; + } + } + + if (input2Line !== undefined) { + const diffInput2 = max - input2Line_; + if (diffInput2 > 0) { + input2ViewZones.push(new Spacer(input2Line - 1, diffInput2)); + input2LinesAdded += diffInput2; + } + } + + if (options.shouldAlignBase) { + const diffBase = max - baseLine_; + if (diffBase > 0) { + baseViewZones.push(new Spacer(baseLine - 1, diffBase)); + baseLinesAdded += diffBase; + } + } + + if (options.shouldAlignResult && resultLine !== undefined) { + const diffResult = max - resultLine_; + if (diffResult > 0) { + resultViewZones.push(new Spacer(resultLine - 1, diffResult)); + resultLinesAdded += diffResult; + } + } + } + } + + return new MergeEditorViewZones(input1ViewZones, input2ViewZones, baseViewZones, resultViewZones); + } +} + +interface LineAlignment { + baseLine: number; + input1Line?: number; + input2Line?: number; + resultLine?: number; +} + +export class MergeEditorViewZones { + constructor( + public readonly input1ViewZones: readonly MergeEditorViewZone[], + public readonly input2ViewZones: readonly MergeEditorViewZone[], + public readonly baseViewZones: readonly MergeEditorViewZone[], + public readonly resultViewZones: readonly MergeEditorViewZone[], + ) { } +} + +/** + * This is an abstract class to create various editor view zones. +*/ +export abstract class MergeEditorViewZone { + abstract create(viewZoneChangeAccessor: IViewZoneChangeAccessor, viewZoneIdsToCleanUp: string[], disposableStore: DisposableStore): void; +} + +class Spacer extends MergeEditorViewZone { + constructor( + private readonly afterLineNumber: number, + private readonly heightInLines: number + ) { + super(); + } + + override create( + viewZoneChangeAccessor: IViewZoneChangeAccessor, + viewZoneIdsToCleanUp: string[], + disposableStore: DisposableStore + ): void { + viewZoneIdsToCleanUp.push( + viewZoneChangeAccessor.addZone({ + afterLineNumber: this.afterLineNumber, + heightInLines: this.heightInLines, + domNode: $('div.diagonal-fill'), + }) + ); + } +} + +class Placeholder extends MergeEditorViewZone { + constructor( + private readonly afterLineNumber: number, + private readonly heightPx: number + ) { + super(); + } + + override create( + viewZoneChangeAccessor: IViewZoneChangeAccessor, + viewZoneIdsToCleanUp: string[], + disposableStore: DisposableStore + ): void { + viewZoneIdsToCleanUp.push( + viewZoneChangeAccessor.addZone({ + afterLineNumber: this.afterLineNumber, + heightInPx: this.heightPx, + domNode: $('div.conflict-actions-placeholder'), + }) + ); + } +} + +class CommandViewZone extends MergeEditorViewZone { + constructor( + private readonly conflictActionsFactory: ConflictActionsFactory, + private readonly lineNumber: number, + private readonly items: IObservable, + ) { + super(); + } + + override create(viewZoneChangeAccessor: IViewZoneChangeAccessor, viewZoneIdsToCleanUp: string[], disposableStore: DisposableStore): void { + disposableStore.add( + this.conflictActionsFactory.createWidget( + viewZoneChangeAccessor, + this.lineNumber, + this.items, + viewZoneIdsToCleanUp, + ) + ); + } +} diff --git a/src/vs/workbench/contrib/mergeEditor/common/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/common/mergeEditor.ts index f5c4b9f7dcf..07001c6684a 100644 --- a/src/vs/workbench/contrib/mergeEditor/common/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/common/mergeEditor.ts @@ -12,6 +12,7 @@ export const ctxIsMergeEditor = new RawContextKey('isMergeEditor', fals export const ctxIsMergeResultEditor = new RawContextKey('isMergeResultEditor', false, { type: 'boolean', description: localize('isr', 'The editor is a the result editor of a merge editor.') }); export const ctxMergeEditorLayout = new RawContextKey('mergeEditorLayout', 'mixed', { type: 'string', description: localize('editorLayout', 'The layout mode of a merge editor') }); export const ctxMergeEditorShowBase = new RawContextKey('mergeEditorShowBase', false, { type: 'boolean', description: localize('showBase', 'If the merge editor shows the base version') }); +export const ctxMergeEditorShowBaseAtTop = new RawContextKey('mergeEditorShowBaseAtTop', false, { type: 'boolean', description: localize('showBaseAtTop', 'If base should be shown at the top') }); export const ctxMergeEditorShowNonConflictingChanges = new RawContextKey('mergeEditorShowNonConflictingChanges', false, { type: 'boolean', description: localize('showNonConflictingChanges', 'If the merge editor shows non-conflicting changes') }); export const ctxMergeBaseUri = new RawContextKey('mergeEditorBaseUri', '', { type: 'string', description: localize('baseUri', 'The uri of the baser of a merge editor') }); diff --git a/src/vs/workbench/contrib/mergeEditor/electron-sandbox/devCommands.ts b/src/vs/workbench/contrib/mergeEditor/electron-sandbox/devCommands.ts index 0f8549b5647..63049439462 100644 --- a/src/vs/workbench/contrib/mergeEditor/electron-sandbox/devCommands.ts +++ b/src/vs/workbench/contrib/mergeEditor/electron-sandbox/devCommands.ts @@ -9,6 +9,7 @@ import { randomPath } from 'vs/base/common/extpath'; import { URI } from 'vs/base/common/uri'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { localize } from 'vs/nls'; +import { ILocalizedString } from 'vs/platform/action/common/action'; import { Action2, IAction2Options } from 'vs/platform/actions/common/actions'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -21,11 +22,13 @@ import { MergeEditorViewModel } from 'vs/workbench/contrib/mergeEditor/browser/v import { MergeEditorContents } from 'vs/workbench/contrib/mergeEditor/common/mergeEditor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +const MERGE_EDITOR_CATEGORY: ILocalizedString = { value: localize('mergeEditor', "Merge Editor (Dev)"), original: 'Merge Editor (Dev)' }; + export class MergeEditorOpenContentsFromJSON extends Action2 { constructor() { super({ id: 'merge.dev.openContentsJson', - category: 'Merge Editor (Dev)', + category: MERGE_EDITOR_CATEGORY, title: { value: localize( 'merge.dev.openState', @@ -132,13 +135,13 @@ export class OpenSelectionInTemporaryMergeEditor extends MergeEditorAction { constructor() { super({ id: 'merge.dev.openSelectionInTemporaryMergeEditor', - category: 'Merge Editor (Dev)', + category: MERGE_EDITOR_CATEGORY, title: { value: localize( 'merge.dev.openSelectionInTemporaryMergeEditor', 'Open Selection In Temporary Merge Editor' ), - original: 'Open Selection In Temporary', + original: 'Open Selection In Temporary Merge Editor', }, icon: Codicon.layoutCentered, f1: true, diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/breakpoints/notebookBreakpoints.ts b/src/vs/workbench/contrib/notebook/browser/contrib/breakpoints/notebookBreakpoints.ts index dab66bf78fe..908b48c6295 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/breakpoints/notebookBreakpoints.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/breakpoints/notebookBreakpoints.ts @@ -129,7 +129,7 @@ class NotebookBreakpoints extends Disposable implements IWorkbenchContribution { } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(NotebookBreakpoints, 'NotebookBreakpoints', LifecyclePhase.Restored); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(NotebookBreakpoints, LifecyclePhase.Restored); class NotebookCellPausing extends Disposable implements IWorkbenchContribution { private readonly _pausedCells = new Set(); @@ -196,4 +196,4 @@ class NotebookCellPausing extends Disposable implements IWorkbenchContribution { } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(NotebookCellPausing, 'NotebookCellPausing', LifecyclePhase.Restored); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(NotebookCellPausing, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts b/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts index a7f9f6bb958..ee2b1fdfa22 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts @@ -155,4 +155,4 @@ class BuiltinCellStatusBarProviders extends Disposable { } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(BuiltinCellStatusBarProviders, 'BuiltinCellStatusBarProviders', LifecyclePhase.Restored); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(BuiltinCellStatusBarProviders, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/notebookClipboard.ts b/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/notebookClipboard.ts index 71403cf4fde..b555b4709ba 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/notebookClipboard.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/notebookClipboard.ts @@ -26,7 +26,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { RedoCommand, UndoCommand } from 'vs/editor/browser/editorExtensions'; import { IWebview } from 'vs/workbench/contrib/webview/browser/webview'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { IOutputService } from 'vs/workbench/services/output/common/output'; import { rendererLogChannelId } from 'vs/workbench/contrib/logs/common/logConstants'; import { ILogService } from 'vs/platform/log/common/log'; @@ -400,7 +400,7 @@ export class NotebookClipboardContribution extends Disposable { } const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchContributionsRegistry.registerWorkbenchContribution(NotebookClipboardContribution, 'NotebookClipboardContribution', LifecyclePhase.Ready); +workbenchContributionsRegistry.registerWorkbenchContribution(NotebookClipboardContribution, LifecyclePhase.Ready); const COPY_CELL_COMMAND_ID = 'notebook.cell.copy'; const CUT_CELL_COMMAND_ID = 'notebook.cell.cut'; @@ -550,7 +550,7 @@ registerAction2(class extends Action2 { super({ id: 'workbench.action.toggleNotebookClipboardLog', title: { value: localize('toggleNotebookClipboardLog', "Toggle Notebook Clipboard Troubleshooting"), original: 'Toggle Notebook Clipboard Troubleshooting' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true }); } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts index ad13e9be7a0..6e19f81fde4 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts @@ -31,7 +31,7 @@ import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/note import { selectKernelIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_KERNEL_COUNT, NOTEBOOK_KERNEL_SOURCE_COUNT, NOTEBOOK_MISSING_KERNEL_EXTENSION } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; +import { NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_KERNEL_COUNT } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; import { INotebookKernel, INotebookKernelService, ISourceAction } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -52,17 +52,13 @@ registerAction2(class extends Action2 { id: MenuId.EditorTitle, when: ContextKeyExpr.and( NOTEBOOK_IS_ACTIVE_EDITOR, - ContextKeyExpr.or(NOTEBOOK_KERNEL_COUNT.notEqualsTo(0), NOTEBOOK_KERNEL_SOURCE_COUNT.notEqualsTo(0), NOTEBOOK_MISSING_KERNEL_EXTENSION), ContextKeyExpr.notEquals('config.notebook.globalToolbar', true) ), group: 'navigation', order: -10 }, { id: MenuId.NotebookToolbar, - when: ContextKeyExpr.and( - ContextKeyExpr.or(NOTEBOOK_KERNEL_COUNT.notEqualsTo(0), NOTEBOOK_KERNEL_SOURCE_COUNT.notEqualsTo(0), NOTEBOOK_MISSING_KERNEL_EXTENSION), - ContextKeyExpr.equals('config.notebook.globalToolbar', true) - ), + when: ContextKeyExpr.equals('config.notebook.globalToolbar', true), group: 'status', order: -10 }, { @@ -213,7 +209,7 @@ registerAction2(class extends Action2 { }); } - const sourceActions = notebookKernelService.getSourceActions(); + const sourceActions = notebookKernelService.getSourceActions(notebook, editor.scopedContextKeyService); if (sourceActions.length) { quickPickItems.push({ type: 'separator', @@ -517,7 +513,7 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution { } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(KernelStatus, 'KernelStatus', LifecyclePhase.Restored); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(KernelStatus, LifecyclePhase.Restored); export class ActiveCellStatus extends Disposable implements IWorkbenchContribution { @@ -593,4 +589,4 @@ export class ActiveCellStatus extends Disposable implements IWorkbenchContributi } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ActiveCellStatus, 'ActiveCellStatus', LifecyclePhase.Restored); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ActiveCellStatus, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/execute/executionEditorProgress.ts b/src/vs/workbench/contrib/notebook/browser/contrib/execute/executionEditorProgress.ts index 2aef79dc87b..f60773f10f0 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/execute/executionEditorProgress.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/execute/executionEditorProgress.ts @@ -38,7 +38,7 @@ export class ExecutionEditorProgressController extends Disposable implements INo return; } - const executing = this._notebookExecutionStateService.getCellExecutionStatesForNotebook(this._notebookEditor.textModel?.uri) + const executing = this._notebookExecutionStateService.getCellExecutionsForNotebook(this._notebookEditor.textModel?.uri) .filter(exe => exe.state === NotebookCellExecutionState.Executing); const executionIsVisible = (exe: INotebookCellExecution) => { for (const range of this._notebookEditor.visibleRanges) { diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/gettingStarted/notebookGettingStarted.ts b/src/vs/workbench/contrib/notebook/browser/contrib/gettingStarted/notebookGettingStarted.ts index 9bcf8daff2e..7c37a41ac75 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/gettingStarted/notebookGettingStarted.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/gettingStarted/notebookGettingStarted.ts @@ -12,7 +12,7 @@ import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/commo import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { Registry } from 'vs/platform/registry/common/platform'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { Memento } from 'vs/workbench/common/memento'; import { HAS_OPENED_NOTEBOOK } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; @@ -75,7 +75,7 @@ export class NotebookGettingStarted extends Disposable implements IWorkbenchCont } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(NotebookGettingStarted, 'NotebookGettingStarted', LifecyclePhase.Restored); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(NotebookGettingStarted, LifecyclePhase.Restored); registerAction2(class NotebookClearNotebookLayoutAction extends Action2 { constructor() { @@ -87,7 +87,7 @@ registerAction2(class NotebookClearNotebookLayoutAction extends Action2 { }, f1: true, precondition: ContextKeyExpr.equals(`config.${NotebookSetting.openGettingStarted}`, true), - category: CATEGORIES.Developer, + category: Categories.Developer, }); } run(accessor: ServicesAccessor): void { diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/marker/markerProvider.ts b/src/vs/workbench/contrib/notebook/browser/contrib/marker/markerProvider.ts index f7c3f252abd..13315c8ab5d 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/marker/markerProvider.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/marker/markerProvider.ts @@ -46,4 +46,4 @@ class MarkerListProvider implements IMarkerListProvider { Registry .as(WorkbenchExtensions.Workbench) - .registerWorkbenchContribution(MarkerListProvider, 'MarkerListProvider', LifecyclePhase.Ready); + .registerWorkbenchContribution(MarkerListProvider, LifecyclePhase.Ready); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline.ts b/src/vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline.ts index 7c189a4b3dc..24b51153c5b 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline.ts @@ -8,7 +8,7 @@ import { Codicon } from 'vs/base/common/codicons'; import { Emitter, Event } from 'vs/base/common/event'; import { combinedDisposable, IDisposable, Disposable, DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; -import { CellRevealType, IActiveNotebookEditor, ICellViewModel, INotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellRevealType, IActiveNotebookEditor, ICellViewModel, INotebookEditorOptions, INotebookViewCellsUpdateEvent } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEditor'; import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IOutline, IOutlineComparator, IOutlineCreator, IOutlineListConfig, IOutlineService, IQuickPickDataSource, IQuickPickOutlineElement, OutlineChangeEvent, OutlineConfigKeys, OutlineTarget } from 'vs/workbench/services/outline/browser/outline'; @@ -320,8 +320,16 @@ export class NotebookCellOutline extends Disposable implements IOutline this._recomputeActive()), - notebookEditor.onDidChangeViewCells(() => this._recomputeState()) + Event.debounce( + notebookEditor.onDidChangeSelection, + (last, _current) => last, + 200 + )(this._recomputeActive, this), + Event.debounce( + notebookEditor.onDidChangeViewCells, + (last, _current) => last ?? _current, + 200 + )(this._recomputeState, this) ); } }; @@ -648,7 +656,7 @@ class NotebookOutlineCreator implements IOutlineCreator(WorkbenchExtensions.Workbench).registerWorkbenchContribution(NotebookOutlineCreator, 'NotebookOutlineCreator', LifecyclePhase.Eventually); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(NotebookOutlineCreator, LifecyclePhase.Eventually); Registry.as(ConfigurationExtensions.Configuration).registerConfiguration({ diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/profile/notebookProfile.ts b/src/vs/workbench/contrib/notebook/browser/contrib/profile/notebookProfile.ts index 5848a745b90..42089a40651 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/profile/notebookProfile.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/profile/notebookProfile.ts @@ -125,5 +125,5 @@ export class NotebookProfileContribution extends Disposable { } const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchContributionsRegistry.registerWorkbenchContribution(NotebookProfileContribution, 'NotebookProfileContribution', LifecyclePhase.Ready); +workbenchContributionsRegistry.registerWorkbenchContribution(NotebookProfileContribution, LifecyclePhase.Ready); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/troubleshoot/layout.ts b/src/vs/workbench/contrib/notebook/browser/contrib/troubleshoot/layout.ts index 3644400d74b..ce2b9849c2a 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/troubleshoot/layout.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/troubleshoot/layout.ts @@ -7,7 +7,7 @@ import { Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/commo import { localize } from 'vs/nls'; import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { getNotebookEditorFromEditorPane, ICellViewModel, ICommonCellViewModelLayoutChangeInfo, INotebookDeltaCellStatusBarItems, INotebookEditor, INotebookEditorContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { registerNotebookContribution } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; @@ -126,7 +126,7 @@ registerAction2(class extends Action2 { value: localize('workbench.notebook.toggleLayoutTroubleshoot', "Toggle Layout Troubleshoot"), original: 'Toggle Notebook Layout Troubleshoot' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true }); } @@ -152,7 +152,7 @@ registerAction2(class extends Action2 { value: localize('workbench.notebook.inspectLayout', "Inspect Notebook Layout"), original: 'Inspect Notebook Layout' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true }); } @@ -180,7 +180,7 @@ registerAction2(class extends Action2 { value: localize('workbench.notebook.clearNotebookEdtitorTypeCache', "Clear Notebook Editor Type Cache"), original: 'Clear Notebook Editor Cache' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true }); } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/undoRedo/notebookUndoRedo.ts b/src/vs/workbench/contrib/notebook/browser/contrib/undoRedo/notebookUndoRedo.ts index ee32a2865c5..22678c6af01 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/undoRedo/notebookUndoRedo.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/undoRedo/notebookUndoRedo.ts @@ -65,4 +65,4 @@ class NotebookUndoRedoContribution extends Disposable { } const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchContributionsRegistry.registerWorkbenchContribution(NotebookUndoRedoContribution, 'NotebookUndoRedoContribution', LifecyclePhase.Ready); +workbenchContributionsRegistry.registerWorkbenchContribution(NotebookUndoRedoContribution, LifecyclePhase.Ready); diff --git a/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts index eefafb7edab..9ba98a19c19 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts @@ -618,7 +618,7 @@ registerAction2(class RevealRunningCellAction extends NotebookAction { async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise { const notebookExecutionStateService = accessor.get(INotebookExecutionStateService); const notebook = context.notebookEditor.textModel.uri; - const executingCells = notebookExecutionStateService.getCellExecutionStatesForNotebook(notebook); + const executingCells = notebookExecutionStateService.getCellExecutionsForNotebook(notebook); if (executingCells[0]) { const cell = context.notebookEditor.getCellByHandle(executingCells[0].cellHandle); if (cell) { diff --git a/src/vs/workbench/contrib/notebook/browser/controller/layoutActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/layoutActions.ts index 7f6ac0a645b..8a168cc34e1 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/layoutActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/layoutActions.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Codicon } from 'vs/base/common/codicons'; +import { URI, UriComponents } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { Action2, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -12,9 +13,12 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { NOTEBOOK_ACTIONS_CATEGORY } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; -import { NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_IS_ACTIVE_EDITOR } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; +import { getNotebookEditorFromEditorPane } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; import { NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_IS_ACTIVE_EDITOR } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; registerAction2(class NotebookConfigureLayoutAction extends Action2 { @@ -133,7 +137,7 @@ registerAction2(class ToggleLineNumberFromEditorTitle extends Action2 { f1: true, toggled: { condition: ContextKeyExpr.notEquals('config.notebook.lineNumbers', 'off'), - title: { value: localize('notebook.showLineNumbers', "Show Notebook Line Numbers"), original: 'Show Notebook Line Numbers' }, + title: localize('notebook.showLineNumbers', "Notebook Line Numbers"), } }); } @@ -218,3 +222,38 @@ registerAction2(class SaveMimeTypeDisplayOrder extends Action2 { qp.show(); } }); + +registerAction2(class NotebookWebviewResetAction extends Action2 { + constructor() { + super({ + id: 'workbench.notebook.layout.webview.reset', + title: { + value: localize('workbench.notebook.layout.webview.reset.label', "Reset Notebook Webview"), + original: 'Reset Notebook Webview' + }, + f1: false, + category: NOTEBOOK_ACTIONS_CATEGORY + }); + } + run(accessor: ServicesAccessor, args?: UriComponents): void { + const editorService = accessor.get(IEditorService); + + if (args) { + const uri = URI.revive(args); + const notebookEditorService = accessor.get(INotebookEditorService); + const widgets = notebookEditorService.listNotebookEditors().filter(widget => widget.hasModel() && widget.textModel.uri.toString() === uri.toString()); + for (const widget of widgets) { + if (widget.hasModel()) { + widget.getInnerWebview()?.reload(); + } + } + } else { + const editor = getNotebookEditorFromEditorPane(editorService.activeEditorPane); + if (!editor) { + return; + } + + editor.getInnerWebview()?.reload(); + } + } +}); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts index 7d8ac846bcf..e3a628d0d87 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts @@ -1253,6 +1253,7 @@ export class InsertElement extends SingleSideDiffElement { export class ModifiedElement extends AbstractElementRenderer { private _editor?: DiffEditorWidget; + private _editorViewStateChanged: boolean; private _editorContainer!: HTMLElement; private _inputToolbarContainer!: HTMLElement; protected _toolbar!: ToolBar; @@ -1279,6 +1280,7 @@ export class ModifiedElement extends AbstractElementRenderer { super(notebookEditor, cell, templateData, 'full', instantiationService, languageService, modelService, textModelService, contextMenuService, keybindingService, notificationService, menuService, contextKeyService, configurationService); this.cell = cell; this.templateData = templateData; + this._editorViewStateChanged = false; } init() { } @@ -1598,7 +1600,26 @@ export class ModifiedElement extends AbstractElementRenderer { modified: modifiedTextModel }); - this._editor!.restoreViewState(this.cell.getSourceEditorViewState() as editorCommon.IDiffEditorViewState); + const handleViewStateChange = () => { + this._editorViewStateChanged = true; + }; + + const handleScrollChange = (e: editorCommon.IScrollEvent) => { + if (e.scrollTopChanged || e.scrollLeftChanged) { + this._editorViewStateChanged = true; + } + }; + + this._register(this._editor!.getOriginalEditor().onDidChangeCursorSelection(handleViewStateChange)); + this._register(this._editor!.getOriginalEditor().onDidScrollChange(handleScrollChange)); + this._register(this._editor!.getModifiedEditor().onDidChangeCursorSelection(handleViewStateChange)); + this._register(this._editor!.getModifiedEditor().onDidScrollChange(handleScrollChange)); + + const editorViewState = this.cell.getSourceEditorViewState() as editorCommon.IDiffEditorViewState | null; + if (editorViewState) { + console.log('restore view state', this.cell.modified.handle, editorViewState); + this._editor!.restoreViewState(editorViewState); + } const contentHeight = this._editor!.getContentHeight(); this.cell.editorHeight = contentHeight; @@ -1645,7 +1666,7 @@ export class ModifiedElement extends AbstractElementRenderer { } override dispose() { - if (this._editor) { + if (this._editor && this._editorViewStateChanged) { this.cell.saveSpirceEditorViewState(this._editor.saveViewState()); } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css index 801d217ba08..bb6786c48c2 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css @@ -16,6 +16,10 @@ width: 50%; } */ +.notebook-text-diff-editor { + position: relative; +} + .notebook-text-diff-editor .cell-body { display: flex; flex-direction: row; @@ -61,7 +65,7 @@ /* overflow: hidden; */ } -.notebook-text-diff-editor > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row { +.notebook-text-diff-editor > .notebook-diff-list-view > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row { cursor: default; } @@ -142,13 +146,13 @@ overflow: hidden; } -.monaco-workbench .notebook-text-diff-editor > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row { +.monaco-workbench .notebook-text-diff-editor > .notebook-diff-list-view > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row { overflow: visible !important; } -.monaco-workbench .notebook-text-diff-editor > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row, -.monaco-workbench .notebook-text-diff-editor > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover, -.monaco-workbench .notebook-text-diff-editor > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused { +.monaco-workbench .notebook-text-diff-editor > .notebook-diff-list-view > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row, +.monaco-workbench .notebook-text-diff-editor > .notebook-diff-list-view > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover, +.monaco-workbench .notebook-text-diff-editor > .notebook-diff-list-view > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused { outline: none !important; background-color: transparent !important; } @@ -287,3 +291,18 @@ left: 4px !important; width: 15px !important; } + +.monaco-workbench .notebook-text-diff-editor > .monaco-list > .monaco-scrollable-element > .scrollbar.visible { + z-index: var(--z-index-notebook-scrollbar); + cursor: default; +} + +.notebook-text-diff-editor .notebook-overview-ruler-container { + position: absolute; + top: 0; + right: 0; +} + +.notebook-text-diff-editor .notebook-overview-ruler-container .diffViewport { + z-index: var(--notebook-diff-view-viewport-slider); +} diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts index ebb303246b8..e881fda60b7 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts @@ -30,9 +30,12 @@ export interface INotebookTextDiffEditor { notebookOptions: NotebookOptions; readonly textModel?: NotebookTextModel; onMouseUp: Event<{ readonly event: MouseEvent; readonly target: DiffElementViewModelBase }>; + onDidScroll: Event; onDidDynamicOutputRendered: Event<{ cell: IGenericCellViewModel; output: ICellOutputViewModel }>; getOverflowContainerDomNode(): HTMLElement; getLayoutInfo(): NotebookLayoutInfo; + getScrollTop(): number; + getScrollHeight(): number; layoutNotebookCell(cell: DiffElementViewModelBase, height: number): void; createOutput(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: IDiffNestedCellViewModel, output: IInsetRenderOutput, getOffset: () => number, diffSide: DiffSide): void; showInset(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: IDiffNestedCellViewModel, displayOutput: ICellOutputViewModel, diffSide: DiffSide): void; @@ -42,6 +45,7 @@ export interface INotebookTextDiffEditor { * Trigger the editor to scroll from scroll event programmatically */ triggerScroll(event: IMouseWheelEvent): void; + delegateVerticalScrollbarPointerDown(browserEvent: PointerEvent): void; getCellByInfo(cellInfo: ICommonCellInfo): IGenericCellViewModel; focusNotebookCell(cell: IGenericCellViewModel, focus: 'editor' | 'container' | 'output'): Promise; focusNextNotebookCell(cell: IGenericCellViewModel, focus: 'editor' | 'container' | 'output'): Promise; @@ -71,7 +75,6 @@ export interface CellDiffSingleSideRenderTemplate extends CellDiffCommonRenderTe readonly metadataInfoContainer: HTMLElement; readonly outputHeaderContainer: HTMLElement; readonly outputInfoContainer: HTMLElement; - } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffOverviewRuler.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffOverviewRuler.ts new file mode 100644 index 00000000000..7ccc71587d3 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffOverviewRuler.ts @@ -0,0 +1,245 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { createFastDomNode, FastDomNode } from 'vs/base/browser/fastDomNode'; +import { Color } from 'vs/base/common/color'; +import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { defaultInsertColor, defaultRemoveColor, diffInserted, diffOverviewRulerInserted, diffOverviewRulerRemoved, diffRemoved, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground } from 'vs/platform/theme/common/colorRegistry'; +import { IColorTheme, IThemeService, registerThemingParticipant, Themable } from 'vs/platform/theme/common/themeService'; +import { DiffElementViewModelBase } from 'vs/workbench/contrib/notebook/browser/diff/diffElementViewModel'; +import { NotebookDiffEditorEventDispatcher } from 'vs/workbench/contrib/notebook/browser/diff/eventDispatcher'; +import { INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser'; + +export class NotebookDiffOverviewRuler extends Themable { + private readonly _domNode: FastDomNode; + private readonly _overviewViewportDomElement: FastDomNode; + + private _diffElementViewModels: DiffElementViewModelBase[] = []; + private _lanes = 2; + + private _insertColor: Color | null; + private _insertColorHex: string | null; + private _removeColor: Color | null; + private _removeColorHex: string | null; + + private _disposables: DisposableStore; + private _renderAnimationFrame: IDisposable | null; + + constructor(readonly notebookEditor: INotebookTextDiffEditor, readonly width: number, container: HTMLElement, @IThemeService themeService: IThemeService) { + super(themeService); + this._insertColor = null; + this._removeColor = null; + this._insertColorHex = null; + this._removeColorHex = null; + this._disposables = this._register(new DisposableStore()); + this._renderAnimationFrame = null; + this._domNode = createFastDomNode(document.createElement('canvas')); + this._domNode.setPosition('relative'); + this._domNode.setLayerHinting(true); + this._domNode.setContain('strict'); + + container.appendChild(this._domNode.domNode); + + this._overviewViewportDomElement = createFastDomNode(document.createElement('div')); + this._overviewViewportDomElement.setClassName('diffViewport'); + this._overviewViewportDomElement.setPosition('absolute'); + this._overviewViewportDomElement.setWidth(width); + container.appendChild(this._overviewViewportDomElement.domNode); + + this._register(browser.PixelRatio.onDidChange(() => { + this._scheduleRender(); + })); + + this._register(this.themeService.onDidColorThemeChange(e => { + const colorChanged = this.applyColors(e); + if (colorChanged) { + this._scheduleRender(); + } + })); + this.applyColors(this.themeService.getColorTheme()); + + this._register(this.notebookEditor.onDidScroll(() => { + this._renderOverviewViewport(); + })); + + this._register(DOM.addStandardDisposableListener(container, DOM.EventType.POINTER_DOWN, (e) => { + this.notebookEditor.delegateVerticalScrollbarPointerDown(e); + })); + } + + private applyColors(theme: IColorTheme): boolean { + const newInsertColor = theme.getColor(diffOverviewRulerInserted) || (theme.getColor(diffInserted) || defaultInsertColor).transparent(2); + const newRemoveColor = theme.getColor(diffOverviewRulerRemoved) || (theme.getColor(diffRemoved) || defaultRemoveColor).transparent(2); + const hasChanges = !newInsertColor.equals(this._insertColor) || !newRemoveColor.equals(this._removeColor); + this._insertColor = newInsertColor; + this._removeColor = newRemoveColor; + if (this._insertColor) { + this._insertColorHex = Color.Format.CSS.formatHexA(this._insertColor); + } + + if (this._removeColor) { + this._removeColorHex = Color.Format.CSS.formatHexA(this._removeColor); + } + + return hasChanges; + } + + layout() { + this._layoutNow(); + } + + updateViewModels(elements: DiffElementViewModelBase[], eventDispatcher: NotebookDiffEditorEventDispatcher | undefined) { + this._disposables.clear(); + + this._diffElementViewModels = elements; + + if (eventDispatcher) { + this._disposables.add(eventDispatcher.onDidChangeLayout(() => { + this._scheduleRender(); + })); + + this._disposables.add(eventDispatcher.onDidChangeCellLayout(() => { + this._scheduleRender(); + })); + } + + this._scheduleRender(); + } + + private _scheduleRender(): void { + if (this._renderAnimationFrame === null) { + this._renderAnimationFrame = DOM.runAtThisOrScheduleAtNextAnimationFrame(this._onRenderScheduled.bind(this), 100); + } + } + + private _onRenderScheduled(): void { + this._renderAnimationFrame = null; + this._layoutNow(); + } + + private _layoutNow() { + const layoutInfo = this.notebookEditor.getLayoutInfo(); + const height = layoutInfo.height; + const scrollHeight = layoutInfo.scrollHeight; + const ratio = browser.PixelRatio.value; + this._domNode.setWidth(this.width); + this._domNode.setHeight(height); + this._domNode.domNode.width = this.width * ratio; + this._domNode.domNode.height = height * ratio; + const ctx = this._domNode.domNode.getContext('2d')!; + ctx.clearRect(0, 0, this.width * ratio, height * ratio); + this._renderCanvas(ctx, this.width * ratio, height * ratio, scrollHeight * ratio, ratio); + this._renderOverviewViewport(); + } + + private _renderOverviewViewport(): void { + const layout = this._computeOverviewViewport(); + if (!layout) { + this._overviewViewportDomElement.setTop(0); + this._overviewViewportDomElement.setHeight(0); + } else { + this._overviewViewportDomElement.setTop(layout.top); + this._overviewViewportDomElement.setHeight(layout.height); + } + } + + private _computeOverviewViewport(): { height: number; top: number } | null { + const layoutInfo = this.notebookEditor.getLayoutInfo(); + if (!layoutInfo) { + return null; + } + + const scrollTop = this.notebookEditor.getScrollTop(); + const scrollHeight = this.notebookEditor.getScrollHeight(); + + const computedAvailableSize = Math.max(0, layoutInfo.height); + const computedRepresentableSize = Math.max(0, computedAvailableSize - 2 * 0); + const computedRatio = scrollHeight > 0 ? (computedRepresentableSize / scrollHeight) : 0; + + const computedSliderSize = Math.max(0, Math.floor(layoutInfo.height * computedRatio)); + const computedSliderPosition = Math.floor(scrollTop * computedRatio); + + return { + height: computedSliderSize, + top: computedSliderPosition + }; + } + + private _renderCanvas(ctx: CanvasRenderingContext2D, width: number, height: number, scrollHeight: number, ratio: number) { + if (!this._insertColorHex || !this._removeColorHex) { + // no op when colors are not yet known + return; + } + + const laneWidth = width / this._lanes; + let currentFrom = 0; + for (let i = 0; i < this._diffElementViewModels.length; i++) { + const element = this._diffElementViewModels[i]; + + const cellHeight = (element.layoutInfo.totalHeight / scrollHeight) * ratio * height; + + switch (element.type) { + case 'insert': + ctx.fillStyle = this._insertColorHex; + ctx.fillRect(laneWidth, currentFrom, laneWidth, cellHeight); + break; + case 'delete': + ctx.fillStyle = this._removeColorHex; + ctx.fillRect(0, currentFrom, laneWidth, cellHeight); + break; + case 'unchanged': + break; + case 'modified': + ctx.fillStyle = this._removeColorHex; + ctx.fillRect(0, currentFrom, laneWidth, cellHeight); + ctx.fillStyle = this._insertColorHex; + ctx.fillRect(laneWidth, currentFrom, laneWidth, cellHeight); + break; + } + + currentFrom += cellHeight; + } + } + + override dispose() { + if (this._renderAnimationFrame !== null) { + this._renderAnimationFrame.dispose(); + this._renderAnimationFrame = null; + } + + super.dispose(); + } +} + +registerThemingParticipant((theme, collector) => { + const scrollbarSliderBackgroundColor = theme.getColor(scrollbarSliderBackground); + if (scrollbarSliderBackgroundColor) { + collector.addRule(` + .notebook-text-diff-editor .diffViewport { + background: ${scrollbarSliderBackgroundColor}; + } + `); + } + + const scrollbarSliderHoverBackgroundColor = theme.getColor(scrollbarSliderHoverBackground); + if (scrollbarSliderHoverBackgroundColor) { + collector.addRule(` + .notebook-text-diff-editor .diffViewport:hover { + background: ${scrollbarSliderHoverBackgroundColor}; + } + `); + } + + const scrollbarSliderActiveBackgroundColor = theme.getColor(scrollbarSliderActiveBackground); + if (scrollbarSliderActiveBackgroundColor) { + collector.addRule(` + .notebook-text-diff-editor .diffViewport:active { + background: ${scrollbarSliderActiveBackgroundColor}; + } + `); + } +}); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index 53f2626ba24..751d6fb5d7c 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -44,6 +44,8 @@ import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/co import { NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { cellIndexesToRanges, cellRangesToIndexes } from 'vs/workbench/contrib/notebook/common/notebookRange'; +import { NotebookDiffOverviewRuler } from 'vs/workbench/contrib/notebook/browser/diff/notebookDiffOverviewRuler'; +import { registerZIndex, ZIndex } from 'vs/platform/layout/browser/zIndexRegistry'; const $ = DOM.$; @@ -82,11 +84,15 @@ class NotebookDiffEditorSelection implements IEditorPaneSelection { } export class NotebookTextDiffEditor extends EditorPane implements INotebookTextDiffEditor, INotebookDelegateForWebview, IEditorPaneWithSelection { + public static readonly ENTIRE_DIFF_OVERVIEW_WIDTH = 30; creationOptions: INotebookEditorCreationOptions = getDefaultNotebookCreationOptions(); static readonly ID: string = NOTEBOOK_DIFF_EDITOR_ID; private _rootElement!: HTMLElement; + private _listViewContainer!: HTMLElement; private _overflowContainer!: HTMLElement; + private _overviewRulerContainer!: HTMLElement; + private _overviewRuler!: NotebookDiffOverviewRuler; private _dimension: DOM.Dimension | null = null; private _diffElementViewModels: DiffElementViewModelBase[] = []; private _list!: NotebookTextDiffList; @@ -97,6 +103,8 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD private readonly _onMouseUp = this._register(new Emitter<{ readonly event: MouseEvent; readonly target: DiffElementViewModelBase }>()); public readonly onMouseUp = this._onMouseUp.event; + private readonly _onDidScroll = this._register(new Emitter()); + readonly onDidScroll: Event = this._onDidScroll.event; private _eventDispatcher: NotebookDiffEditorEventDispatcher | undefined; protected _scopeContextKeyService!: IContextKeyService; private _model: INotebookDiffEditorModel | null = null; @@ -149,6 +157,10 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD this._revealFirst = true; } + private isOverviewRulerEnabled(): boolean { + return this.configurationService.getValue('notebook.experimental.diffOverviewRuler.enabled') ?? false; + } + getSelection(): IEditorPaneSelection | undefined { const selections = this._list.getFocus(); return new NotebookDiffEditorSelection(selections); @@ -166,6 +178,18 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD // throw new Error('Method not implemented.'); } + getScrollTop() { + return this._list?.scrollTop ?? 0; + } + + getScrollHeight() { + return this._list?.scrollHeight ?? 0; + } + + delegateVerticalScrollbarPointerDown(browserEvent: PointerEvent) { + this._list?.delegateVerticalScrollbarPointerDown(browserEvent); + } + updateOutputHeight(cellInfo: IDiffCellInfo, output: ICellOutputViewModel, outputHeight: number, isInit: boolean): void { const diffElement = cellInfo.diffElement; const cell = this.getCellByInfo(cellInfo); @@ -217,10 +241,12 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD this.instantiationService.createInstance(CellDiffSideBySideRenderer, this), ]; + this._listViewContainer = DOM.append(this._rootElement, DOM.$('.notebook-diff-list-view')); + this._list = this.instantiationService.createInstance( NotebookTextDiffList, 'NotebookTextDiff', - this._rootElement, + this._listViewContainer, this.instantiationService.createInstance(NotebookCellTextDiffListDelegate), renderers, this.contextKeyService, @@ -274,8 +300,17 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD } })); + this._register(this._list.onDidScroll(() => { + this._onDidScroll.fire(); + })); + this._register(this._list.onDidChangeFocus(() => this._onDidChangeSelection.fire({ reason: EditorPaneSelectionChangeReason.USER }))); + this._overviewRulerContainer = document.createElement('div'); + this._overviewRulerContainer.classList.add('notebook-overview-ruler-container'); + this._rootElement.appendChild(this._overviewRulerContainer); + this._registerOverviewRuler(); + // transparent cover this._webviewTransparentCover = DOM.append(this._list.rowsContainer, $('.webview-cover')); this._webviewTransparentCover.style.display = 'none'; @@ -296,8 +331,10 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD this._register(this._list.onDidScroll(e => { this._webviewTransparentCover!.style.top = `${e.scrollTop}px`; })); + } - + private _registerOverviewRuler() { + this._overviewRuler = this._register(this.instantiationService.createInstance(NotebookDiffOverviewRuler, this, NotebookTextDiffEditor.ENTIRE_DIFF_OVERVIEW_WIDTH, this._overviewRulerContainer!)); } private _updateOutputsOffsetsInWebview(scrollTop: number, scrollHeight: number, activeWebview: BackLayerWebView, getActiveNestedCell: (diffElement: DiffElementViewModelBase) => DiffNestedCellViewModel | undefined, diffSide: DiffSide) { @@ -373,11 +410,11 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD } })); - await this._createOriginalWebview(generateUuid(), this._model.original.resource); + await this._createOriginalWebview(generateUuid(), this._model.original.viewType, this._model.original.resource); if (this._originalWebview) { this._modifiedResourceDisposableStore.add(this._originalWebview); } - await this._createModifiedWebview(generateUuid(), this._model.modified.resource); + await this._createModifiedWebview(generateUuid(), this._model.modified.viewType, this._model.modified.resource); if (this._modifiedWebview) { this._modifiedResourceDisposableStore.add(this._modifiedWebview); } @@ -429,10 +466,10 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD })); } - private async _createModifiedWebview(id: string, resource: URI): Promise { + private async _createModifiedWebview(id: string, viewType: string, resource: URI): Promise { this._modifiedWebview?.dispose(); - this._modifiedWebview = this.instantiationService.createInstance(BackLayerWebView, this, id, resource, { + this._modifiedWebview = this.instantiationService.createInstance(BackLayerWebView, this, id, viewType, resource, { ...this._notebookOptions.computeDiffWebviewOptions(), fontFamily: this._generateFontFamily() }, undefined) as BackLayerWebView; @@ -446,10 +483,10 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD return this._fontInfo?.fontFamily ?? `"SF Mono", Monaco, Menlo, Consolas, "Ubuntu Mono", "Liberation Mono", "DejaVu Sans Mono", "Courier New", monospace`; } - private async _createOriginalWebview(id: string, resource: URI): Promise { + private async _createOriginalWebview(id: string, viewType: string, resource: URI): Promise { this._originalWebview?.dispose(); - this._originalWebview = this.instantiationService.createInstance(BackLayerWebView, this, id, resource, { + this._originalWebview = this.instantiationService.createInstance(BackLayerWebView, this, id, viewType, resource, { ...this._notebookOptions.computeDiffWebviewOptions(), fontFamily: this._generateFontFamily() }, undefined) as BackLayerWebView; @@ -526,6 +563,9 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD private _setViewModel(viewModels: DiffElementViewModelBase[]) { this._diffElementViewModels = viewModels; this._list.splice(0, this._list.length, this._diffElementViewModels); + if (this.isOverviewRulerEnabled()) { + this._overviewRuler.updateViewModels(this._diffElementViewModels, this._eventDispatcher); + } } /** @@ -942,12 +982,14 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD layout(dimension: DOM.Dimension): void { this._rootElement.classList.toggle('mid-width', dimension.width < 1000 && dimension.width >= 600); this._rootElement.classList.toggle('narrow-width', dimension.width < 600); - this._dimension = dimension; - this._rootElement.style.height = `${dimension.height}px`; + const overviewRulerEnabled = this.isOverviewRulerEnabled(); + this._dimension = dimension.with(dimension.width - (overviewRulerEnabled ? NotebookTextDiffEditor.ENTIRE_DIFF_OVERVIEW_WIDTH : 0)); + + this._listViewContainer.style.height = `${dimension.height}px`; + this._listViewContainer.style.width = `${this._dimension.width}px`; this._list?.layout(this._dimension.height, this._dimension.width); - if (this._modifiedWebview) { this._modifiedWebview.element.style.width = `calc(50% - 16px)`; this._modifiedWebview.element.style.left = `calc(50%)`; @@ -959,8 +1001,12 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD } if (this._webviewTransparentCover) { - this._webviewTransparentCover.style.height = `${dimension.height}px`; - this._webviewTransparentCover.style.width = `${dimension.width}px`; + this._webviewTransparentCover.style.height = `${this._dimension.height}px`; + this._webviewTransparentCover.style.width = `${this._dimension.width}px`; + } + + if (overviewRulerEnabled) { + this._overviewRuler.layout(); } this._eventDispatcher?.emit([new NotebookDiffLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]); @@ -974,6 +1020,8 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD } } +registerZIndex(ZIndex.Base, 10, 'notebook-diff-view-viewport-slider'); + registerThemingParticipant((theme, collector) => { const cellBorderColor = theme.getColor(notebookCellBorder); if (cellBorderColor) { diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts index 0ec024ae116..fe5b564916d 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts @@ -147,6 +147,7 @@ export class CellDiffSingleSideRenderer implements IListRenderer(WorkbenchExtensions.Workbench); -workbenchContributionsRegistry.registerWorkbenchContribution(NotebookContribution, 'NotebookContribution', LifecyclePhase.Starting); -workbenchContributionsRegistry.registerWorkbenchContribution(CellContentProvider, 'CellContentProvider', LifecyclePhase.Starting); -workbenchContributionsRegistry.registerWorkbenchContribution(CellInfoContentProvider, 'CellInfoContentProvider', LifecyclePhase.Starting); -workbenchContributionsRegistry.registerWorkbenchContribution(RegisterSchemasContribution, 'RegisterSchemasContribution', LifecyclePhase.Starting); -workbenchContributionsRegistry.registerWorkbenchContribution(NotebookEditorManager, 'NotebookEditorManager', LifecyclePhase.Ready); -workbenchContributionsRegistry.registerWorkbenchContribution(NotebookLanguageSelectorScoreRefine, 'NotebookLanguageSelectorScoreRefine', LifecyclePhase.Ready); -workbenchContributionsRegistry.registerWorkbenchContribution(SimpleNotebookWorkingCopyEditorHandler, 'SimpleNotebookWorkingCopyEditorHandler', LifecyclePhase.Ready); -workbenchContributionsRegistry.registerWorkbenchContribution(ComplexNotebookWorkingCopyEditorHandler, 'ComplexNotebookWorkingCopyEditorHandler', LifecyclePhase.Ready); +workbenchContributionsRegistry.registerWorkbenchContribution(NotebookContribution, LifecyclePhase.Starting); +workbenchContributionsRegistry.registerWorkbenchContribution(CellContentProvider, LifecyclePhase.Starting); +workbenchContributionsRegistry.registerWorkbenchContribution(CellInfoContentProvider, LifecyclePhase.Starting); +workbenchContributionsRegistry.registerWorkbenchContribution(RegisterSchemasContribution, LifecyclePhase.Starting); +workbenchContributionsRegistry.registerWorkbenchContribution(NotebookEditorManager, LifecyclePhase.Ready); +workbenchContributionsRegistry.registerWorkbenchContribution(NotebookLanguageSelectorScoreRefine, LifecyclePhase.Ready); +workbenchContributionsRegistry.registerWorkbenchContribution(SimpleNotebookWorkingCopyEditorHandler, LifecyclePhase.Ready); +workbenchContributionsRegistry.registerWorkbenchContribution(ComplexNotebookWorkingCopyEditorHandler, LifecyclePhase.Ready); -registerSingleton(INotebookService, NotebookService, false); -registerSingleton(INotebookEditorWorkerService, NotebookEditorWorkerServiceImpl, false); -registerSingleton(INotebookEditorModelResolverService, NotebookModelResolverServiceImpl, true); -registerSingleton(INotebookCellStatusBarService, NotebookCellStatusBarService, true); -registerSingleton(INotebookEditorService, NotebookEditorWidgetService, true); -registerSingleton(INotebookKernelService, NotebookKernelService, true); -registerSingleton(INotebookExecutionService, NotebookExecutionService, true); -registerSingleton(INotebookExecutionStateService, NotebookExecutionStateService, true); -registerSingleton(INotebookRendererMessagingService, NotebookRendererMessagingService, true); -registerSingleton(INotebookKeymapService, NotebookKeymapService, true); +registerSingleton(INotebookService, NotebookService, InstantiationType.Delayed); +registerSingleton(INotebookEditorWorkerService, NotebookEditorWorkerServiceImpl, InstantiationType.Delayed); +registerSingleton(INotebookEditorModelResolverService, NotebookModelResolverServiceImpl, InstantiationType.Delayed); +registerSingleton(INotebookCellStatusBarService, NotebookCellStatusBarService, InstantiationType.Delayed); +registerSingleton(INotebookEditorService, NotebookEditorWidgetService, InstantiationType.Delayed); +registerSingleton(INotebookKernelService, NotebookKernelService, InstantiationType.Delayed); +registerSingleton(INotebookExecutionService, NotebookExecutionService, InstantiationType.Delayed); +registerSingleton(INotebookExecutionStateService, NotebookExecutionStateService, InstantiationType.Delayed); +registerSingleton(INotebookRendererMessagingService, NotebookRendererMessagingService, InstantiationType.Delayed); +registerSingleton(INotebookKeymapService, NotebookKeymapService, InstantiationType.Delayed); const schemas: IJSONSchemaMap = {}; function isConfigurationPropertySchema(x: IConfigurationPropertySchema | { [path: string]: IConfigurationPropertySchema }): x is IConfigurationPropertySchema { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index b95a790cc6d..95df7696197 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -25,8 +25,9 @@ import { isCompositeNotebookEditorInput } from 'vs/workbench/contrib/notebook/co import { INotebookKernel } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions'; import { cellRangesToIndexes, ICellRange, reduceCellRanges } from 'vs/workbench/contrib/notebook/common/notebookRange'; -import { IWebview } from 'vs/workbench/contrib/webview/browser/webview'; +import { IWebviewElement } from 'vs/workbench/contrib/webview/browser/webview'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; //#region Shared commands export const EXPAND_CELL_INPUT_COMMAND_ID = 'notebook.cell.expandCellInput'; @@ -428,6 +429,7 @@ export interface INotebookEditor { readonly isDisposed: boolean; readonly activeKernel: INotebookKernel | undefined; readonly scrollTop: number; + readonly scopedContextKeyService: IContextKeyService; //#endregion getLength(): number; @@ -441,7 +443,7 @@ export interface INotebookEditor { hasModel(): this is IActiveNotebookEditor; dispose(): void; getDomNode(): HTMLElement; - getInnerWebview(): IWebview | undefined; + getInnerWebview(): IWebviewElement | undefined; getSelectionViewModels(): ICellViewModel[]; /** diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 09d6ffab345..295e86ea211 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -14,7 +14,6 @@ import * as DOM from 'vs/base/browser/dom'; import { IMouseWheelEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; -import { IAction } from 'vs/base/common/actions'; import { DeferredPromise, runWhenIdle, SequencerByKey } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Color, RGBA } from 'vs/base/common/color'; @@ -33,8 +32,7 @@ import { Range } from 'vs/editor/common/core/range'; import { IEditor } from 'vs/editor/common/editorCommon'; import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestController'; import * as nls from 'vs/nls'; -import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { MenuId } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -80,18 +78,19 @@ import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { INotebookRendererMessagingService } from 'vs/workbench/contrib/notebook/common/notebookRendererMessagingService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { editorGutterModifiedBackground } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator'; -import { IWebview } from 'vs/workbench/contrib/webview/browser/webview'; +import { IWebviewElement } from 'vs/workbench/contrib/webview/browser/webview'; import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { NotebookPerfMarks } from 'vs/workbench/contrib/notebook/common/notebookPerformance'; import { BaseCellEditorOptions } from 'vs/workbench/contrib/notebook/browser/viewModel/cellEditorOptions'; import { ILogService } from 'vs/platform/log/common/log'; +import { FloatingClickMenu } from 'vs/workbench/browser/codeeditor'; const $ = DOM.$; export function getDefaultNotebookCreationOptions(): INotebookEditorCreationOptions { // We inlined the id to avoid loading comment contrib in tests - const skipContributions = ['editor.contrib.review']; + const skipContributions = ['editor.contrib.review', FloatingClickMenu.ID]; const contributions = EditorExtensionsRegistry.getEditorContributions().filter(c => skipContributions.indexOf(c.id) === -1); return { @@ -251,7 +250,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD @IContextKeyService contextKeyService: IContextKeyService, @ILayoutService private readonly layoutService: ILayoutService, @IContextMenuService private readonly contextMenuService: IContextMenuService, - @IMenuService private readonly menuService: IMenuService, @ITelemetryService private readonly telemetryService: ITelemetryService, @INotebookExecutionService private readonly notebookExecutionService: INotebookExecutionService, @INotebookExecutionStateService notebookExecutionStateService: INotebookExecutionStateService, @@ -952,13 +950,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD private showListContextMenu(e: IListContextMenuEvent) { this.contextMenuService.showContextMenu({ - getActions: () => { - const result: IAction[] = []; - const menu = this.menuService.createMenu(MenuId.NotebookCellTitle, this.scopedContextKeyService); - createAndFillInContextMenuActions(menu, undefined, result); - menu.dispose(); - return result; - }, + menuId: MenuId.NotebookCellTitle, + contextKeyService: this.scopedContextKeyService, getAnchor: () => e.anchor }); } @@ -999,7 +992,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD return this._overflowContainer; } - getInnerWebview(): IWebview | undefined { + getInnerWebview(): IWebviewElement | undefined { return this._webview?.webview; } @@ -1247,7 +1240,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD } if (!this._webview) { - this._createWebview(this.getId(), this.textModel.uri); + this._createWebview(this.getId(), this.textModel.viewType, this.textModel.uri); } this._webviewResolvePromise = (async () => { @@ -1284,7 +1277,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD return this._webviewResolvePromise; } - private async _createWebview(id: string, resource: URI): Promise { + private async _createWebview(id: string, viewType: string, resource: URI): Promise { const that = this; this._webview = this.instantiationService.createInstance(BackLayerWebView, { @@ -1305,7 +1298,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD didDropMarkupCell: that._didDropMarkupCell.bind(that), didEndDragMarkupCell: that._didEndDragMarkupCell.bind(that), didResizeOutput: that._didResizeOutput.bind(that) - }, id, resource, { + }, id, viewType, resource, { ...this._notebookOptions.computeWebviewOptions(), fontFamily: this._generateFontFamily() }, this.notebookRendererMessaging.getScoped(this._uuid)); @@ -1317,7 +1310,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD } private async _attachModel(textModel: NotebookTextModel, viewState: INotebookEditorViewState | undefined, perf?: NotebookPerfMarks) { - await this._createWebview(this.getId(), textModel.uri); + await this._createWebview(this.getId(), textModel.viewType, textModel.uri); this.viewModel = this.instantiationService.createInstance(NotebookViewModel, textModel.viewType, textModel, this._viewContext, this.getLayoutInfo(), { isReadOnly: this._readOnly }); this._viewContext.eventDispatcher.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]); @@ -2071,7 +2064,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD if (!cells) { cells = this.viewModel.viewCells; } - return this.notebookExecutionService.executeNotebookCells(this.textModel, Array.from(cells).map(c => c.model)); + return this.notebookExecutionService.executeNotebookCells(this.textModel, Array.from(cells).map(c => c.model), this.scopedContextKeyService); } //#endregion diff --git a/src/vs/workbench/contrib/notebook/browser/notebookExtensionPoint.ts b/src/vs/workbench/contrib/notebook/browser/notebookExtensionPoint.ts index 651082499a8..4b32dd1539e 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookExtensionPoint.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookExtensionPoint.ts @@ -6,7 +6,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; import * as nls from 'vs/nls'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { NotebookEditorPriority, NotebookRendererEntrypoint, RendererMessagingSpec } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookEditorPriority, ContributedNotebookRendererEntrypoint, RendererMessagingSpec } from 'vs/workbench/contrib/notebook/common/notebookCommon'; const NotebookEditorContribution = Object.freeze({ type: 'type', @@ -36,12 +36,22 @@ export interface INotebookRendererContribution { readonly [NotebookRendererContribution.id]?: string; readonly [NotebookRendererContribution.displayName]: string; readonly [NotebookRendererContribution.mimeTypes]?: readonly string[]; - readonly [NotebookRendererContribution.entrypoint]: NotebookRendererEntrypoint; + readonly [NotebookRendererContribution.entrypoint]: ContributedNotebookRendererEntrypoint; readonly [NotebookRendererContribution.hardDependencies]: readonly string[]; readonly [NotebookRendererContribution.optionalDependencies]: readonly string[]; readonly [NotebookRendererContribution.requiresMessaging]: RendererMessagingSpec; } +const NotebookPreloadContribution = Object.freeze({ + type: 'type', + entrypoint: 'entrypoint', +}); + +export interface INotebookPreloadContribution { + readonly [NotebookPreloadContribution.type]: string; + readonly [NotebookPreloadContribution.entrypoint]: string; +} + const notebookProviderContribution: IJSONSchema = { description: nls.localize('contributes.notebook.provider', 'Contributes notebook document provider.'), type: 'array', @@ -139,7 +149,6 @@ const notebookRendererContribution: IJSONSchema = { 'optional', 'never', ], - enumDescriptions: [ nls.localize('contributes.notebook.renderer.requiresMessaging.always', 'Messaging is required. The renderer will only be used when it\'s part of an extension that can be run in an extension host.'), nls.localize('contributes.notebook.renderer.requiresMessaging.optional', 'The renderer is better with messaging available, but it\'s not requried.'), @@ -198,14 +207,40 @@ const notebookRendererContribution: IJSONSchema = { } }; -export const notebooksExtensionPoint = ExtensionsRegistry.registerExtensionPoint( - { - extensionPoint: 'notebooks', - jsonSchema: notebookProviderContribution - }); +const notebookPreloadContribution: IJSONSchema = { + description: nls.localize('contributes.preload.provider', 'Contributes notebook preloads.'), + type: 'array', + defaultSnippets: [{ body: [{ type: '', entrypoint: '' }] }], + items: { + type: 'object', + required: [ + NotebookPreloadContribution.type, + NotebookPreloadContribution.entrypoint + ], + properties: { + [NotebookPreloadContribution.type]: { + type: 'string', + description: nls.localize('contributes.preload.provider.viewType', 'Type of the notebook.'), + }, + [NotebookPreloadContribution.entrypoint]: { + type: 'string', + description: nls.localize('contributes.preload.entrypoint', 'Path to file loaded in the webview.'), + }, + } + } +}; -export const notebookRendererExtensionPoint = ExtensionsRegistry.registerExtensionPoint( - { - extensionPoint: 'notebookRenderer', - jsonSchema: notebookRendererContribution - }); +export const notebooksExtensionPoint = ExtensionsRegistry.registerExtensionPoint({ + extensionPoint: 'notebooks', + jsonSchema: notebookProviderContribution +}); + +export const notebookRendererExtensionPoint = ExtensionsRegistry.registerExtensionPoint({ + extensionPoint: 'notebookRenderer', + jsonSchema: notebookRendererContribution +}); + +export const notebookPreloadExtensionPoint = ExtensionsRegistry.registerExtensionPoint({ + extensionPoint: 'notebookPreload', + jsonSchema: notebookPreloadContribution, +}); diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts index 19d44f350d6..6e89fd39df6 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts @@ -58,12 +58,12 @@ export class NotebookEditorWidgetService implements INotebookEditorService { })); listeners.push(group.onWillMoveEditor(e => { if (e.editor instanceof NotebookEditorInput) { - this._freeWidget(e.editor, e.groupId, e.target); + this._allowWidgetMove(e.editor, e.groupId, e.target); } if (isCompositeNotebookEditorInput(e.editor)) { e.editor.editorInputs.forEach(input => { - this._freeWidget(input, e.groupId, e.target); + this._allowWidgetMove(input, e.groupId, e.target); }); } })); @@ -105,7 +105,7 @@ export class NotebookEditorWidgetService implements INotebookEditorService { domNode.remove(); } - private _freeWidget(input: NotebookEditorInput, sourceID: GroupIdentifier, targetID: GroupIdentifier): void { + private _allowWidgetMove(input: NotebookEditorInput, sourceID: GroupIdentifier, targetID: GroupIdentifier): void { const targetWidget = this._borrowableEditors.get(targetID)?.get(input.resource); if (targetWidget) { // not needed @@ -116,9 +116,10 @@ export class NotebookEditorWidgetService implements INotebookEditorService { if (!widget) { throw new Error('no widget at source group'); } + // don't allow the widget to be retrieved at its previous location any more this._borrowableEditors.get(sourceID)?.delete(input.resource); - widget.token = undefined; + // allow the widget to be retrieved at its new location let targetMap = this._borrowableEditors.get(targetID); if (!targetMap) { targetMap = new ResourceMap(); diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookExecutionServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookExecutionServiceImpl.ts index e2d7a240ced..ef959384ee2 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookExecutionServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookExecutionServiceImpl.ts @@ -15,6 +15,7 @@ import { CellKind, INotebookTextModel, NotebookCellExecutionState } from 'vs/wor import { INotebookExecutionService } from 'vs/workbench/contrib/notebook/common/notebookExecutionService'; import { INotebookCellExecution, INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; import { INotebookKernel, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; export class NotebookExecutionService implements INotebookExecutionService, IDisposable { declare _serviceBrand: undefined; @@ -29,7 +30,7 @@ export class NotebookExecutionService implements INotebookExecutionService, IDis ) { } - async executeNotebookCells(notebook: INotebookTextModel, cells: Iterable): Promise { + async executeNotebookCells(notebook: INotebookTextModel, cells: Iterable, contextKeyService: IContextKeyService): Promise { const cellsArr = Array.from(cells); this._logService.debug(`NotebookExecutionService#executeNotebookCells ${JSON.stringify(cellsArr.map(c => c.handle))}`); const message = nls.localize('notebookRunTrust', "Executing a notebook cell will run code from this workspace."); @@ -50,7 +51,7 @@ export class NotebookExecutionService implements INotebookExecutionService, IDis let kernel = this._notebookKernelService.getSelectedOrSuggestedKernel(notebook); if (!kernel) { - kernel = await this.resolveSourceActions(notebook); + kernel = await this.resolveSourceActions(notebook, contextKeyService); } if (!kernel) { @@ -87,12 +88,12 @@ export class NotebookExecutionService implements INotebookExecutionService, IDis } } - private async resolveSourceActions(notebook: INotebookTextModel) { + private async resolveSourceActions(notebook: INotebookTextModel, contextKeyService: IContextKeyService) { let kernel: INotebookKernel | undefined; const info = this._notebookKernelService.getMatchingKernel(notebook); if (info.all.length === 0) { // no kernel at all - const sourceActions = this._notebookKernelService.getSourceActions(); + const sourceActions = this._notebookKernelService.getSourceActions(notebook, contextKeyService); const primaryActions = sourceActions.filter(action => action.isPrimary); const action = sourceActions.length === 1 ? sourceActions[0] diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookExecutionStateServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookExecutionStateServiceImpl.ts index b1be91749fa..c38f92fe4c4 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookExecutionStateServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookExecutionStateServiceImpl.ts @@ -7,6 +7,7 @@ import { Emitter } from 'vs/base/common/event'; import { combinedDisposable, Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { ResourceMap } from 'vs/base/common/map'; import { isEqual } from 'vs/base/common/resources'; +import { withNullAsUndefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; @@ -14,6 +15,7 @@ import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/no import { CellEditType, CellUri, ICellEditOperation, NotebookCellExecutionState, NotebookCellInternalMetadata, NotebookTextModelWillAddRemoveEvent } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { CellExecutionUpdateType, INotebookExecutionService } from 'vs/workbench/contrib/notebook/common/notebookExecutionService'; import { ICellExecuteUpdate, ICellExecutionComplete, ICellExecutionStateChangedEvent, ICellExecutionStateUpdate, IFailedCellInfo, INotebookCellExecution, INotebookExecutionStateService, INotebookFailStateChangedEvent } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; +import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; export class NotebookExecutionStateService extends Disposable implements INotebookExecutionStateService { @@ -68,11 +70,16 @@ export class NotebookExecutionStateService extends Disposable implements INotebo return undefined; } - getCellExecutionStatesForNotebook(notebook: URI): INotebookCellExecution[] { + getCellExecutionsForNotebook(notebook: URI): INotebookCellExecution[] { const exeMap = this._executions.get(notebook); return exeMap ? Array.from(exeMap.values()) : []; } + getCellExecutionsByHandleForNotebook(notebook: URI): Map | undefined { + const exeMap = this._executions.get(notebook); + return withNullAsUndefined(exeMap); + } + private _onCellExecutionDidChange(notebookUri: URI, cellHandle: number, exe: CellExecution): void { this._onDidChangeCellExecution.fire(new NotebookExecutionEvent(notebookUri, cellHandle, exe)); } @@ -244,6 +251,7 @@ class NotebookExecutionListeners extends Disposable { constructor( notebook: URI, @INotebookService private readonly _notebookService: INotebookService, + @INotebookKernelService private readonly _notebookKernelService: INotebookKernelService, @INotebookExecutionService private readonly _notebookExecutionService: INotebookExecutionService, @INotebookExecutionStateService private readonly _notebookExecutionStateService: INotebookExecutionStateService, @ILogService private readonly _logService: ILogService, @@ -263,7 +271,7 @@ class NotebookExecutionListeners extends Disposable { private cancelAll(): void { this._logService.debug(`NotebookExecutionListeners#cancelAll`); - const exes = this._notebookExecutionStateService.getCellExecutionStatesForNotebook(this._notebookModel.uri); + const exes = this._notebookExecutionStateService.getCellExecutionsForNotebook(this._notebookModel.uri); this._notebookExecutionService.cancelNotebookCellHandles(this._notebookModel, exes.map(exe => exe.cellHandle)); } @@ -273,25 +281,34 @@ class NotebookExecutionListeners extends Disposable { } private onWillAddRemoveCells(e: NotebookTextModelWillAddRemoveEvent): void { - const notebookExes = this._notebookExecutionStateService.getCellExecutionStatesForNotebook(this._notebookModel.uri); - const handles = new Set(notebookExes.map(exe => exe.cellHandle)); - const myDeletedHandles = new Set(); - e.rawEvent.changes.forEach(([start, deleteCount]) => { - if (deleteCount) { - const deletedHandles = this._notebookModel.cells.slice(start, start + deleteCount).map(c => c.handle); - deletedHandles.forEach(h => { - if (handles.has(h)) { - myDeletedHandles.add(h); - } - }); + const notebookExes = this._notebookExecutionStateService.getCellExecutionsByHandleForNotebook(this._notebookModel.uri); + + const executingDeletedHandles = new Set(); + const pendingDeletedHandles = new Set(); + if (notebookExes) { + e.rawEvent.changes.forEach(([start, deleteCount]) => { + if (deleteCount) { + const deletedHandles = this._notebookModel.cells.slice(start, start + deleteCount).map(c => c.handle); + deletedHandles.forEach(h => { + const exe = notebookExes.get(h); + if (exe?.state === NotebookCellExecutionState.Executing) { + executingDeletedHandles.add(h); + } else if (exe) { + pendingDeletedHandles.add(h); + } + }); + } + }); + } + + if (executingDeletedHandles.size || pendingDeletedHandles.size) { + const kernel = this._notebookKernelService.getSelectedOrSuggestedKernel(this._notebookModel); + if (kernel) { + const implementsInterrupt = kernel.implementsInterrupt; + const handlesToCancel = implementsInterrupt ? [...executingDeletedHandles] : [...executingDeletedHandles, ...pendingDeletedHandles]; + this._logService.debug(`NotebookExecution#onWillAddRemoveCells, ${JSON.stringify([...handlesToCancel])}`); + kernel.cancelNotebookCellExecution(this._notebookModel.uri, handlesToCancel); } - - return false; - }); - - if (myDeletedHandles.size) { - this._logService.debug(`NotebookExecution#onWillAddRemoveCells, ${JSON.stringify([...myDeletedHandles])}`); - this._notebookExecutionService.cancelNotebookCellHandles(this._notebookModel, myDeletedHandles); } } } diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookKernelServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookKernelServiceImpl.ts index dd2ab69fa3c..5c3e2e06544 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookKernelServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookKernelServiceImpl.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { Event, Emitter } from 'vs/base/common/event'; -import { Disposable, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { INotebookTextModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { INotebookKernel, ISelectedNotebooksChangeEvent, INotebookKernelMatchResult, INotebookKernelService, INotebookTextModelLike, ISourceAction } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel, ISelectedNotebooksChangeEvent, INotebookKernelMatchResult, INotebookKernelService, INotebookTextModelLike, ISourceAction, INotebookSourceActionChangeEvent } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { LRUCache, ResourceMap } from 'vs/base/common/map'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { URI } from 'vs/base/common/uri'; @@ -71,10 +71,20 @@ class SourceAction extends Disposable implements ISourceAction { } private async _runAction(): Promise { - await this.action.run(); + try { + await this.action.run(); + } catch (error) { + console.warn(`Kernel source command failed: ${error}`); + } } } +interface IKernelInfoCache { + menu: IMenu; + actions: [ISourceAction, IDisposable][]; + +} + export class NotebookKernelService extends Disposable implements INotebookKernelService { declare _serviceBrand: undefined; @@ -87,15 +97,14 @@ export class NotebookKernelService extends Disposable implements INotebookKernel private readonly _onDidAddKernel = this._register(new Emitter()); private readonly _onDidRemoveKernel = this._register(new Emitter()); private readonly _onDidChangeNotebookAffinity = this._register(new Emitter()); - private readonly _onDidChangeSourceActions = this._register(new Emitter()); - private readonly _sourceMenu: IMenu; - private _sourceActions: [ISourceAction, IDisposable][]; + private readonly _onDidChangeSourceActions = this._register(new Emitter()); + private readonly _kernelSources = new Map(); readonly onDidChangeSelectedNotebooks: Event = this._onDidChangeNotebookKernelBinding.event; readonly onDidAddKernel: Event = this._onDidAddKernel.event; readonly onDidRemoveKernel: Event = this._onDidRemoveKernel.event; readonly onDidChangeNotebookAffinity: Event = this._onDidChangeNotebookAffinity.event; - readonly onDidChangeSourceActions: Event = this._onDidChangeSourceActions.event; + readonly onDidChangeSourceActions: Event = this._onDidChangeSourceActions.event; private static _storageNotebookBinding = 'notebook.controller2NotebookBindings'; @@ -104,7 +113,7 @@ export class NotebookKernelService extends Disposable implements INotebookKernel @INotebookService private readonly _notebookService: INotebookService, @IStorageService private readonly _storageService: IStorageService, @IMenuService readonly _menuService: IMenuService, - @IContextKeyService contextKeyService: IContextKeyService + @IContextKeyService readonly _contextKeyService: IContextKeyService ) { super(); @@ -117,10 +126,6 @@ export class NotebookKernelService extends Disposable implements INotebookKernel this.selectKernelForNotebook(undefined, notebook); } })); - this._sourceMenu = this._register(this._menuService.createMenu(MenuId.NotebookKernelSource, contextKeyService)); - this._sourceActions = []; - - this._initSourceActions(); // restore from storage try { @@ -131,34 +136,12 @@ export class NotebookKernelService extends Disposable implements INotebookKernel } } - private _initSourceActions() { - const loadActionsFromMenu = (menu: IMenu) => { - const groups = menu.getActions({ shouldForwardArgs: true }); - const sourceActions: [ISourceAction, IDisposable][] = []; - groups.forEach(group => { - const isPrimary = /^primary/.test(group[0]); - group[1].forEach(action => { - const sourceAction = new SourceAction(action, isPrimary); - const stateChangeListener = sourceAction.onDidChangeState(() => { - this._onDidChangeSourceActions.fire(); - }); - sourceActions.push([sourceAction, stateChangeListener]); - }); - }); - this._sourceActions = sourceActions; - this._onDidChangeSourceActions.fire(); - }; - - this._register(this._sourceMenu.onDidChange(() => { - loadActionsFromMenu(this._sourceMenu); - })); - - loadActionsFromMenu(this._sourceMenu); - } - override dispose() { this._kernels.clear(); - dispose(this._sourceActions.map(a => a[1])); + this._kernelSources.forEach(v => { + v.menu.dispose(); + v.actions.forEach(a => a[1].dispose()); + }); super.dispose(); } @@ -246,7 +229,7 @@ export class NotebookKernelService extends Disposable implements INotebookKernel // bound kernel const selectedId = this._notebookBindings.get(NotebookTextModelLikeId.str(notebook)); const selected = selectedId ? this._kernels.get(selectedId)?.kernel : undefined; - const suggestions = kernels.filter(item => item.instanceAffinity > 1 && item.kernel !== selected).map(item => item.kernel); + const suggestions = kernels.filter(item => item.instanceAffinity > 1).map(item => item.kernel); const hidden = kernels.filter(item => item.instanceAffinity < 0).map(item => item.kernel); return { all, selected, suggestions, hidden }; } @@ -303,11 +286,54 @@ export class NotebookKernelService extends Disposable implements INotebookKernel this._onDidChangeNotebookAffinity.fire(); } - getRunningSourceActions() { - return this._sourceActions.filter(action => action[0].execution).map(action => action[0]); + getRunningSourceActions(notebook: INotebookTextModelLike) { + const id = NotebookTextModelLikeId.str(notebook); + const existingInfo = this._kernelSources.get(id); + if (existingInfo) { + return existingInfo.actions.filter(action => action[0].execution).map(action => action[0]); + } + + return []; } - getSourceActions(): ISourceAction[] { - return this._sourceActions.map(a => a[0]); + getSourceActions(notebook: INotebookTextModelLike, contextKeyService: IContextKeyService | undefined): ISourceAction[] { + contextKeyService = contextKeyService ?? this._contextKeyService; + const id = NotebookTextModelLikeId.str(notebook); + const existingInfo = this._kernelSources.get(id); + + if (existingInfo) { + return existingInfo.actions.map(a => a[0]); + } + + const sourceMenu = this._register(this._menuService.createMenu(MenuId.NotebookKernelSource, contextKeyService)); + const info: IKernelInfoCache = { menu: sourceMenu, actions: [] }; + + const loadActionsFromMenu = (menu: IMenu) => { + const groups = menu.getActions({ shouldForwardArgs: true }); + const sourceActions: [ISourceAction, IDisposable][] = []; + groups.forEach(group => { + const isPrimary = /^primary/.test(group[0]); + group[1].forEach(action => { + const sourceAction = new SourceAction(action, isPrimary); + const stateChangeListener = sourceAction.onDidChangeState(() => { + this._onDidChangeSourceActions.fire({ + notebook: notebook.uri + }); + }); + sourceActions.push([sourceAction, stateChangeListener]); + }); + }); + info.actions = sourceActions; + this._kernelSources.set(id, info); + this._onDidChangeSourceActions.fire({ notebook: notebook.uri }); + }; + + this._register(sourceMenu.onDidChange(() => { + loadActionsFromMenu(sourceMenu); + })); + + loadActionsFromMenu(sourceMenu); + + return info.actions.map(a => a[0]); } } diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts index 5fc81948747..4242f322651 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts @@ -23,20 +23,20 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { Memento } from 'vs/workbench/common/memento'; -import { INotebookEditorContribution, notebookRendererExtensionPoint, notebooksExtensionPoint } from 'vs/workbench/contrib/notebook/browser/notebookExtensionPoint'; +import { INotebookEditorContribution, notebookPreloadExtensionPoint, notebookRendererExtensionPoint, notebooksExtensionPoint } from 'vs/workbench/contrib/notebook/browser/notebookExtensionPoint'; import { INotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookDiffEditorInput } from 'vs/workbench/contrib/notebook/common/notebookDiffEditorInput'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellUri, NotebookSetting, INotebookContributionData, INotebookExclusiveDocumentFilter, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, IOutputDto, MimeTypeDisplayOrder, NotebookData, NotebookEditorPriority, NotebookRendererMatch, NOTEBOOK_DISPLAY_ORDER, RENDERER_EQUIVALENT_EXTENSIONS, RENDERER_NOT_AVAILABLE, TransientOptions, NotebookExtensionDescription } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellUri, NotebookSetting, INotebookContributionData, INotebookExclusiveDocumentFilter, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, IOutputDto, MimeTypeDisplayOrder, NotebookData, NotebookEditorPriority, NotebookRendererMatch, NOTEBOOK_DISPLAY_ORDER, RENDERER_EQUIVALENT_EXTENSIONS, RENDERER_NOT_AVAILABLE, TransientOptions, NotebookExtensionDescription, INotebookStaticPreloadInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput'; import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; import { updateEditorTopPadding } from 'vs/workbench/contrib/notebook/common/notebookOptions'; -import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer'; +import { NotebookOutputRendererInfo, NotebookStaticPreloadInfo as NotebookStaticPreloadInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer'; import { NotebookEditorDescriptor, NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { ComplexNotebookProviderInfo, INotebookContentProvider, INotebookSerializer, INotebookService, SimpleNotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookService'; import { DiffEditorInputFactoryFunction, EditorInputFactoryFunction, EditorInputFactoryObject, IEditorResolverService, IEditorType, RegisteredEditorInfo, RegisteredEditorPriority, UntitledEditorInputFactoryFunction } from 'vs/workbench/services/editor/common/editorResolverService'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionService, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; export class NotebookProviderInfoStore extends Disposable { @@ -422,6 +422,9 @@ export class NotebookService extends Disposable implements INotebookService { private readonly _notebookRenderersInfoStore = this._instantiationService.createInstance(NotebookOutputRendererInfoStore); private readonly _onDidChangeOutputRenderers = this._register(new Emitter()); readonly onDidChangeOutputRenderers = this._onDidChangeOutputRenderers.event; + + private readonly _notebookStaticPreloadInfoStore = new Set(); + private readonly _models = new ResourceMap(); private readonly _onWillAddNotebookDocument = this._register(new Emitter()); @@ -490,6 +493,35 @@ export class NotebookService extends Disposable implements INotebookService { this._onDidChangeOutputRenderers.fire(); }); + notebookPreloadExtensionPoint.setHandler(extensions => { + this._notebookStaticPreloadInfoStore.clear(); + + for (const extension of extensions) { + if (!isProposedApiEnabled(extension.description, 'contribNotebookStaticPreloads')) { + continue; + } + + for (const notebookContribution of extension.value) { + if (!notebookContribution.entrypoint) { // avoid crashing + extension.collector.error(`Notebook preload does not specify entry point`); + continue; + } + + const type = notebookContribution.type; + if (!type) { + extension.collector.error(`Notebook preload does not specify type-property`); + continue; + } + + this._notebookStaticPreloadInfoStore.add(new NotebookStaticPreloadInfo({ + type, + extension: extension.description, + entrypoint: notebookContribution.entrypoint, + })); + } + } + }); + const updateOrder = () => { this._displayOrder = new MimeTypeDisplayOrder( this._configurationService.getValue(NotebookSetting.displayOrder) || [], @@ -513,7 +545,7 @@ export class NotebookService extends Disposable implements INotebookService { let decorationTriggeredAdjustment = false; const decorationCheckSet = new Set(); - this._register(this._codeEditorService.onDecorationTypeRegistered(e => { + const onDidAddDecorationType = (e: string) => { if (decorationTriggeredAdjustment) { return; } @@ -544,7 +576,9 @@ export class NotebookService extends Disposable implements INotebookService { } decorationCheckSet.add(e); - })); + }; + this._register(this._codeEditorService.onDecorationTypeRegistered(onDidAddDecorationType)); + this._codeEditorService.listDecorationTypes().forEach(onDidAddDecorationType); } @@ -669,6 +703,14 @@ export class NotebookService extends Disposable implements INotebookService { return this._notebookRenderersInfoStore.getAll(); } + *getStaticPreloads(viewType: string): Iterable { + for (const preload of this._notebookStaticPreloadInfoStore) { + if (preload.type === viewType) { + yield preload; + } + } + } + // --- notebook documents: create, destory, retrieve, enumerate createNotebookTextModel(viewType: string, uri: URI, data: NotebookData, transientOptions: TransientOptions): NotebookTextModel { diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions.ts index 3059080c7d5..f3751c8dbdf 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions.ts @@ -134,7 +134,7 @@ registerAction2(class ToggleLineNumberAction extends Action2 { f1: true, toggled: { condition: ContextKeyExpr.notEquals('config.notebook.lineNumbers', 'off'), - title: { value: localize('notebook.showLineNumbers', "Show Notebook Line Numbers"), original: 'Show Notebook Line Numbers' }, + title: localize('notebook.showLineNumbers', "Notebook Line Numbers"), } }); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index 2d50947923e..2e1f3869a32 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -934,7 +934,11 @@ export class NotebookCellList extends WorkbenchList implements ID } triggerScrollFromMouseWheelEvent(browserEvent: IMouseWheelEvent) { - this.view.triggerScrollFromMouseWheelEvent(browserEvent); + this.view.delegateScrollFromMouseWheelEvent(browserEvent); + } + + delegateVerticalScrollbarPointerDown(browserEvent: PointerEvent) { + this.view.delegateVerticalScrollbarPointerDown(browserEvent); } isElementAboveViewport(index: number) { diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts index ceabb8d6de9..37b932a0398 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts @@ -92,12 +92,12 @@ export interface INotebookCellList { } export interface BaseCellRenderTemplate { - rootContainer: HTMLElement; - editorPart: HTMLElement; - cellInputCollapsedContainer: HTMLElement; - instantiationService: IInstantiationService; - container: HTMLElement; - cellContainer: HTMLElement; + readonly rootContainer: HTMLElement; + readonly editorPart: HTMLElement; + readonly cellInputCollapsedContainer: HTMLElement; + readonly instantiationService: IInstantiationService; + readonly container: HTMLElement; + readonly cellContainer: HTMLElement; readonly templateDisposables: DisposableStore; readonly elementDisposables: DisposableStore; currentRenderedCell?: ICellViewModel; @@ -106,8 +106,8 @@ export interface BaseCellRenderTemplate { } export interface MarkdownCellRenderTemplate extends BaseCellRenderTemplate { - editorContainer: HTMLElement; - foldingIndicator: HTMLElement; + readonly editorContainer: HTMLElement; + readonly foldingIndicator: HTMLElement; currentEditor?: ICodeEditor; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 30da51aa51c..bbef3bc7abc 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; -import { IAction } from 'vs/base/common/actions'; import { coalesce } from 'vs/base/common/arrays'; import { decodeBase64 } from 'vs/base/common/buffer'; import { Emitter, Event } from 'vs/base/common/event'; @@ -21,8 +20,7 @@ import { ILanguageService } from 'vs/editor/common/languages/language'; import { generateTokensCSSForColorMap } from 'vs/editor/common/languages/supports/tokenization'; import { tokenizeToString } from 'vs/editor/common/languages/textToHtmlTokenizer'; import * as nls from 'vs/nls'; -import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { MenuId } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -37,7 +35,7 @@ import { NOTEBOOK_WEBVIEW_BOUNDARY } from 'vs/workbench/contrib/notebook/browser import { preloadsScriptStr } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads'; import { transformWebviewThemeVars } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewThemeMapping'; import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel'; -import { CellUri, INotebookRendererInfo, isTextStreamMime, NotebookSetting, RendererMessagingSpec } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellUri, INotebookRendererInfo, NotebookSetting, RendererMessagingSpec } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookKernel } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { IScopedRendererMessaging } from 'vs/workbench/contrib/notebook/common/notebookRendererMessagingService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; @@ -45,8 +43,8 @@ import { IWebviewElement, IWebviewService, WebviewContentPurpose } from 'vs/work import { WebviewWindowDragMonitor } from 'vs/workbench/contrib/webview/browser/webviewWindowDragMonitor'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { FromWebviewMessage, IAckOutputHeight, IClickedDataUrlMessage, ICodeBlockHighlightRequest, IContentWidgetTopRequest, IControllerPreload, ICreationContent, ICreationRequestMessage, IFindMatch, IMarkupCellInitialization, RendererMetadata, ToWebviewMessage } from './webviewMessages'; -import { compressOutputItemStreams } from 'vs/workbench/contrib/notebook/browser/view/renderers/stdOutErrorPreProcessor'; +import { FromWebviewMessage, IAckOutputHeight, IClickedDataUrlMessage, ICodeBlockHighlightRequest, IContentWidgetTopRequest, IControllerPreload, ICreationContent, ICreationRequestMessage, IFindMatch, IMarkupCellInitialization, RendererMetadata, StaticPreloadMetadata, ToWebviewMessage } from './webviewMessages'; +import { DeferredPromise } from 'vs/base/common/async'; export interface ICachedInset { outputId: string; @@ -110,15 +108,20 @@ export class BackLayerWebView extends Disposable { private readonly _onMessage = this._register(new Emitter()); private readonly _preloadsCache = new Set(); public readonly onMessage: Event = this._onMessage.event; - private _initialized?: Promise; private _disposed = false; private _currentKernel?: INotebookKernel; + private _initialized?: DeferredPromise; + private _webviewPreloadInitialized?: DeferredPromise; + private firstInit = true; + private initializeMarkupPromise?: { readonly requestId: string; readonly p: DeferredPromise; readonly isFirstInit: boolean }; + private readonly nonce = UUID.generateUuid(); constructor( public notebookEditor: INotebookDelegateForWebview, public readonly id: string, + public readonly notebookViewType: string, public readonly documentUri: URI, private options: BacklayerWebviewOptions, private readonly rendererMessaging: IScopedRendererMessaging | undefined, @@ -130,7 +133,6 @@ export class BackLayerWebView extends Disposable { @IFileDialogService private readonly fileDialogService: IFileDialogService, @IFileService private readonly fileService: IFileService, @IContextMenuService private readonly contextMenuService: IContextMenuService, - @IMenuService private readonly menuService: IMenuService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IWorkspaceTrustManagementService private readonly workspaceTrustManagementService: IWorkspaceTrustManagementService, @IConfigurationService private readonly configurationService: IConfigurationService, @@ -224,10 +226,12 @@ export class BackLayerWebView extends Disposable { private generateContent(coreDependencies: string, baseUrl: string) { const renderersData = this.getRendererData(); + const preloadsData = this.getStaticPreloadsData(); const preloadScript = preloadsScriptStr( this.options, { dragAndDropEnabled: this.options.dragAndDropEnabled }, renderersData, + preloadsData, this.workspaceTrustManagementService.isWorkspaceTrusted(), this.configurationService.getValue(NotebookSetting.textOutputLineLimit) ?? 30, this.nonce); @@ -402,18 +406,26 @@ export class BackLayerWebView extends Disposable { private getRendererData(): RendererMetadata[] { return this.notebookService.getRenderers().map((renderer): RendererMetadata => { - const entrypoint = this.asWebviewUri(renderer.entrypoint, renderer.extensionLocation).toString(); + const entrypoint = { + extends: renderer.entrypoint.extends, + path: this.asWebviewUri(renderer.entrypoint.path, renderer.extensionLocation).toString() + }; return { id: renderer.id, entrypoint, mimeTypes: renderer.mimeTypes, - extends: renderer.extends, messaging: renderer.messaging !== RendererMessagingSpec.Never, isBuiltin: renderer.isBuiltin }; }); } + private getStaticPreloadsData(): StaticPreloadMetadata[] { + return Array.from(this.notebookService.getStaticPreloads(this.notebookViewType), preload => { + return { entrypoint: this.asWebviewUri(preload.entrypoint, preload.extensionLocation).toString().toString() }; + }); + } + private asWebviewUri(uri: URI, fromExtension: URI | undefined) { return asWebviewUri(uri, fromExtension?.scheme === Schemas.vscodeRemote ? { isRemote: true, authority: fromExtension.authority } : undefined); } @@ -452,11 +464,9 @@ export class BackLayerWebView extends Disposable { } let coreDependencies = ''; - let resolveFunc: () => void; - this._initialized = new Promise((resolve) => { - resolveFunc = resolve; - }); + this._initialized = new DeferredPromise(); + this._webviewPreloadInitialized = new DeferredPromise(); if (!isWeb) { const loaderUri = FileAccess.asFileUri('vs/loader.js', require); @@ -469,7 +479,7 @@ export class BackLayerWebView extends Disposable { `; const htmlContent = this.generateContent(coreDependencies, baseUrl.toString()); this._initialize(htmlContent); - resolveFunc!(); + this._initialized.complete(); } else { const loaderUri = FileAccess.asBrowserUri('vs/loader.js', require); @@ -493,16 +503,16 @@ var requirejs = (function() { const htmlContent = this.generateContent(coreDependencies, baseUrl.toString()); this._initialize(htmlContent); - resolveFunc!(); + this._initialized!.complete(); }, error => { // the fetch request is rejected const htmlContent = this.generateContent(coreDependencies, baseUrl.toString()); this._initialize(htmlContent); - resolveFunc!(); + this._initialized!.complete(); }); } - await this._initialized; + await this._initialized.p; } private getNotebookBaseUri() { @@ -557,24 +567,8 @@ var requirejs = (function() { return; } - if (matchesScheme(link, Schemas.command)) { - const ret = /command\:workbench\.action\.openLargeOutput\?(.*)/.exec(link); - if (ret && ret.length === 2) { - const outputId = ret[1]; - this.openerService.open(CellUri.generateCellOutputUri(this.documentUri, outputId)); - return; - } - console.warn('Command links are deprecated and will be removed, use message passing instead: https://github.com/microsoft/vscode/issues/123601'); - } - - if (matchesScheme(link, Schemas.command)) { - if (this.workspaceTrustManagementService.isWorkspaceTrusted()) { - this.openerService.open(link, { fromUserGesture: true, allowContributedOpeners: true, allowCommands: true }); - } else { - console.warn('Command links are disabled in untrusted workspaces'); - } - } else if (matchesSomeScheme(link, Schemas.vscodeNotebookCell, Schemas.http, Schemas.https, Schemas.mailto)) { - this.openerService.open(link, { fromUserGesture: true, allowContributedOpeners: true, allowCommands: true }); + if (matchesSomeScheme(link, Schemas.vscodeNotebookCell, Schemas.http, Schemas.https, Schemas.mailto)) { + this.openerService.open(link, { fromUserGesture: true, allowContributedOpeners: true, allowCommands: false }); } })); @@ -590,9 +584,17 @@ var requirejs = (function() { switch (data.type) { case 'initialized': { + this._webviewPreloadInitialized?.complete(); this.initializeWebViewState(); break; } + case 'initializedMarkup': { + if (this.initializeMarkupPromise?.requestId === data.requestId) { + this.initializeMarkupPromise?.p.complete(); + this.initializeMarkupPromise = undefined; + } + break; + } case 'dimension': { for (const update of data.updates) { const height = update.height; @@ -685,23 +687,35 @@ var requirejs = (function() { } case 'clicked-link': { let linkToOpen: URI | string | undefined; + if (matchesScheme(data.href, Schemas.command)) { - const ret = /command\:workbench\.action\.openLargeOutput\?(.*)/.exec(data.href); - if (ret && ret.length === 2) { - const outputId = ret[1]; - const group = this.editorGroupService.activeGroup; - - if (group) { - if (group.activeEditor) { - group.pinEditor(group.activeEditor); + // We allow a very limited set of commands + const uri = URI.parse(data.href); + switch (uri.path) { + case 'workbench.action.openLargeOutput': { + const outputId = uri.query; + const group = this.editorGroupService.activeGroup; + if (group) { + if (group.activeEditor) { + group.pinEditor(group.activeEditor); + } } - } - this.openerService.open(CellUri.generateCellOutputUri(this.documentUri, outputId)); - return; + this.openerService.open(CellUri.generateCellOutputUri(this.documentUri, outputId)); + return; + } + case 'github-issues.authNow': + case 'workbench.extensions.search': + case 'workbench.action.openSettings': { + this.openerService.open(data.href, { fromUserGesture: true, allowCommands: true, fromWorkspace: true }); + return; + } } + + return; } - if (matchesSomeScheme(data.href, Schemas.http, Schemas.https, Schemas.mailto, Schemas.command, Schemas.vscodeNotebookCell, Schemas.vscodeNotebook)) { + + if (matchesSomeScheme(data.href, Schemas.http, Schemas.https, Schemas.mailto, Schemas.vscodeNotebookCell, Schemas.vscodeNotebook)) { linkToOpen = data.href; } else if (!/^[\w\-]+:/.test(data.href)) { const fragmentStartIndex = data.href.lastIndexOf('#'); @@ -732,7 +746,7 @@ var requirejs = (function() { } if (linkToOpen) { - this.openerService.open(linkToOpen, { fromUserGesture: true, allowCommands: true, fromWorkspace: true }); + this.openerService.open(linkToOpen, { fromUserGesture: true, allowCommands: false, fromWorkspace: true }); } break; } @@ -766,13 +780,8 @@ var requirejs = (function() { // Then show the context menu const webviewRect = this.element.getBoundingClientRect(); this.contextMenuService.showContextMenu({ - getActions: () => { - const result: IAction[] = []; - const menu = this.menuService.createMenu(MenuId.NotebookCellTitle, this.contextKeyService); - createAndFillInContextMenuActions(menu, undefined, result); - menu.dispose(); - return result; - }, + menuId: MenuId.NotebookCellTitle, + contextKeyService: this.contextKeyService, getAnchor: () => ({ x: webviewRect.x + data.clientX, y: webviewRect.y + data.clientY @@ -920,20 +929,19 @@ var requirejs = (function() { return webview; } - private _getResourceRootsCache() { + private _getResourceRootsCache(): URI[] { const workspaceFolders = this.contextService.getWorkspace().folders.map(x => x.uri); const notebookDir = this.getNotebookBaseUri(); return [ - ...this.notebookService.getNotebookProviderResourceRoots(), - ...this.notebookService.getRenderers().map(x => dirname(x.entrypoint)), - ...workspaceFolders, + this.notebookService.getNotebookProviderResourceRoots(), + this.notebookService.getRenderers().map(x => dirname(x.entrypoint.path)), + Array.from(this.notebookService.getStaticPreloads(this.notebookViewType), x => dirname(x.entrypoint)), + workspaceFolders, notebookDir, - ...this.getBuiltinLocalResourceRoots() - ]; + this.getBuiltinLocalResourceRoots() + ].flat(); } - private firstInit = true; - private initializeWebViewState() { this._preloadsCache.clear(); if (this._currentKernel) { @@ -944,9 +952,9 @@ var requirejs = (function() { this._sendMessageToWebview({ ...inset.cachedCreation, initiallyHidden: this.hiddenInsetMapping.has(output) }); } - if (this.firstInit) { + if (this.initializeMarkupPromise?.isFirstInit) { // On first run the contents have already been initialized so we don't need to init them again - this.firstInit = false; + // no op } else { const mdCells = [...this.markupPreviewMapping.values()]; this.markupPreviewMapping.clear(); @@ -1160,15 +1168,16 @@ var requirejs = (function() { return; } - // TODO: use proper handler - const p = new Promise(resolve => { - const sub = this.webview?.onMessage(e => { - if (e.message.type === 'initializedMarkup') { - resolve(); - sub?.dispose(); - } - }); - }); + this.initializeMarkupPromise?.p.complete(); + const requestId = UUID.generateUuid(); + this.initializeMarkupPromise = { p: new DeferredPromise(), requestId, isFirstInit: this.firstInit }; + + if (this._webviewPreloadInitialized) { + // wait for webview preload script module to be loaded + await this._webviewPreloadInitialized.p; + } + + this.firstInit = false; for (const cell of cells) { this.markupPreviewMapping.set(cell.cellId, cell); @@ -1177,9 +1186,10 @@ var requirejs = (function() { this._sendMessageToWebview({ type: 'initializeMarkup', cells, + requestId, }); - await p; + return this.initializeMarkupPromise.p.p; } /** @@ -1278,14 +1288,12 @@ var requirejs = (function() { let updatedContent: ICreationContent | undefined = undefined; if (content.type === RenderOutputType.Extension) { const output = content.source.model; - const firstBuffer = isTextStreamMime(content.mimeType) ? - compressOutputItemStreams(content.mimeType, output.outputs) : - output.outputs.find(op => op.mime === content.mimeType)!.data.buffer; + const firstBuffer = output.outputs.find(op => op.mime === content.mimeType)!.data; updatedContent = { type: RenderOutputType.Extension, outputId: outputCache.outputId, mimeType: content.mimeType, - valueBytes: firstBuffer, + valueBytes: firstBuffer.buffer, metadata: output.metadata, }; } @@ -1529,4 +1537,3 @@ function getTokenizationCss() { const tokenizationCss = colorMap ? generateTokensCSSForColorMap(colorMap) : ''; return tokenizationCss; } - diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 5bbf9dd0be2..3e7b8a61362 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -214,7 +214,8 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen } disposeTemplate(templateData: MarkdownCellRenderTemplate): void { - templateData.templateDisposables.clear(); + templateData.elementDisposables.dispose(); + templateData.templateDisposables.dispose(); } disposeElement(_element: ICellViewModel, _index: number, templateData: MarkdownCellRenderTemplate): void { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/stdOutErrorPreProcessor.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/stdOutErrorPreProcessor.ts deleted file mode 100644 index 7f20c6316fc..00000000000 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/stdOutErrorPreProcessor.ts +++ /dev/null @@ -1,56 +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 { VSBuffer } from 'vs/base/common/buffer'; -import type { IOutputItemDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; - - -/** - * Given a stream of individual stdout outputs, this function will return the compressed lines, escaping some of the common terminal escape codes. - * E.g. some terminal escape codes would result in the previous line getting cleared, such if we had 3 lines and - * last line contained such a code, then the result string would be just the first two lines. - */ -export function compressOutputItemStreams(mimeType: string, outputs: IOutputItemDto[]) { - const buffers: Uint8Array[] = []; - let startAppending = false; - - // Pick the first set of outputs with the same mime type. - for (const output of outputs) { - if (output.mime === mimeType) { - if ((buffers.length === 0 || startAppending)) { - buffers.push(output.data.buffer); - startAppending = true; - } - } else if (startAppending) { - startAppending = false; - } - } - compressStreamBuffer(buffers); - return VSBuffer.concat(buffers.map(buffer => VSBuffer.wrap(buffer))).buffer; -} -const MOVE_CURSOR_1_LINE_COMMAND = `${String.fromCharCode(27)}[A`; -const MOVE_CURSOR_1_LINE_COMMAND_BYTES = MOVE_CURSOR_1_LINE_COMMAND.split('').map(c => c.charCodeAt(0)); -const LINE_FEED = 10; -function compressStreamBuffer(streams: Uint8Array[]) { - streams.forEach((stream, index) => { - if (index === 0 || stream.length < MOVE_CURSOR_1_LINE_COMMAND.length) { - return; - } - - const previousStream = streams[index - 1]; - - // Remove the previous line if required. - const command = stream.subarray(0, MOVE_CURSOR_1_LINE_COMMAND.length); - if (command[0] === MOVE_CURSOR_1_LINE_COMMAND_BYTES[0] && command[1] === MOVE_CURSOR_1_LINE_COMMAND_BYTES[1] && command[2] === MOVE_CURSOR_1_LINE_COMMAND_BYTES[2]) { - const lastIndexOfLineFeed = previousStream.lastIndexOf(LINE_FEED); - if (lastIndexOfLineFeed === -1) { - return; - } - streams[index - 1] = previousStream.subarray(0, lastIndexOfLineFeed); - streams[index] = stream.subarray(MOVE_CURSOR_1_LINE_COMMAND.length); - } - }); - return streams; -} diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts index 79b1b1d3959..5eea6e550cf 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts @@ -138,6 +138,7 @@ export interface ICellDragEndMessage extends BaseToWebviewMessage { export interface IInitializedMarkupMessage extends BaseToWebviewMessage { readonly type: 'initializedMarkup'; + readonly requestId: string; } export interface ICodeBlockHighlightRequest { @@ -274,13 +275,16 @@ export interface IUpdateControllerPreloadsMessage { export interface RendererMetadata { readonly id: string; - readonly entrypoint: string; + readonly entrypoint: { readonly extends: string | undefined; readonly path: string }; readonly mimeTypes: readonly string[]; - readonly extends: string | undefined; readonly messaging: boolean; readonly isBuiltin: boolean; } +export interface StaticPreloadMetadata { + readonly entrypoint: string; +} + export interface IUpdateRenderersMessage { readonly type: 'updateRenderers'; readonly rendererData: readonly RendererMetadata[]; @@ -351,6 +355,7 @@ export interface IMarkupCellInitialization { export interface IInitializeMarkupCells { readonly type: 'initializeMarkup'; readonly cells: readonly IMarkupCellInitialization[]; + readonly requestId: string; } export interface INotebookStylesMessage { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index c0f2e223fea..9263b6ad49b 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -69,6 +69,7 @@ interface PreloadContext { readonly style: PreloadStyles; readonly options: PreloadOptions; readonly rendererData: readonly webviewMessages.RendererMetadata[]; + readonly staticPreloadsData: readonly webviewMessages.StaticPreloadMetadata[]; readonly isWorkspaceTrusted: boolean; readonly lineLimit: number; } @@ -199,7 +200,7 @@ async function webviewPreloads(ctx: PreloadContext) { } }; - async function loadScriptSource(url: string, originalUri = url): Promise { + async function loadScriptSource(url: string, originalUri: string): Promise { const res = await fetch(url); const text = await res.text(); if (!res.ok) { @@ -235,10 +236,10 @@ async function webviewPreloads(ctx: PreloadContext) { } function createKernelContext(): KernelPreloadContext { - return { + return Object.freeze({ onDidReceiveKernelMessage: onDidReceiveKernelMessage.event, postKernelMessage: (data: unknown) => postNotebookMessage('customKernelMessage', { message: data }), - }; + }); } const invokeSourceWithGlobals = (functionSrc: string, globals: { [name: string]: unknown }) => { @@ -246,11 +247,11 @@ async function webviewPreloads(ctx: PreloadContext) { return new Function(...args.map(([k]) => k), functionSrc)(...args.map(([, v]) => v)); }; - const runKernelPreload = async (url: string, originalUri: string): Promise => { + const runKernelPreload = async (url: string, originalUri: string, forceLoadAsModule: boolean): Promise => { const text = await loadScriptSource(url, originalUri); const isModule = /\bexport\b.*\bactivate\b/.test(text); try { - if (isModule) { + if (isModule || forceLoadAsModule) { const module: KernelPreloadModule = await __import(url); if (!module.activate) { console.error(`Notebook preload (${url}) looks like a module but does not export an activate function`); @@ -1048,7 +1049,7 @@ async function webviewPreloads(ctx: PreloadContext) { await Promise.all(event.data.cells.map(info => viewModel.ensureMarkupCell(info))); } finally { dimensionUpdater.updateImmediately(); - postNotebookMessage('initializedMarkup', {}); + postNotebookMessage('initializedMarkup', { requestId: event.data.requestId }); } break; } @@ -1140,7 +1141,7 @@ async function webviewPreloads(ctx: PreloadContext) { case 'preload': { const resources = event.data.resources; for (const { uri, originalUri } of resources) { - kernelPreloads.load(uri, originalUri); + kernelPreloads.load(uri, originalUri, false); } break; } @@ -1305,16 +1306,13 @@ async function webviewPreloads(ctx: PreloadContext) { } private load(): Promise { - if (!this._loadPromise) { - this._loadPromise = this._load(); - } - + this._loadPromise ??= this._load(); return this._loadPromise; } /** Inner function cached in the _loadPromise(). */ private async _load(): Promise { - const module: RendererModule = await __import(this.data.entrypoint); + const module: RendererModule = await __import(this.data.entrypoint.path); if (!module) { return; } @@ -1324,7 +1322,7 @@ async function webviewPreloads(ctx: PreloadContext) { // Load all renderers that extend this renderer await Promise.all( ctx.rendererData - .filter(d => d.extends === this.data.id) + .filter(d => d.entrypoint.extends === this.data.id) .map(async d => { const renderer = renderers.getRenderer(d.id); if (!renderer) { @@ -1360,9 +1358,9 @@ async function webviewPreloads(ctx: PreloadContext) { * @param uri URI to load from * @param originalUri URI to show in an error message if the preload is invalid. */ - public load(uri: string, originalUri: string) { + public load(uri: string, originalUri: string, forceLoadAsModule: boolean) { const promise = Promise.all([ - runKernelPreload(uri, originalUri), + runKernelPreload(uri, originalUri, forceLoadAsModule), this.waitForAllCurrent(), ]); @@ -1436,7 +1434,7 @@ async function webviewPreloads(ctx: PreloadContext) { } private rendererEqual(a: webviewMessages.RendererMetadata, b: webviewMessages.RendererMetadata) { - if (a.entrypoint !== b.entrypoint || a.id !== b.id || a.extends !== b.extends || a.messaging !== b.messaging) { + if (a.id !== b.id || a.entrypoint.path !== b.entrypoint.path || a.entrypoint.extends !== b.entrypoint.extends || a.messaging !== b.messaging) { return false; } @@ -1489,11 +1487,28 @@ async function webviewPreloads(ctx: PreloadContext) { this._renderers.get(rendererId)?.disposeOutputItem(outputId); } - public async render(info: rendererApi.OutputItem, element: HTMLElement, signal: AbortSignal): Promise { - const renderers = Array.from(this._renderers.values()) - .filter((renderer) => renderer.data.mimeTypes.includes(info.mime) && !renderer.data.extends); + public async render(info: rendererApi.OutputItem, preferredRendererId: string | undefined, element: HTMLElement, signal: AbortSignal): Promise { + let renderer: Renderer | undefined; - if (!renderers.length) { + if (typeof preferredRendererId === 'string') { + renderer = Array.from(this._renderers.values()) + .find((renderer) => renderer.data.id === preferredRendererId); + } else { + const renderers = Array.from(this._renderers.values()) + .filter((renderer) => renderer.data.mimeTypes.includes(info.mime) && !renderer.data.entrypoint.extends); + + if (renderers.length) { + // De-prioritize built-in renderers + renderers.sort((a, b) => +a.data.isBuiltin - +b.data.isBuiltin); + + // Use first renderer we find in sorted list + renderer = renderers[0]; + } + } + + if (renderer) { + await renderer.renderOutputItem(info, element, signal); + } else { const errorContainer = document.createElement('div'); const error = document.createElement('div'); @@ -1509,15 +1524,7 @@ async function webviewPreloads(ctx: PreloadContext) { element.innerText = ''; element.appendChild(errorContainer); - - return; } - - // De-prioritize built-in renderers - renderers.sort((a, b) => +a.data.isBuiltin - +b.data.isBuiltin); - - // Use first renderer we find in sorted list - await renderers[0].renderOutputItem(info, element, signal); } }(); @@ -1874,7 +1881,7 @@ async function webviewPreloads(ctx: PreloadContext) { const controller = new AbortController(); this.renderTaskAbort = controller; try { - await renderers.render(this.outputItem, this.element, this.renderTaskAbort.signal); + await renderers.render(this.outputItem, undefined, this.element, this.renderTaskAbort.signal); } finally { if (this.renderTaskAbort === controller) { this.renderTaskAbort = undefined; @@ -2001,7 +2008,7 @@ async function webviewPreloads(ctx: PreloadContext) { public async renderOutputElement(data: webviewMessages.ICreationRequestMessage, preloadErrors: ReadonlyArray, signal: AbortSignal) { const outputElement = this.createOutputElement(data); - await outputElement.render(data.content, preloadErrors, signal); + await outputElement.render(data.content, data.rendererId, preloadErrors, signal); // don't hide until after this step so that the height is right outputElement.element.style.visibility = data.initiallyHidden ? 'hidden' : ''; @@ -2117,6 +2124,10 @@ async function webviewPreloads(ctx: PreloadContext) { type: 'initialized' }); + for (const preload of ctx.staticPreloadsData) { + kernelPreloads.load(preload.entrypoint, preload.entrypoint, true); + } + function postNotebookMessage( type: T['type'], properties: Omit @@ -2132,6 +2143,7 @@ async function webviewPreloads(ctx: PreloadContext) { public readonly element: HTMLElement; private _content?: { readonly content: webviewMessages.ICreationContent; + readonly preferredRendererId: string | undefined; readonly preloadErrors: ReadonlyArray; }; private hasResizeObserver = false; @@ -2164,11 +2176,11 @@ async function webviewPreloads(ctx: PreloadContext) { this.renderTaskAbort = undefined; } - public async render(content: webviewMessages.ICreationContent, preloadErrors: ReadonlyArray, signal?: AbortSignal) { + public async render(content: webviewMessages.ICreationContent, preferredRendererId: string | undefined, preloadErrors: ReadonlyArray, signal?: AbortSignal) { this.renderTaskAbort?.abort(); this.renderTaskAbort = undefined; - this._content = { content, preloadErrors }; + this._content = { content, preferredRendererId, preloadErrors }; if (content.type === 0 /* RenderOutputType.Html */) { const trustedHtml = ttPolicy?.createHTML(content.htmlContent) ?? content.htmlContent; this.element.innerHTML = trustedHtml as string; @@ -2186,7 +2198,7 @@ async function webviewPreloads(ctx: PreloadContext) { signal?.addEventListener('abort', () => controller.abort()); try { - await renderers.render(item, this.element, controller.signal); + await renderers.render(item, preferredRendererId, this.element, controller.signal); } finally { if (this.renderTaskAbort === controller) { this.renderTaskAbort = undefined; @@ -2229,13 +2241,13 @@ async function webviewPreloads(ctx: PreloadContext) { public rerender() { if (this._content) { - this.render(this._content.content, this._content.preloadErrors); + this.render(this._content.content, this._content.preferredRendererId, this._content.preloadErrors); } } public updateAndRerender(content: webviewMessages.ICreationContent) { if (this._content) { - this._content = { content, preloadErrors: this._content.preloadErrors }; + this._content = { content, preferredRendererId: this._content.preferredRendererId, preloadErrors: this._content.preloadErrors }; this.rerender(); } } @@ -2345,11 +2357,12 @@ async function webviewPreloads(ctx: PreloadContext) { }(); } -export function preloadsScriptStr(styleValues: PreloadStyles, options: PreloadOptions, renderers: readonly webviewMessages.RendererMetadata[], isWorkspaceTrusted: boolean, lineLimit: number, nonce: string) { +export function preloadsScriptStr(styleValues: PreloadStyles, options: PreloadOptions, renderers: readonly webviewMessages.RendererMetadata[], preloads: readonly webviewMessages.StaticPreloadMetadata[], isWorkspaceTrusted: boolean, lineLimit: number, nonce: string) { const ctx: PreloadContext = { style: styleValues, options, rendererData: renderers, + staticPreloadsData: preloads, isWorkspaceTrusted, lineLimit, nonce, diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts index f48c7cb938b..18abfa18c17 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts @@ -306,16 +306,11 @@ export class NotebookEditorToolbar extends Disposable { this._secondaryActions = []; this._buildBody(); - this._register(this.editorService.onDidActiveEditorChange(() => { - if (this.editorService.activeEditorPane?.getId() === NOTEBOOK_EDITOR_ID) { - const notebookEditor = this.editorService.activeEditorPane.getControl() as INotebookEditorDelegate; - if (notebookEditor === this.notebookEditor) { - // this is the active editor - this._showNotebookActionsinEditorToolbar(); - return; - } - } - })); + this._register(Event.debounce( + this.editorService.onDidActiveEditorChange, + (last, _current) => last, + 200 + )(this._updatePerEditorChange, this)); this._registerNotebookActionsToolbar(); } @@ -338,6 +333,17 @@ export class NotebookEditorToolbar extends Disposable { DOM.append(this.domNode, this._notebookTopRightToolbarContainer); } + private _updatePerEditorChange() { + if (this.editorService.activeEditorPane?.getId() === NOTEBOOK_EDITOR_ID) { + const notebookEditor = this.editorService.activeEditorPane.getControl() as INotebookEditorDelegate; + if (notebookEditor === this.notebookEditor) { + // this is the active editor + this._showNotebookActionsinEditorToolbar(); + return; + } + } + } + private _registerNotebookActionsToolbar() { this._notebookGlobalActionsMenu = this._register(this.menuService.createMenu(this.notebookEditor.creationOptions.menuIds.notebookToolbar, this.contextKeyService)); this._register(this._notebookGlobalActionsMenu); diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorWidgetContextKeys.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorWidgetContextKeys.ts index f8e26930397..7ee5fba6af6 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorWidgetContextKeys.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorWidgetContextKeys.ts @@ -136,7 +136,7 @@ export class NotebookEditorContextKeys { private _updateForCellExecution(): void { if (this._editor.textModel) { - const notebookExe = this._notebookExecutionStateService.getCellExecutionStatesForNotebook(this._editor.textModel.uri); + const notebookExe = this._notebookExecutionStateService.getCellExecutionsForNotebook(this._editor.textModel.uri); this._someCellRunning.set(notebookExe.length > 0); } else { this._someCellRunning.set(false); @@ -169,7 +169,7 @@ export class NotebookEditorContextKeys { } const { selected, all } = this._notebookKernelService.getMatchingKernel(this._editor.textModel); - const sourceActions = this._notebookKernelService.getSourceActions(); + const sourceActions = this._notebookKernelService.getSourceActions(this._editor.textModel, this._editor.scopedContextKeyService); this._notebookKernelCount.set(all.length); this._notebookKernelSourceCount.set(sourceActions.length); this._interruptibleKernel.set(selected?.implementsInterrupt ?? false); diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts index c461cd64a72..cf027f3af32 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts @@ -13,6 +13,7 @@ import { INotebookKernel, INotebookKernelMatchResult, INotebookKernelService, IS import { Event } from 'vs/base/common/event'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; export class NotebooKernelActionViewItem extends ActionViewItem { @@ -20,7 +21,7 @@ export class NotebooKernelActionViewItem extends ActionViewItem { constructor( actualAction: IAction, - private readonly _editor: { onDidChangeModel: Event; textModel: NotebookTextModel | undefined } | INotebookEditor, + private readonly _editor: { onDidChangeModel: Event; textModel: NotebookTextModel | undefined; scopedContextKeyService?: IContextKeyService } | INotebookEditor, @INotebookKernelService private readonly _notebookKernelService: INotebookKernelService, ) { super( @@ -59,7 +60,7 @@ export class NotebooKernelActionViewItem extends ActionViewItem { return; } - const runningActions = this._notebookKernelService.getRunningSourceActions(); + const runningActions = this._notebookKernelService.getRunningSourceActions(notebook); if (runningActions.length) { return this._updateActionFromSourceAction(runningActions[0] /** TODO handle multiple actions state */, true); } @@ -82,7 +83,7 @@ export class NotebooKernelActionViewItem extends ActionViewItem { private _updateActionsFromSourceActions() { this._action.enabled = true; - const sourceActions = this._notebookKernelService.getSourceActions(); + const sourceActions = this._editor.textModel ? this._notebookKernelService.getSourceActions(this._editor.textModel, this._editor.scopedContextKeyService) : []; if (sourceActions.length === 1) { // exact one action this._updateActionFromSourceAction(sourceActions[0], false); diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts index 7890d272892..e8576c8234a 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts @@ -16,7 +16,7 @@ import { TextModel } from 'vs/editor/common/model/textModel'; import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { NotebookCellOutputTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellOutputTextModel'; -import { CellInternalMetadataChangedEvent, CellKind, ICell, ICellDto2, ICellOutput, IOutputDto, IOutputItemDto, NotebookCellCollapseState, NotebookCellInternalMetadata, NotebookCellMetadata, NotebookCellOutputsSplice, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellInternalMetadataChangedEvent, CellKind, compressOutputItemStreams, ICell, ICellDto2, ICellOutput, IOutputDto, IOutputItemDto, isTextStreamMime, NotebookCellCollapseState, NotebookCellInternalMetadata, NotebookCellMetadata, NotebookCellOutputsSplice, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; export class NotebookCellTextModel extends Disposable implements ICell { private readonly _onDidChangeOutputs = this._register(new Emitter()); @@ -297,6 +297,31 @@ export class NotebookCellTextModel extends Disposable implements ICell { } else { output.replaceData(items); } + if (output.outputs.length > 1 && output.outputs.every(item => isTextStreamMime(item.mime))) { + // Look for the mimes in the items, and keep track of their order. + // Merge the streams into one output item, per mime type. + const mimeOutputs = new Map(); + const mimeTypes: string[] = []; + output.outputs.forEach(item => { + let items: Uint8Array[]; + if (mimeOutputs.has(item.mime)) { + items = mimeOutputs.get(item.mime)!; + } else { + items = []; + mimeOutputs.set(item.mime, items); + mimeTypes.push(item.mime); + } + items.push(item.data.buffer); + }); + output.outputs.length = 0; + mimeTypes.forEach(mime => { + const compressed = compressOutputItemStreams(mimeOutputs.get(mime)!); + output.outputs.push({ + mime, + data: compressed + }); + }); + } this._onDidChangeOutputItems.fire(); return true; diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index ab73eaf88b9..13c5beb2320 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -75,7 +75,7 @@ export const RENDERER_EQUIVALENT_EXTENSIONS: ReadonlyMap; - extensionLocation: URI; - extensionId: ExtensionIdentifier; - messaging: RendererMessagingSpec; + readonly id: string; + readonly displayName: string; + readonly entrypoint: NotebookRendererEntrypoint; + readonly extensionLocation: URI; + readonly extensionId: ExtensionIdentifier; + readonly messaging: RendererMessagingSpec; readonly mimeTypes: readonly string[]; - readonly dependencies: readonly string[]; - readonly isBuiltin: boolean; matchesWithoutKernel(mimeType: string): NotebookRendererMatch; matches(mimeType: string, kernelProvides: ReadonlyArray): NotebookRendererMatch; } +export interface INotebookStaticPreloadInfo { + readonly type: string; + readonly entrypoint: URI; + readonly extensionLocation: URI; +} export interface IOrderedMimeType { mimeType: string; @@ -951,9 +954,98 @@ export interface NotebookExtensionDescription { } /** - * Whether the provided mime type is a text streamn like `stdout`, `stderr`. + * Whether the provided mime type is a text stream like `stdout`, `stderr`. */ export function isTextStreamMime(mimeType: string) { - return ['application/vnd.code.notebook.stdout', 'application/x.notebook.stdout', 'application/x.notebook.stream', 'application/vnd.code.notebook.stderr', 'application/x.notebook.stderr'].includes(mimeType); + return ['application/vnd.code.notebook.stdout', 'application/vnd.code.notebook.stderr'].includes(mimeType); } + +const textDecoder = new TextDecoder(); + +/** + * Given a stream of individual stdout outputs, this function will return the compressed lines, escaping some of the common terminal escape codes. + * E.g. some terminal escape codes would result in the previous line getting cleared, such if we had 3 lines and + * last line contained such a code, then the result string would be just the first two lines. + */ +export function compressOutputItemStreams(outputs: Uint8Array[]) { + const buffers: Uint8Array[] = []; + let startAppending = false; + + // Pick the first set of outputs with the same mime type. + for (const output of outputs) { + if ((buffers.length === 0 || startAppending)) { + buffers.push(output); + startAppending = true; + } + } + compressStreamBuffer(buffers); + return formatStreamText(VSBuffer.concat(buffers.map(buffer => VSBuffer.wrap(buffer)))); +} +const MOVE_CURSOR_1_LINE_COMMAND = `${String.fromCharCode(27)}[A`; +const MOVE_CURSOR_1_LINE_COMMAND_BYTES = MOVE_CURSOR_1_LINE_COMMAND.split('').map(c => c.charCodeAt(0)); +const LINE_FEED = 10; +function compressStreamBuffer(streams: Uint8Array[]) { + streams.forEach((stream, index) => { + if (index === 0 || stream.length < MOVE_CURSOR_1_LINE_COMMAND.length) { + return; + } + + const previousStream = streams[index - 1]; + + // Remove the previous line if required. + const command = stream.subarray(0, MOVE_CURSOR_1_LINE_COMMAND.length); + if (command[0] === MOVE_CURSOR_1_LINE_COMMAND_BYTES[0] && command[1] === MOVE_CURSOR_1_LINE_COMMAND_BYTES[1] && command[2] === MOVE_CURSOR_1_LINE_COMMAND_BYTES[2]) { + const lastIndexOfLineFeed = previousStream.lastIndexOf(LINE_FEED); + if (lastIndexOfLineFeed === -1) { + return; + } + streams[index - 1] = previousStream.subarray(0, lastIndexOfLineFeed); + streams[index] = stream.subarray(MOVE_CURSOR_1_LINE_COMMAND.length); + } + }); +} + + + +/** + * Took this from jupyter/notebook + * https://github.com/jupyter/notebook/blob/b8b66332e2023e83d2ee04f83d8814f567e01a4e/notebook/static/base/js/utils.js + * Remove characters that are overridden by backspace characters + */ +function fixBackspace(txt: string) { + let tmp = txt; + do { + txt = tmp; + // Cancel out anything-but-newline followed by backspace + tmp = txt.replace(/[^\n]\x08/gm, ''); + } while (tmp.length < txt.length); + return txt; +} + +/** + * Remove chunks that should be overridden by the effect of carriage return characters + * From https://github.com/jupyter/notebook/blob/master/notebook/static/base/js/utils.js + */ +function fixCarriageReturn(txt: string) { + txt = txt.replace(/\r+\n/gm, '\n'); // \r followed by \n --> newline + while (txt.search(/\r[^$]/g) > -1) { + const base = txt.match(/^(.*)\r+/m)![1]; + let insert = txt.match(/\r+(.*)$/m)![1]; + insert = insert + base.slice(insert.length, base.length); + txt = txt.replace(/\r+.*$/m, '\r').replace(/^.*\r/m, insert); + } + return txt; +} + +const BACKSPACE_CHARACTER = '\b'.charCodeAt(0); +const CARRIAGE_RETURN_CHARACTER = '\r'.charCodeAt(0); +function formatStreamText(buffer: VSBuffer): VSBuffer { + // We have special handling for backspace and carriage return characters. + // Don't unnecessary decode the bytes if we don't need to perform any processing. + if (!buffer.buffer.includes(BACKSPACE_CHARACTER) && !buffer.buffer.includes(CARRIAGE_RETURN_CHARACTER)) { + return buffer; + } + // Do the same thing jupyter is doing + return VSBuffer.fromString(fixCarriageReturn(fixBackspace(textDecoder.decode(buffer.buffer)))); +} diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts index 362123693b2..34479ba1115 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts @@ -473,7 +473,7 @@ export class SimpleNotebookEditorModel extends EditorModel implements INotebookE } override isResolved(): this is IResolvedNotebookEditorModel { - return Boolean(this._workingCopy); + return Boolean(this._workingCopy?.model?.notebookModel); } isDirty(): boolean { diff --git a/src/vs/workbench/contrib/notebook/common/notebookExecutionService.ts b/src/vs/workbench/contrib/notebook/common/notebookExecutionService.ts index 1a3b6ac9a94..b2c18f4f6b1 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookExecutionService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookExecutionService.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { INotebookTextModel, IOutputDto, IOutputItemDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; @@ -32,7 +33,7 @@ export const INotebookExecutionService = createDecorator): Promise; + executeNotebookCells(notebook: INotebookTextModel, cells: Iterable, contextKeyService: IContextKeyService): Promise; cancelNotebookCells(notebook: INotebookTextModel, cells: Iterable): Promise; cancelNotebookCellHandles(notebook: INotebookTextModel, cells: Iterable): Promise; } diff --git a/src/vs/workbench/contrib/notebook/common/notebookExecutionStateService.ts b/src/vs/workbench/contrib/notebook/common/notebookExecutionStateService.ts index 996364b1eb0..19660330113 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookExecutionStateService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookExecutionStateService.ts @@ -60,7 +60,8 @@ export interface INotebookExecutionStateService { onDidChangeLastRunFailState: Event; forceCancelNotebookExecutions(notebookUri: URI): void; - getCellExecutionStatesForNotebook(notebook: URI): INotebookCellExecution[]; + getCellExecutionsForNotebook(notebook: URI): INotebookCellExecution[]; + getCellExecutionsByHandleForNotebook(notebook: URI): Map | undefined; getCellExecution(cellUri: URI): INotebookCellExecution | undefined; createCellExecution(notebook: URI, cellHandle: number): INotebookCellExecution; getLastFailedCellForNotebook(notebook: URI): number | undefined; diff --git a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts index e674058c02b..81cf910e50c 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts @@ -7,6 +7,7 @@ import { IAction } from 'vs/base/common/actions'; import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -74,6 +75,10 @@ export interface ISourceAction { runAction: () => Promise; } +export interface INotebookSourceActionChangeEvent { + notebook: URI; +} + export interface INotebookTextModelLike { uri: URI; viewType: string } export const INotebookKernelService = createDecorator('INotebookKernelService'); @@ -111,8 +116,8 @@ export interface INotebookKernelService { updateKernelNotebookAffinity(kernel: INotebookKernel, notebook: URI, preference: number | undefined): void; //#region Kernel source actions - readonly onDidChangeSourceActions: Event; - getSourceActions(): ISourceAction[]; - getRunningSourceActions(): ISourceAction[]; + readonly onDidChangeSourceActions: Event; + getSourceActions(notebook: INotebookTextModelLike, contextKeyService: IContextKeyService | undefined): ISourceAction[]; + getRunningSourceActions(notebook: INotebookTextModelLike): ISourceAction[]; //#endregion } diff --git a/src/vs/workbench/contrib/notebook/common/notebookOutputRenderer.ts b/src/vs/workbench/contrib/notebook/common/notebookOutputRenderer.ts index e22c7fd486f..8400da97fa0 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookOutputRenderer.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookOutputRenderer.ts @@ -8,7 +8,7 @@ import { Iterable } from 'vs/base/common/iterator'; import { joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { INotebookRendererInfo, NotebookRendererEntrypoint, NotebookRendererMatch, RendererMessagingSpec } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookRendererInfo, ContributedNotebookRendererEntrypoint, NotebookRendererMatch, RendererMessagingSpec, NotebookRendererEntrypoint, INotebookStaticPreloadInfo as INotebookStaticPreloadInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon'; class DependencyList { private readonly value: ReadonlySet; @@ -19,10 +19,6 @@ class DependencyList { this.defined = this.value.size > 0; } - public values(): string[] { - return Array.from(this.value); - } - /** Gets whether any of the 'available' dependencies match the ones in this list */ public matches(available: ReadonlyArray) { // For now this is simple, but this may expand to support globs later @@ -34,17 +30,13 @@ class DependencyList { export class NotebookOutputRendererInfo implements INotebookRendererInfo { readonly id: string; - readonly extends?: string; - readonly entrypoint: URI; + readonly entrypoint: NotebookRendererEntrypoint; readonly displayName: string; readonly extensionLocation: URI; readonly extensionId: ExtensionIdentifier; readonly hardDependencies: DependencyList; readonly optionalDependencies: DependencyList; - /** @see RendererMessagingSpec */ readonly messaging: RendererMessagingSpec; - // todo: re-add preloads in pure renderer API - readonly preloads: ReadonlyArray = []; readonly mimeTypes: readonly string[]; private readonly mimeTypeGlobs: glob.ParsedPattern[]; @@ -54,7 +46,7 @@ export class NotebookOutputRendererInfo implements INotebookRendererInfo { constructor(descriptor: { readonly id: string; readonly displayName: string; - readonly entrypoint: NotebookRendererEntrypoint; + readonly entrypoint: ContributedNotebookRendererEntrypoint; readonly mimeTypes: readonly string[]; readonly extension: IExtensionDescription; readonly dependencies: readonly string[] | undefined; @@ -67,10 +59,15 @@ export class NotebookOutputRendererInfo implements INotebookRendererInfo { this.isBuiltin = descriptor.extension.isBuiltin; if (typeof descriptor.entrypoint === 'string') { - this.entrypoint = joinPath(this.extensionLocation, descriptor.entrypoint); + this.entrypoint = { + extends: undefined, + path: joinPath(this.extensionLocation, descriptor.entrypoint) + }; } else { - this.extends = descriptor.entrypoint.extends; - this.entrypoint = joinPath(this.extensionLocation, descriptor.entrypoint.path); + this.entrypoint = { + extends: descriptor.entrypoint.extends, + path: joinPath(this.extensionLocation, descriptor.entrypoint.path) + }; } this.displayName = descriptor.displayName; @@ -81,10 +78,6 @@ export class NotebookOutputRendererInfo implements INotebookRendererInfo { this.messaging = descriptor.requiresMessaging ?? RendererMessagingSpec.Never; } - public get dependencies(): string[] { - return this.hardDependencies.values(); - } - public matchesWithoutKernel(mimeType: string) { if (!this.matchesMimeTypeOnly(mimeType)) { return NotebookRendererMatch.Never; @@ -118,10 +111,28 @@ export class NotebookOutputRendererInfo implements INotebookRendererInfo { } private matchesMimeTypeOnly(mimeType: string) { - if (this.extends !== undefined) { + if (this.entrypoint.extends) { // We're extending another renderer return false; } return this.mimeTypeGlobs.some(pattern => pattern(mimeType)) || this.mimeTypes.some(pattern => pattern === mimeType); } } + +export class NotebookStaticPreloadInfo implements INotebookStaticPreloadInfo { + + readonly type: string; + readonly entrypoint: URI; + readonly extensionLocation: URI; + + constructor(descriptor: { + readonly type: string; + readonly entrypoint: string; + readonly extension: IExtensionDescription; + }) { + this.type = descriptor.type; + + this.entrypoint = joinPath(descriptor.extension.extensionLocation, descriptor.entrypoint); + this.extensionLocation = descriptor.extension.extensionLocation; + } +} diff --git a/src/vs/workbench/contrib/notebook/common/notebookService.ts b/src/vs/workbench/contrib/notebook/common/notebookService.ts index f67d9be987d..d599600383a 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookService.ts @@ -7,7 +7,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { URI } from 'vs/base/common/uri'; import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { Event } from 'vs/base/common/event'; -import { INotebookRendererInfo, NotebookData, TransientOptions, IOrderedMimeType, IOutputDto, INotebookContributionData, NotebookExtensionDescription } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookRendererInfo, NotebookData, TransientOptions, IOrderedMimeType, IOutputDto, INotebookContributionData, NotebookExtensionDescription, INotebookStaticPreloadInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { CancellationToken } from 'vs/base/common/cancellation'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; @@ -73,6 +73,8 @@ export interface INotebookService { getRendererInfo(id: string): INotebookRendererInfo | undefined; getRenderers(): INotebookRendererInfo[]; + getStaticPreloads(viewType: string): Iterable; + /** Updates the preferred renderer for the given mimetype in the workspace. */ updateMimePreferredRenderer(viewType: string, mimeType: string, rendererId: string, otherMimetypes: readonly string[]): void; saveMimeDisplayOrder(target: ConfigurationTarget): void; diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts index e8f8a86798b..dd524a0eda0 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts @@ -72,20 +72,22 @@ suite('NotebookExecutionStateService', () => { return _withTestNotebook(cells, (editor, viewModel) => callback(viewModel, viewModel.notebookDocument)); } - test('cancel execution when cell is deleted', async function () { // TODO@roblou Should be a test for NotebookExecutionListeners, which can be a standalone contribution + function testCancelOnDelete(expectedCancels: number, implementsInterrupt: boolean) { return withTestNotebook([], async viewModel => { testNotebookModel = viewModel.notebookDocument; - let didCancel = false; + let cancels = 0; const kernel = new class extends TestNotebookKernel { + implementsInterrupt = implementsInterrupt; + constructor() { super({ languages: ['javascript'] }); } override async executeNotebookCellsRequest(): Promise { } - override async cancelNotebookCellExecution(): Promise { - didCancel = true; + override async cancelNotebookCellExecution(_uri: URI, handles: number[]): Promise { + cancels += handles.length; } }; kernelService.registerKernel(kernel); @@ -93,14 +95,33 @@ suite('NotebookExecutionStateService', () => { const executionStateService: INotebookExecutionStateService = instantiationService.get(INotebookExecutionStateService); + // Should cancel executing and pending cells, when kernel does not implement interrupt const cell = insertCellAtIndex(viewModel, 0, 'var c = 3', 'javascript', CellKind.Code, {}, [], true, true); - executionStateService.createCellExecution(viewModel.uri, cell.handle); - assert.strictEqual(didCancel, false); + const cell2 = insertCellAtIndex(viewModel, 1, 'var c = 3', 'javascript', CellKind.Code, {}, [], true, true); + const cell3 = insertCellAtIndex(viewModel, 2, 'var c = 3', 'javascript', CellKind.Code, {}, [], true, true); + insertCellAtIndex(viewModel, 3, 'var c = 3', 'javascript', CellKind.Code, {}, [], true, true); // Not deleted + const exe = executionStateService.createCellExecution(viewModel.uri, cell.handle); // Executing + exe.confirm(); + exe.update([{ editType: CellExecutionUpdateType.ExecutionState, executionOrder: 1 }]); + const exe2 = executionStateService.createCellExecution(viewModel.uri, cell2.handle); // Pending + exe2.confirm(); + executionStateService.createCellExecution(viewModel.uri, cell3.handle); // Unconfirmed + assert.strictEqual(cancels, 0); viewModel.notebookDocument.applyEdits([{ - editType: CellEditType.Replace, index: 0, count: 1, cells: [] + editType: CellEditType.Replace, index: 0, count: 3, cells: [] }], true, undefined, () => undefined, undefined, false); - assert.strictEqual(didCancel, true); + assert.strictEqual(cancels, expectedCancels); }); + + } + + // TODO@roblou Could be a test just for NotebookExecutionListeners, which can be a standalone contribution + test('cancel execution when cell is deleted', async function () { + return testCancelOnDelete(3, false); + }); + + test('cancel execution when cell is deleted in interrupt-type kernel', async function () { + return testCancelOnDelete(1, true); }); test('fires onDidChangeCellExecution when cell is completed while deleted', async function () { @@ -219,7 +240,7 @@ class TestNotebookKernel implements INotebookKernel { preloadProvides: string[] = []; supportedLanguages: string[] = []; async executeNotebookCellsRequest(): Promise { } - async cancelNotebookCellExecution(): Promise { } + async cancelNotebookCellExecution(uri: URI, cellHandles: number[]): Promise { } constructor(opts?: { languages?: string[]; id?: string }) { this.supportedLanguages = opts?.languages ?? [PLAINTEXT_LANGUAGE_ID]; diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookTextModel.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookTextModel.test.ts index 455d706b637..4da03a9f248 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookTextModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookTextModel.test.ts @@ -1043,4 +1043,110 @@ suite('NotebookTextModel', () => { assert.equal(model.cells[0].outputs[0].outputs[0].data.toString(), '_World_'); }); }); + test('Append multiple text/plain output items', async function () { + await withTestNotebook([ + ['var a = 1;', 'javascript', CellKind.Code, [{ + outputId: '1', + outputs: [{ mime: 'text/plain', data: valueBytesFromString('foo') }] + }], {}] + ], (editor) => { + const model = editor.textModel; + const edits: ICellEditOperation[] = [ + { + editType: CellEditType.OutputItems, + outputId: '1', + append: true, + items: [{ mime: 'text/plain', data: VSBuffer.fromString('bar') }, { mime: 'text/plain', data: VSBuffer.fromString('baz') }] + } + ]; + model.applyEdits(edits, true, undefined, () => undefined, undefined, true); + assert.equal(model.cells.length, 1); + assert.equal(model.cells[0].outputs.length, 1); + assert.equal(model.cells[0].outputs[0].outputs.length, 3); + assert.equal(model.cells[0].outputs[0].outputs[0].mime, 'text/plain'); + assert.equal(model.cells[0].outputs[0].outputs[0].data.toString(), 'foo'); + assert.equal(model.cells[0].outputs[0].outputs[1].mime, 'text/plain'); + assert.equal(model.cells[0].outputs[0].outputs[1].data.toString(), 'bar'); + assert.equal(model.cells[0].outputs[0].outputs[2].mime, 'text/plain'); + assert.equal(model.cells[0].outputs[0].outputs[2].data.toString(), 'baz'); + }); + }); + test('Append multiple stdout stream output items to an output with another mime', async function () { + await withTestNotebook([ + ['var a = 1;', 'javascript', CellKind.Code, [{ + outputId: '1', + outputs: [{ mime: 'text/plain', data: valueBytesFromString('foo') }] + }], {}] + ], (editor) => { + const model = editor.textModel; + const edits: ICellEditOperation[] = [ + { + editType: CellEditType.OutputItems, + outputId: '1', + append: true, + items: [{ mime: 'application/vnd.code.notebook.stdout', data: VSBuffer.fromString('bar') }, { mime: 'application/vnd.code.notebook.stdout', data: VSBuffer.fromString('baz') }] + } + ]; + model.applyEdits(edits, true, undefined, () => undefined, undefined, true); + assert.equal(model.cells.length, 1); + assert.equal(model.cells[0].outputs.length, 1); + assert.equal(model.cells[0].outputs[0].outputs.length, 3); + assert.equal(model.cells[0].outputs[0].outputs[0].mime, 'text/plain'); + assert.equal(model.cells[0].outputs[0].outputs[0].data.toString(), 'foo'); + assert.equal(model.cells[0].outputs[0].outputs[1].mime, 'application/vnd.code.notebook.stdout'); + assert.equal(model.cells[0].outputs[0].outputs[1].data.toString(), 'bar'); + assert.equal(model.cells[0].outputs[0].outputs[2].mime, 'application/vnd.code.notebook.stdout'); + assert.equal(model.cells[0].outputs[0].outputs[2].data.toString(), 'baz'); + }); + }); + test('Compress multiple stdout stream output items', async function () { + await withTestNotebook([ + ['var a = 1;', 'javascript', CellKind.Code, [{ + outputId: '1', + outputs: [{ mime: 'application/vnd.code.notebook.stdout', data: valueBytesFromString('foo') }] + }], {}] + ], (editor) => { + const model = editor.textModel; + const edits: ICellEditOperation[] = [ + { + editType: CellEditType.OutputItems, + outputId: '1', + append: true, + items: [{ mime: 'application/vnd.code.notebook.stdout', data: VSBuffer.fromString('bar') }, { mime: 'application/vnd.code.notebook.stdout', data: VSBuffer.fromString('baz') }] + } + ]; + model.applyEdits(edits, true, undefined, () => undefined, undefined, true); + assert.equal(model.cells.length, 1); + assert.equal(model.cells[0].outputs.length, 1); + assert.equal(model.cells[0].outputs[0].outputs.length, 1); + assert.equal(model.cells[0].outputs[0].outputs[0].mime, 'application/vnd.code.notebook.stdout'); + assert.equal(model.cells[0].outputs[0].outputs[0].data.toString(), 'foobarbaz'); + }); + + }); + test('Compress multiple stderr stream output items', async function () { + await withTestNotebook([ + ['var a = 1;', 'javascript', CellKind.Code, [{ + outputId: '1', + outputs: [{ mime: 'application/vnd.code.notebook.stderr', data: valueBytesFromString('foo') }] + }], {}] + ], (editor) => { + const model = editor.textModel; + const edits: ICellEditOperation[] = [ + { + editType: CellEditType.OutputItems, + outputId: '1', + append: true, + items: [{ mime: 'application/vnd.code.notebook.stderr', data: VSBuffer.fromString('bar') }, { mime: 'application/vnd.code.notebook.stderr', data: VSBuffer.fromString('baz') }] + } + ]; + model.applyEdits(edits, true, undefined, () => undefined, undefined, true); + assert.equal(model.cells.length, 1); + assert.equal(model.cells[0].outputs.length, 1); + assert.equal(model.cells[0].outputs[0].outputs.length, 1); + assert.equal(model.cells[0].outputs[0].outputs[0].mime, 'application/vnd.code.notebook.stderr'); + assert.equal(model.cells[0].outputs[0].outputs[0].data.toString(), 'foobarbaz'); + }); + + }); }); diff --git a/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts index 718588d06fe..649d20e9001 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts @@ -413,11 +413,6 @@ class TestCellExecution implements INotebookCellExecution { } class TestNotebookExecutionStateService implements INotebookExecutionStateService { - - getLastFailedCellForNotebook(notebook: URI): number | undefined { - return; - } - _serviceBrand: undefined; private _executions = new ResourceMap(); @@ -428,7 +423,7 @@ class TestNotebookExecutionStateService implements INotebookExecutionStateServic forceCancelNotebookExecutions(notebookUri: URI): void { } - getCellExecutionStatesForNotebook(notebook: URI): INotebookCellExecution[] { + getCellExecutionsForNotebook(notebook: URI): INotebookCellExecution[] { return []; } @@ -442,4 +437,12 @@ class TestNotebookExecutionStateService implements INotebookExecutionStateServic this._executions.set(CellUri.generate(notebook, cellHandle), exe); return exe; } + + getCellExecutionsByHandleForNotebook(notebook: URI): Map | undefined { + return; + } + + getLastFailedCellForNotebook(notebook: URI): number | undefined { + return; + } } diff --git a/src/vs/workbench/contrib/offline/browser/offline.contribution.ts b/src/vs/workbench/contrib/offline/browser/offline.contribution.ts index 470eaf250e3..75b1671e327 100644 --- a/src/vs/workbench/contrib/offline/browser/offline.contribution.ts +++ b/src/vs/workbench/contrib/offline/browser/offline.contribution.ts @@ -94,4 +94,4 @@ export class OfflineStatusBarController implements IWorkbenchContribution { } Registry.as(Extensions.Workbench) - .registerWorkbenchContribution(OfflineStatusBarController, 'OfflineStatusBarController', LifecyclePhase.Restored); + .registerWorkbenchContribution(OfflineStatusBarController, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/outline/browser/outline.contribution.ts b/src/vs/workbench/contrib/outline/browser/outline.contribution.ts index 5b512f1e3c3..3492875c13d 100644 --- a/src/vs/workbench/contrib/outline/browser/outline.contribution.ts +++ b/src/vs/workbench/contrib/outline/browser/outline.contribution.ts @@ -40,22 +40,36 @@ Registry.as(ConfigurationExtensions.Configuration).regis 'type': 'object', 'properties': { [OutlineConfigKeys.icons]: { - 'description': localize('outline.showIcons', "Render Outline Elements with Icons."), + 'description': localize('outline.showIcons', "Render outline elements with icons."), 'type': 'boolean', 'default': true }, + [OutlineConfigKeys.collapseItems]: { + 'description': localize('outline.initialState', "Controls whether outline items are collapsed or expanded."), + 'type': 'string', + scope: ConfigurationScope.LANGUAGE_OVERRIDABLE, + 'enum': [ + 'alwaysCollapse', + 'alwaysExpand' + ], + 'enumDescriptions': [ + localize('outline.initialState.collapsed', "Collapse all items."), + localize('outline.initialState.expanded', "Expand all items.") + ], + 'default': 'alwaysExpand' + }, [OutlineConfigKeys.problemsEnabled]: { - 'description': localize('outline.showProblem', "Show Errors & Warnings on Outline Elements."), + 'description': localize('outline.showProblem', "Show errors and warnings on outline elements."), 'type': 'boolean', 'default': true }, [OutlineConfigKeys.problemsColors]: { - 'description': localize('outline.problem.colors', "Use colors for Errors & Warnings."), + 'description': localize('outline.problem.colors', "Use colors for errors and warnings on outline elements."), 'type': 'boolean', 'default': true }, [OutlineConfigKeys.problemsBadges]: { - 'description': localize('outline.problems.badges', "Use badges for Errors & Warnings."), + 'description': localize('outline.problems.badges', "Use badges for errors and warnings on outline elements."), 'type': 'boolean', 'default': true }, diff --git a/src/vs/workbench/contrib/outline/browser/outlinePane.ts b/src/vs/workbench/contrib/outline/browser/outlinePane.ts index d41777b2c54..f9f9bb3d4c2 100644 --- a/src/vs/workbench/contrib/outline/browser/outlinePane.ts +++ b/src/vs/workbench/contrib/outline/browser/outlinePane.ts @@ -36,10 +36,12 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Event } from 'vs/base/common/event'; import { ITreeSorter } from 'vs/base/browser/ui/tree/tree'; import { AbstractTreeViewState, IAbstractTreeViewState, TreeFindMode } from 'vs/base/browser/ui/tree/abstractTree'; +import { URI } from 'vs/base/common/uri'; -const _ctxFollowsCursor = new RawContextKey('outlineFollowsCursor', false); -const _ctxFilterOnType = new RawContextKey('outlineFiltersOnType', false); +const _ctxFollowsCursor = new RawContextKey('outlineFollowsCursor', false); +const _ctxFilterOnType = new RawContextKey('outlineFiltersOnType', false); const _ctxSortMode = new RawContextKey('outlineSortMode', OutlineSortOrder.ByPosition); +const _ctxAllCollapsed = new RawContextKey('outlineAllCollapsed', false); class OutlineTreeSorter implements ITreeSorter { @@ -82,6 +84,7 @@ export class OutlinePane extends ViewPane { private _ctxFollowsCursor!: IContextKey; private _ctxFilterOnType!: IContextKey; private _ctxSortMode!: IContextKey; + private _ctxAllCollapsed!: IContextKey; constructor( options: IViewletViewOptions, @@ -107,6 +110,7 @@ export class OutlinePane extends ViewPane { this._ctxFollowsCursor = _ctxFollowsCursor.bindTo(contextKeyService); this._ctxFilterOnType = _ctxFilterOnType.bindTo(contextKeyService); this._ctxSortMode = _ctxSortMode.bindTo(contextKeyService); + this._ctxAllCollapsed = _ctxAllCollapsed.bindTo(contextKeyService); }); const updateContext = () => { @@ -170,6 +174,10 @@ export class OutlinePane extends ViewPane { this._tree?.collapseAll(); } + expandAll(): void { + this._tree?.expandAll(); + } + get outlineViewState() { return this._outlineViewState; } @@ -180,11 +188,14 @@ export class OutlinePane extends ViewPane { this._message.innerText = message; } - private _captureViewState(): boolean { + private _captureViewState(uri?: URI): boolean { if (this._tree) { const oldOutline = this._tree.getInput(); - if (oldOutline && oldOutline.uri) { - this._treeStates.set(`${oldOutline.outlineKind}/${oldOutline.uri}`, this._tree.getViewState()); + if (!uri) { + uri = oldOutline?.uri; + } + if (oldOutline && uri) { + this._treeStates.set(`${oldOutline.outlineKind}/${uri}`, this._tree.getViewState()); return true; } } @@ -269,13 +280,13 @@ export class OutlinePane extends ViewPane { if (newOutline.isEmpty) { // no more elements this._showMessage(localize('no-symbols', "No symbols found in document '{0}'", basename(resource))); - this._captureViewState(); + this._captureViewState(resource); tree.setInput(undefined); } else if (!tree.getInput()) { // first: init tree this._domNode.classList.remove('message'); - const state = this._treeStates.get(`${newOutline.outlineKind}/${resource}`); + const state = this._treeStates.get(`${newOutline.outlineKind}/${newOutline.uri}`); tree.setInput(newOutline, state && AbstractTreeViewState.lift(state)); } else { @@ -355,7 +366,15 @@ export class OutlinePane extends ViewPane { } })); - // last: set tree property + // feature: update all-collapsed context key + const updateAllCollapsedCtx = () => { + this._ctxAllCollapsed.set(tree.getNode(null).children.every(node => !node.collapsible || node.collapsed)); + }; + this._editorControlDisposables.add(tree.onDidChangeCollapseState(updateAllCollapsedCtx)); + this._editorControlDisposables.add(tree.onDidChangeModel(updateAllCollapsedCtx)); + updateAllCollapsedCtx(); + + // last: set tree property and wire it up to one of our context keys tree.layout(this._treeDimensions?.height, this._treeDimensions?.width); this._tree = tree; this._editorControlDisposables.add(toDisposable(() => { @@ -379,7 +398,7 @@ registerAction2(class Collapse extends ViewAction { menu: { id: MenuId.ViewTitle, group: 'navigation', - when: ContextKeyExpr.equals('view', OutlinePane.Id) + when: ContextKeyExpr.and(ContextKeyExpr.equals('view', OutlinePane.Id), _ctxAllCollapsed.isEqualTo(false)) } }); } @@ -388,6 +407,26 @@ registerAction2(class Collapse extends ViewAction { } }); +registerAction2(class Collapse extends ViewAction { + constructor() { + super({ + viewId: OutlinePane.Id, + id: 'outline.expand', + title: localize('expand', "Expand All"), + f1: false, + icon: Codicon.expandAll, + menu: { + id: MenuId.ViewTitle, + group: 'navigation', + when: ContextKeyExpr.and(ContextKeyExpr.equals('view', OutlinePane.Id), _ctxAllCollapsed.isEqualTo(true)) + } + }); + } + runInView(_accessor: ServicesAccessor, view: OutlinePane) { + view.expandAll(); + } +}); + registerAction2(class FollowCursor extends ViewAction { constructor() { super({ diff --git a/src/vs/workbench/contrib/output/browser/media/output.css b/src/vs/workbench/contrib/output/browser/media/output.css deleted file mode 100644 index ea0ee3e8d6b..00000000000 --- a/src/vs/workbench/contrib/output/browser/media/output.css +++ /dev/null @@ -1,42 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - - -.monaco-workbench .part.sidebar > .title > .title-actions .switch-output, -.monaco-pane-view .pane > .pane-header .monaco-action-bar .switch-output { - border-width: 1px; - border-style: solid; -} - -.part.panel > .title > .title-actions .switch-output > .monaco-select-box { - border-width: 1px; - border-style: solid; -} - -.monaco-workbench .part.sidebar > .title > .title-actions .switch-output { - display: flex; - align-items: center; - font-size: 11px; - margin-right: 0.3em; - height: 20px; - flex-shrink: 1; - margin-top: 7px; -} - -.monaco-workbench.mac .part.sidebar > .title > .title-actions .switch-output { - border-radius: 4px; -} - -.monaco-workbench .part.sidebar > .title > .title-actions .switch-output > .monaco-select-box { - display: block !important; -} - -.monaco-pane-view .pane > .pane-header .monaco-action-bar .switch-output.action-item.select-container { - border: none !important; -} - -.monaco-workbench .part.sidebar > .title > .title-actions .switch-output > .monaco-select-box { - padding: 0 22px 0 6px; -} diff --git a/src/vs/workbench/contrib/output/browser/output.contribution.ts b/src/vs/workbench/contrib/output/browser/output.contribution.ts index d7c89247cde..85f81a2afde 100644 --- a/src/vs/workbench/contrib/output/browser/output.contribution.ts +++ b/src/vs/workbench/contrib/output/browser/output.contribution.ts @@ -5,14 +5,13 @@ import * as nls from 'vs/nls'; import * as aria from 'vs/base/browser/ui/aria/aria'; -import 'vs/css!./media/output'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { ModesRegistry } from 'vs/editor/common/languages/modesRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; -import { MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { MenuId, registerAction2, Action2, MenuRegistry } from 'vs/platform/actions/common/actions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { OutputService, LogContentProvider } from 'vs/workbench/contrib/output/browser/outputServices'; -import { OUTPUT_MODE_ID, OUTPUT_MIME, OUTPUT_VIEW_ID, IOutputService, CONTEXT_IN_OUTPUT, LOG_SCHEME, LOG_MODE_ID, LOG_MIME, CONTEXT_ACTIVE_LOG_OUTPUT, CONTEXT_OUTPUT_SCROLL_LOCK, IOutputChannelDescriptor, IFileOutputChannelDescriptor } from 'vs/workbench/services/output/common/output'; +import { OUTPUT_MODE_ID, OUTPUT_MIME, OUTPUT_VIEW_ID, IOutputService, CONTEXT_IN_OUTPUT, LOG_SCHEME, LOG_MODE_ID, LOG_MIME, CONTEXT_ACTIVE_LOG_OUTPUT, CONTEXT_OUTPUT_SCROLL_LOCK, IOutputChannelDescriptor, IFileOutputChannelDescriptor, ACTIVE_OUTPUT_CHANNEL_CONTEXT, IOutputChannelRegistry, Extensions } from 'vs/workbench/services/output/common/output'; import { OutputViewPane } from 'vs/workbench/contrib/output/browser/outputView'; import { IEditorPaneRegistry, EditorPaneDescriptor } from 'vs/workbench/browser/editor'; import { LogViewer, LogViewerInput } from 'vs/workbench/contrib/output/browser/logViewer'; @@ -30,11 +29,12 @@ import { assertIsDefined } from 'vs/base/common/types'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Codicon } from 'vs/base/common/codicons'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { EditorExtensions } from 'vs/workbench/common/editor'; +import { Disposable, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; // Register Service -registerSingleton(IOutputService, OutputService, true); +registerSingleton(IOutputService, OutputService, InstantiationType.Delayed); // Register Output Mode ModesRegistry.registerLanguage({ @@ -93,202 +93,272 @@ Registry.as(EditorExtensions.EditorPane).registerEditorPane ] ); -class OutputContribution implements IWorkbenchContribution { +class OutputContribution extends Disposable implements IWorkbenchContribution { constructor( @IInstantiationService instantiationService: IInstantiationService, - @ITextModelService textModelService: ITextModelService + @ITextModelService textModelService: ITextModelService, + @IOutputService private readonly outputService: IOutputService, ) { + super(); textModelService.registerTextModelContentProvider(LOG_SCHEME, instantiationService.createInstance(LogContentProvider)); + this.registerActions(); } -} -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(OutputContribution, 'OutputContribution', LifecyclePhase.Restored); + private registerActions(): void { + this.registerSwitchOutputAction(); + this.registerClearOutputAction(); + this.registerToggleAutoScrollAction(); + this.registerOpenActiveLogOutputFileAction(); + this.registerShowLogsAction(); + this.registerOpenLogFileAction(); + } -registerAction2(class extends Action2 { - constructor() { - super({ - id: `workbench.output.action.switchBetweenOutputs`, - title: nls.localize('switchToOutput.label', "Switch to Output"), - menu: { - id: MenuId.ViewTitle, - when: ContextKeyExpr.equals('view', OUTPUT_VIEW_ID), - group: 'navigation', - order: 1 - }, - }); - } - async run(accessor: ServicesAccessor, channelId: string): Promise { - if (typeof channelId === 'string') { - // Sometimes the action is executed with no channelId parameter, then we should just ignore it #103496 - accessor.get(IOutputService).showChannel(channelId, true); - } - } -}); -registerAction2(class extends Action2 { - constructor() { - super({ - id: `workbench.output.action.clearOutput`, - title: { value: nls.localize('clearOutput.label', "Clear Output"), original: 'Clear Output' }, - category: CATEGORIES.View, - menu: [{ - id: MenuId.ViewTitle, - when: ContextKeyExpr.equals('view', OUTPUT_VIEW_ID), - group: 'navigation', - order: 2 - }, { - id: MenuId.CommandPalette - }, { - id: MenuId.EditorContext, - when: CONTEXT_IN_OUTPUT - }], - icon: Codicon.clearAll - }); - } - async run(accessor: ServicesAccessor): Promise { - const outputService = accessor.get(IOutputService); - const activeChannel = outputService.getActiveChannel(); - if (activeChannel) { - activeChannel.clear(); - aria.status(nls.localize('outputCleared', "Output was cleared")); - } - } -}); -registerAction2(class extends Action2 { - constructor() { - super({ - id: `workbench.output.action.toggleAutoScroll`, - title: { value: nls.localize('toggleAutoScroll', "Toggle Auto Scrolling"), original: 'Toggle Auto Scrolling' }, - tooltip: nls.localize('outputScrollOff', "Turn Auto Scrolling Off"), - menu: { - id: MenuId.ViewTitle, - when: ContextKeyExpr.and(ContextKeyExpr.equals('view', OUTPUT_VIEW_ID)), - group: 'navigation', - order: 3, - }, - icon: Codicon.unlock, - toggled: { - condition: CONTEXT_OUTPUT_SCROLL_LOCK, - icon: Codicon.lock, - tooltip: nls.localize('outputScrollOn', "Turn Auto Scrolling On") + private registerSwitchOutputAction(): void { + this._register(registerAction2(class extends Action2 { + constructor() { + super({ + id: `workbench.output.action.switchBetweenOutputs`, + title: nls.localize('switchBetweenOutputs.label', "Switch Output"), + }); } - }); - } - async run(accessor: ServicesAccessor): Promise { - const outputView = accessor.get(IViewsService).getActiveViewWithId(OUTPUT_VIEW_ID)!; - outputView.scrollLock = !outputView.scrollLock; - } -}); -registerAction2(class extends Action2 { - constructor() { - super({ - id: `workbench.action.openActiveLogOutputFile`, - title: { value: nls.localize('openActiveLogOutputFile', "Open Log Output File"), original: 'Open Log Output File' }, - menu: [{ - id: MenuId.ViewTitle, - when: ContextKeyExpr.equals('view', OUTPUT_VIEW_ID), - group: 'navigation', - order: 4 - }, { - id: MenuId.CommandPalette, - when: CONTEXT_ACTIVE_LOG_OUTPUT, - }], - icon: Codicon.goToFile, - precondition: CONTEXT_ACTIVE_LOG_OUTPUT - }); - } - async run(accessor: ServicesAccessor): Promise { - const outputService = accessor.get(IOutputService); - const editorService = accessor.get(IEditorService); - const instantiationService = accessor.get(IInstantiationService); - const logFileOutputChannelDescriptor = this.getLogFileOutputChannelDescriptor(outputService); - if (logFileOutputChannelDescriptor) { - await editorService.openEditor(instantiationService.createInstance(LogViewerInput, logFileOutputChannelDescriptor), { pinned: true }); - } - } - private getLogFileOutputChannelDescriptor(outputService: IOutputService): IFileOutputChannelDescriptor | null { - const channel = outputService.getActiveChannel(); - if (channel) { - const descriptor = outputService.getChannelDescriptors().filter(c => c.id === channel.id)[0]; - if (descriptor && descriptor.file && descriptor.log) { - return descriptor; - } - } - return null; - } -}); - -registerAction2(class extends Action2 { - constructor() { - super({ - id: 'workbench.action.showLogs', - title: { value: nls.localize('showLogs', "Show Logs..."), original: 'Show Logs...' }, - category: CATEGORIES.Developer, - menu: { - id: MenuId.CommandPalette, - }, - }); - } - async run(accessor: ServicesAccessor): Promise { - const outputService = accessor.get(IOutputService); - const quickInputService = accessor.get(IQuickInputService); - const extensionLogs = [], logs = []; - for (const channel of outputService.getChannelDescriptors()) { - if (channel.log) { - if (channel.extensionId) { - extensionLogs.push(channel); - } else { - logs.push(channel); + async run(accessor: ServicesAccessor, channelId: string): Promise { + if (channelId) { + accessor.get(IOutputService).showChannel(channelId, true); } } - } - const entries: ({ id: string; label: string } | IQuickPickSeparator)[] = []; - for (const { id, label } of logs) { - entries.push({ id, label }); - } - if (extensionLogs.length && logs.length) { - entries.push({ type: 'separator', label: nls.localize('extensionLogs', "Extension Logs") }); - } - for (const { id, label } of extensionLogs) { - entries.push({ id, label }); - } - const entry = await quickInputService.pick(entries, { placeHolder: nls.localize('selectlog', "Select Log") }); - if (entry) { - return outputService.showChannel(entry.id); - } + })); + const switchOutputMenu = new MenuId('workbench.output.menu.switchOutput'); + this._register(MenuRegistry.appendMenuItem(MenuId.ViewTitle, { + submenu: switchOutputMenu, + title: nls.localize('switchToOutput.label', "Switch Output"), + group: 'navigation', + when: ContextKeyExpr.equals('view', OUTPUT_VIEW_ID), + order: 1, + isSelection: true + })); + const registeredChannels = new Map(); + this._register(toDisposable(() => dispose(registeredChannels.values()))); + const registerOutputChannels = (channels: IOutputChannelDescriptor[]) => { + for (const channel of channels) { + let group = '0_outputchannels'; + let title = channel.label; + if (channel.log) { + title = nls.localize('logChannel', "Log ({0})", channel.label); + if (channel.extensionId) { + group = '2_extensionlogs'; + } else { + group = '1_logs'; + } + } + registeredChannels.set(channel.id, registerAction2(class extends Action2 { + constructor() { + super({ + id: `workbench.action.output.show.${channel.id}`, + title, + toggled: ACTIVE_OUTPUT_CHANNEL_CONTEXT.isEqualTo(channel.id), + menu: { + id: switchOutputMenu, + group, + } + }); + } + async run(accessor: ServicesAccessor): Promise { + return accessor.get(IOutputService).showChannel(channel.id, true); + } + })); + } + }; + registerOutputChannels(this.outputService.getChannelDescriptors()); + const outputChannelRegistry = Registry.as(Extensions.OutputChannels); + this._register(outputChannelRegistry.onDidRegisterChannel(e => { + const channel = this.outputService.getChannelDescriptor(e); + if (channel) { + registerOutputChannels([channel]); + } + })); + this._register(outputChannelRegistry.onDidRemoveChannel(e => { + registeredChannels.get(e)?.dispose(); + registeredChannels.delete(e); + })); + } + + private registerClearOutputAction(): void { + this._register(registerAction2(class extends Action2 { + constructor() { + super({ + id: `workbench.output.action.clearOutput`, + title: { value: nls.localize('clearOutput.label', "Clear Output"), original: 'Clear Output' }, + category: Categories.View, + menu: [{ + id: MenuId.ViewTitle, + when: ContextKeyExpr.equals('view', OUTPUT_VIEW_ID), + group: 'navigation', + order: 2 + }, { + id: MenuId.CommandPalette + }, { + id: MenuId.EditorContext, + when: CONTEXT_IN_OUTPUT + }], + icon: Codicon.clearAll + }); + } + async run(accessor: ServicesAccessor): Promise { + const outputService = accessor.get(IOutputService); + const activeChannel = outputService.getActiveChannel(); + if (activeChannel) { + activeChannel.clear(); + aria.status(nls.localize('outputCleared', "Output was cleared")); + } + } + })); + } + + private registerToggleAutoScrollAction(): void { + this._register(registerAction2(class extends Action2 { + constructor() { + super({ + id: `workbench.output.action.toggleAutoScroll`, + title: { value: nls.localize('toggleAutoScroll', "Toggle Auto Scrolling"), original: 'Toggle Auto Scrolling' }, + tooltip: nls.localize('outputScrollOff', "Turn Auto Scrolling Off"), + menu: { + id: MenuId.ViewTitle, + when: ContextKeyExpr.and(ContextKeyExpr.equals('view', OUTPUT_VIEW_ID)), + group: 'navigation', + order: 3, + }, + icon: Codicon.lock, + toggled: { + condition: CONTEXT_OUTPUT_SCROLL_LOCK, + icon: Codicon.unlock, + tooltip: nls.localize('outputScrollOn', "Turn Auto Scrolling On") + } + }); + } + async run(accessor: ServicesAccessor): Promise { + const outputView = accessor.get(IViewsService).getActiveViewWithId(OUTPUT_VIEW_ID)!; + outputView.scrollLock = !outputView.scrollLock; + } + })); + } + + private registerOpenActiveLogOutputFileAction(): void { + this._register(registerAction2(class extends Action2 { + constructor() { + super({ + id: `workbench.action.openActiveLogOutputFile`, + title: { value: nls.localize('openActiveLogOutputFile', "Open Log Output File"), original: 'Open Log Output File' }, + menu: [{ + id: MenuId.ViewTitle, + when: ContextKeyExpr.equals('view', OUTPUT_VIEW_ID), + group: 'navigation', + order: 4 + }], + icon: Codicon.goToFile, + precondition: CONTEXT_ACTIVE_LOG_OUTPUT + }); + } + async run(accessor: ServicesAccessor): Promise { + const outputService = accessor.get(IOutputService); + const editorService = accessor.get(IEditorService); + const instantiationService = accessor.get(IInstantiationService); + const logFileOutputChannelDescriptor = this.getLogFileOutputChannelDescriptor(outputService); + if (logFileOutputChannelDescriptor) { + await editorService.openEditor(instantiationService.createInstance(LogViewerInput, logFileOutputChannelDescriptor), { pinned: true }); + } + } + private getLogFileOutputChannelDescriptor(outputService: IOutputService): IFileOutputChannelDescriptor | null { + const channel = outputService.getActiveChannel(); + if (channel) { + const descriptor = outputService.getChannelDescriptors().filter(c => c.id === channel.id)[0]; + if (descriptor && descriptor.file && descriptor.log) { + return descriptor; + } + } + return null; + } + })); + } + + private registerShowLogsAction(): void { + this._register(registerAction2(class extends Action2 { + constructor() { + super({ + id: 'workbench.action.showLogs', + title: { value: nls.localize('showLogs', "Show Logs..."), original: 'Show Logs...' }, + category: Categories.Developer, + menu: { + id: MenuId.CommandPalette, + }, + }); + } + async run(accessor: ServicesAccessor): Promise { + const outputService = accessor.get(IOutputService); + const quickInputService = accessor.get(IQuickInputService); + const extensionLogs = [], logs = []; + for (const channel of outputService.getChannelDescriptors()) { + if (channel.log) { + if (channel.extensionId) { + extensionLogs.push(channel); + } else { + logs.push(channel); + } + } + } + const entries: ({ id: string; label: string } | IQuickPickSeparator)[] = []; + for (const { id, label } of logs) { + entries.push({ id, label }); + } + if (extensionLogs.length && logs.length) { + entries.push({ type: 'separator', label: nls.localize('extensionLogs', "Extension Logs") }); + } + for (const { id, label } of extensionLogs) { + entries.push({ id, label }); + } + const entry = await quickInputService.pick(entries, { placeHolder: nls.localize('selectlog', "Select Log") }); + if (entry) { + return outputService.showChannel(entry.id); + } + } + })); + } + + private registerOpenLogFileAction(): void { + interface IOutputChannelQuickPickItem extends IQuickPickItem { + channel: IOutputChannelDescriptor; + } + this._register(registerAction2(class extends Action2 { + constructor() { + super({ + id: 'workbench.action.openLogFile', + title: { value: nls.localize('openLogFile', "Open Log File..."), original: 'Open Log File...' }, + category: Categories.Developer, + menu: { + id: MenuId.CommandPalette, + }, + }); + } + async run(accessor: ServicesAccessor): Promise { + const outputService = accessor.get(IOutputService); + const quickInputService = accessor.get(IQuickInputService); + const instantiationService = accessor.get(IInstantiationService); + const editorService = accessor.get(IEditorService); + + const entries: IOutputChannelQuickPickItem[] = outputService.getChannelDescriptors().filter(c => c.file && c.log) + .map(channel => ({ id: channel.id, label: channel.label, channel })); + + const entry = await quickInputService.pick(entries, { placeHolder: nls.localize('selectlogFile', "Select Log file") }); + if (entry) { + assertIsDefined(entry.channel.file); + await editorService.openEditor(instantiationService.createInstance(LogViewerInput, (entry.channel as IFileOutputChannelDescriptor)), { pinned: true }); + } + } + })); } -}); -interface IOutputChannelQuickPickItem extends IQuickPickItem { - channel: IOutputChannelDescriptor; } -registerAction2(class extends Action2 { - constructor() { - super({ - id: 'workbench.action.openLogFile', - title: { value: nls.localize('openLogFile', "Open Log File..."), original: 'Open Log File...' }, - category: CATEGORIES.Developer, - menu: { - id: MenuId.CommandPalette, - }, - }); - } - async run(accessor: ServicesAccessor): Promise { - const outputService = accessor.get(IOutputService); - const quickInputService = accessor.get(IQuickInputService); - const instantiationService = accessor.get(IInstantiationService); - const editorService = accessor.get(IEditorService); - - const entries: IOutputChannelQuickPickItem[] = outputService.getChannelDescriptors().filter(c => c.file && c.log) - .map(channel => ({ id: channel.id, label: channel.label, channel })); - - const entry = await quickInputService.pick(entries, { placeHolder: nls.localize('selectlogFile', "Select Log file") }); - if (entry) { - assertIsDefined(entry.channel.file); - await editorService.openEditor(instantiationService.createInstance(LogViewerInput, (entry.channel as IFileOutputChannelDescriptor)), { pinned: true }); - } - } -}); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(OutputContribution, LifecyclePhase.Restored); Registry.as(ConfigurationExtensions.Configuration).registerConfiguration({ id: 'output', diff --git a/src/vs/workbench/contrib/output/browser/outputServices.ts b/src/vs/workbench/contrib/output/browser/outputServices.ts index e9f97c01b29..4fd042c0c9a 100644 --- a/src/vs/workbench/contrib/output/browser/outputServices.ts +++ b/src/vs/workbench/contrib/output/browser/outputServices.ts @@ -9,7 +9,7 @@ import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IOutputChannel, IOutputService, OUTPUT_VIEW_ID, OUTPUT_SCHEME, LOG_SCHEME, LOG_MIME, OUTPUT_MIME, OutputChannelUpdateMode, IOutputChannelDescriptor, Extensions, IOutputChannelRegistry } from 'vs/workbench/services/output/common/output'; +import { IOutputChannel, IOutputService, OUTPUT_VIEW_ID, OUTPUT_SCHEME, LOG_SCHEME, LOG_MIME, OUTPUT_MIME, OutputChannelUpdateMode, IOutputChannelDescriptor, Extensions, IOutputChannelRegistry, ACTIVE_OUTPUT_CHANNEL_CONTEXT, CONTEXT_ACTIVE_LOG_OUTPUT } from 'vs/workbench/services/output/common/output'; import { OutputLinkProvider } from 'vs/workbench/contrib/output/browser/outputLinkProvider'; import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; import { ITextModel } from 'vs/editor/common/model'; @@ -20,6 +20,7 @@ import { IViewsService } from 'vs/workbench/common/views'; import { OutputViewPane } from 'vs/workbench/contrib/output/browser/outputView'; import { IOutputChannelModelService } from 'vs/workbench/contrib/output/common/outputChannelModelService'; import { ILanguageService } from 'vs/editor/common/languages/language'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; const OUTPUT_ACTIVE_CHANNEL_KEY = 'output.activechannel'; @@ -71,6 +72,9 @@ export class OutputService extends Disposable implements IOutputService, ITextMo private readonly _onActiveOutputChannel = this._register(new Emitter()); readonly onActiveOutputChannel: Event = this._onActiveOutputChannel.event; + private readonly activeOutputChannelContext: IContextKey; + private readonly activeLogOutputChannelContext: IContextKey; + constructor( @IStorageService private readonly storageService: IStorageService, @IInstantiationService private readonly instantiationService: IInstantiationService, @@ -78,9 +82,15 @@ export class OutputService extends Disposable implements IOutputService, ITextMo @ILogService private readonly logService: ILogService, @ILifecycleService private readonly lifecycleService: ILifecycleService, @IViewsService private readonly viewsService: IViewsService, + @IContextKeyService contextKeyService: IContextKeyService, ) { super(); this.activeChannelIdInStorage = this.storageService.get(OUTPUT_ACTIVE_CHANNEL_KEY, StorageScope.WORKSPACE, ''); + this.activeOutputChannelContext = ACTIVE_OUTPUT_CHANNEL_CONTEXT.bindTo(contextKeyService); + this.activeOutputChannelContext.set(this.activeChannelIdInStorage); + this._register(this.onActiveOutputChannel(channel => this.activeOutputChannelContext.set(channel))); + + this.activeLogOutputChannelContext = CONTEXT_ACTIVE_LOG_OUTPUT.bindTo(contextKeyService); // Register as text model content provider for output textModelResolverService.registerTextModelContentProvider(OUTPUT_SCHEME, this); @@ -99,6 +109,12 @@ export class OutputService extends Disposable implements IOutputService, ITextMo this.setActiveChannel(channels && channels.length > 0 ? this.getChannel(channels[0].id) : undefined); } + this._register(Event.filter(this.viewsService.onDidChangeViewVisibility, e => e.id === OUTPUT_VIEW_ID && e.visible)(() => { + if (this.activeChannel) { + this.viewsService.getActiveViewWithId(OUTPUT_VIEW_ID)?.showChannel(this.activeChannel, true); + } + })); + this._register(this.lifecycleService.onDidShutdown(() => this.dispose())); } @@ -180,6 +196,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo private setActiveChannel(channel: OutputChannel | undefined): void { this.activeChannel = channel; + this.activeLogOutputChannelContext.set(!!channel?.outputChannelDescriptor?.file && channel?.outputChannelDescriptor?.log); if (this.activeChannel) { this.storageService.store(OUTPUT_ACTIVE_CHANNEL_KEY, this.activeChannel.id, StorageScope.WORKSPACE, StorageTarget.USER); diff --git a/src/vs/workbench/contrib/output/browser/outputView.ts b/src/vs/workbench/contrib/output/browser/outputView.ts index dd969c688c3..9f84798c50c 100644 --- a/src/vs/workbench/contrib/output/browser/outputView.ts +++ b/src/vs/workbench/contrib/output/browser/outputView.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IAction } from 'vs/base/common/actions'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorOptions as ICodeEditorOptions } from 'vs/editor/common/config/editorOptions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -14,7 +13,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IEditorOpenContext } from 'vs/workbench/common/editor'; import { AbstractTextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor'; -import { OUTPUT_VIEW_ID, IOutputService, CONTEXT_IN_OUTPUT, IOutputChannel, CONTEXT_ACTIVE_LOG_OUTPUT, CONTEXT_OUTPUT_SCROLL_LOCK, IOutputChannelDescriptor, IOutputChannelRegistry, Extensions } from 'vs/workbench/services/output/common/output'; +import { OUTPUT_VIEW_ID, CONTEXT_IN_OUTPUT, IOutputChannel, CONTEXT_OUTPUT_SCROLL_LOCK } from 'vs/workbench/services/output/common/output'; import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -23,18 +22,13 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { CursorChangeReason } from 'vs/editor/common/cursorEvents'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPane'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { TextResourceEditorInput } from 'vs/workbench/common/editor/textResourceEditorInput'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; -import { ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; -import { editorBackground, selectBorder } from 'vs/platform/theme/common/colorRegistry'; -import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { editorBackground } from 'vs/platform/theme/common/colorRegistry'; import { Dimension } from 'vs/base/browser/dom'; -import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { IFileService } from 'vs/platform/files/common/files'; @@ -57,7 +51,6 @@ export class OutputViewPane extends ViewPane { @IContextKeyService contextKeyService: IContextKeyService, @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IInstantiationService instantiationService: IInstantiationService, - @IOutputService private readonly outputService: IOutputService, @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, @ITelemetryService telemetryService: ITelemetryService, @@ -86,15 +79,14 @@ export class OutputViewPane extends ViewPane { this.editorPromise?.then(() => this.editor.focus()); } - override renderBody(container: HTMLElement): void { + protected override renderBody(container: HTMLElement): void { super.renderBody(container); this.editor.create(container); container.classList.add('output-view'); const codeEditor = this.editor.getControl(); codeEditor.setAriaOptions({ role: 'document', activeDescendant: undefined }); this._register(codeEditor.onDidChangeModelContent(() => { - const activeChannel = this.outputService.getActiveChannel(); - if (activeChannel && !this.scrollLock) { + if (!this.scrollLock) { this.editor.revealLastLine(); } })); @@ -121,30 +113,16 @@ export class OutputViewPane extends ViewPane { this.editor.layout(new Dimension(width, height)); } - override getActionViewItem(action: IAction): IActionViewItem | undefined { - if (action.id === 'workbench.output.action.switchBetweenOutputs') { - return this.instantiationService.createInstance(SwitchOutputActionViewItem, action); - } - return super.getActionViewItem(action); - } private onDidChangeVisibility(visible: boolean): void { this.editor.setVisible(visible); - let channel: IOutputChannel | undefined = undefined; - if (visible) { - channel = this.channelId ? this.outputService.getChannel(this.channelId) : this.outputService.getActiveChannel(); - } - if (channel) { - this.setInput(channel); - } else { + if (!visible) { this.clearInput(); } } private setInput(channel: IOutputChannel): void { this.channelId = channel.id; - const descriptor = this.outputService.getChannelDescriptor(channel.id); - CONTEXT_ACTIVE_LOG_OUTPUT.bindTo(this.contextKeyService).set(!!descriptor?.file && descriptor?.log); const input = this.createInput(channel); if (!this.editor.input || !input.matches(this.editor.input)) { @@ -156,7 +134,7 @@ export class OutputViewPane extends ViewPane { } private clearInput(): void { - CONTEXT_ACTIVE_LOG_OUTPUT.bindTo(this.contextKeyService).set(false); + this.channelId = undefined; this.editor.clearInput(); this.editorPromise = null; } @@ -167,7 +145,7 @@ export class OutputViewPane extends ViewPane { } -export class OutputEditor extends AbstractTextResourceEditor { +class OutputEditor extends AbstractTextResourceEditor { constructor( @ITelemetryService telemetryService: ITelemetryService, @@ -176,7 +154,6 @@ export class OutputEditor extends AbstractTextResourceEditor { @IConfigurationService private readonly configurationService: IConfigurationService, @ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService, @IThemeService themeService: IThemeService, - @IOutputService private readonly outputService: IOutputService, @IEditorGroupsService editorGroupService: IEditorGroupsService, @IEditorService editorService: IEditorService, @IFileService fileService: IFileService @@ -227,9 +204,7 @@ export class OutputEditor extends AbstractTextResourceEditor { } protected getAriaLabel(): string { - const channel = this.outputService.getActiveChannel(); - - return channel ? nls.localize('outputViewWithInputAriaLabel', "{0}, Output panel", channel.label) : nls.localize('outputViewAriaLabel', "Output panel"); + return this.input ? this.input.getAriaLabel() : nls.localize('outputViewAriaLabel', "Output panel"); } override async setInput(input: TextResourceEditorInput, options: ITextEditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { @@ -270,85 +245,6 @@ export class OutputEditor extends AbstractTextResourceEditor { } } -type OutputChannelSelectionOptionItem = ISelectOptionItem & { readonly channel?: IOutputChannelDescriptor }; - -class SwitchOutputActionViewItem extends SelectActionViewItem { - - private static readonly SEPARATOR = '\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500'; - - private selectionOptionItems: OutputChannelSelectionOptionItem[] = []; - - constructor( - action: IAction, - @IOutputService private readonly outputService: IOutputService, - @IThemeService private readonly themeService: IThemeService, - @IContextViewService contextViewService: IContextViewService - ) { - super(null, action, [], 0, contextViewService, { ariaLabel: nls.localize('outputChannels', "Output Channels"), optionsAsChildren: true }); - - const outputChannelRegistry = Registry.as(Extensions.OutputChannels); - this._register(outputChannelRegistry.onDidRegisterChannel(() => this.updateOptions())); - this._register(outputChannelRegistry.onDidRemoveChannel(() => this.updateOptions())); - this._register(this.outputService.onActiveOutputChannel(() => this.updateOptions())); - this._register(attachSelectBoxStyler(this.selectBox, themeService)); - - this.updateOptions(); - } - - override render(container: HTMLElement): void { - super.render(container); - container.classList.add('switch-output'); - this._register(attachStylerCallback(this.themeService, { selectBorder }, colors => { - container.style.borderColor = colors.selectBorder ? `${colors.selectBorder}` : ''; - })); - } - - protected override getActionContext(option: string, index: number): string { - return this.selectionOptionItems[index]?.channel?.id ?? option; - } - - private updateOptions(): void { - const outputChannels = []; - const logChannels = []; - const extensionLogChannels = []; - this.selectionOptionItems = []; - for (const descriptor of this.outputService.getChannelDescriptors()) { - if (descriptor.log) { - if (descriptor.extensionId) { - extensionLogChannels.push(descriptor); - } else { - logChannels.push(descriptor); - } - } else { - outputChannels.push(descriptor); - } - } - - for (const descriptor of outputChannels) { - this.selectionOptionItems.push({ text: descriptor.label, isDisabled: false, channel: descriptor }); - } - if (outputChannels.length && logChannels.length) { - this.selectionOptionItems.push({ text: SwitchOutputActionViewItem.SEPARATOR, isDisabled: true }); - } - for (const descriptor of logChannels) { - this.selectionOptionItems.push({ text: nls.localize('logChannel', "Log ({0})", descriptor.label), isDisabled: false, channel: descriptor }); - } - if (logChannels.length && extensionLogChannels.length) { - this.selectionOptionItems.push({ text: SwitchOutputActionViewItem.SEPARATOR, isDisabled: true }); - } - for (const descriptor of extensionLogChannels) { - this.selectionOptionItems.push({ text: nls.localize('logChannel', "Log ({0})", descriptor.label), isDisabled: false, channel: descriptor }); - } - - let selected = 0; - const activeChannel = this.outputService.getActiveChannel(); - if (activeChannel) { - selected = this.selectionOptionItems.findIndex(item => item.channel?.id === activeChannel.id); - } - this.setOptions(this.selectionOptionItems, Math.max(0, selected)); - } -} - registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { // Sidebar background for the output view const sidebarBackground = theme.getColor(SIDE_BAR_BACKGROUND); diff --git a/src/vs/workbench/contrib/output/common/outputChannelModelService.ts b/src/vs/workbench/contrib/output/common/outputChannelModelService.ts index b2cf6ad39d3..ec25f0df576 100644 --- a/src/vs/workbench/contrib/output/common/outputChannelModelService.ts +++ b/src/vs/workbench/contrib/output/common/outputChannelModelService.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IFileService } from 'vs/platform/files/common/files'; import { toLocalISOString } from 'vs/base/common/date'; -import { dirname, joinPath } from 'vs/base/common/resources'; +import { joinPath } from 'vs/base/common/resources'; import { DelegatedOutputChannelModel, FileOutputChannelModel, IOutputChannelModel } from 'vs/workbench/contrib/output/common/outputChannelModel'; import { URI } from 'vs/base/common/uri'; import { ILanguageSelection } from 'vs/editor/common/languages/language'; @@ -22,15 +22,19 @@ export interface IOutputChannelModelService { } -export abstract class AbstractOutputChannelModelService { +export class OutputChannelModelService { declare readonly _serviceBrand: undefined; + private readonly outputLocation: URI; + constructor( - private readonly outputLocation: URI, - @IFileService protected readonly fileService: IFileService, - @IInstantiationService protected readonly instantiationService: IInstantiationService - ) { } + @IFileService private readonly fileService: IFileService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService + ) { + this.outputLocation = joinPath(environmentService.windowLogsPath, `output_${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}`); + } createOutputChannelModel(id: string, modelUri: URI, language: ILanguageSelection, file?: URI): IOutputChannelModel { return file ? this.instantiationService.createInstance(FileOutputChannelModel, modelUri, language, file) : this.instantiationService.createInstance(DelegatedOutputChannelModel, id, modelUri, language, this.outputDir); @@ -46,15 +50,4 @@ export abstract class AbstractOutputChannelModelService { } -export class OutputChannelModelService extends AbstractOutputChannelModelService implements IOutputChannelModelService { - - constructor( - @IInstantiationService instantiationService: IInstantiationService, - @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, - @IFileService fileService: IFileService, - ) { - super(joinPath(dirname(environmentService.logFile), toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')), fileService, instantiationService); - } -} - -registerSingleton(IOutputChannelModelService, OutputChannelModelService, true); +registerSingleton(IOutputChannelModelService, OutputChannelModelService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/output/electron-sandbox/outputChannelModelService.ts b/src/vs/workbench/contrib/output/electron-sandbox/outputChannelModelService.ts deleted file mode 100644 index 211a3e73d83..00000000000 --- a/src/vs/workbench/contrib/output/electron-sandbox/outputChannelModelService.ts +++ /dev/null @@ -1,29 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { join } from 'vs/base/common/path'; -import { URI } from 'vs/base/common/uri'; -import { IFileService } from 'vs/platform/files/common/files'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { toLocalISOString } from 'vs/base/common/date'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; -import { AbstractOutputChannelModelService, IOutputChannelModelService } from 'vs/workbench/contrib/output/common/outputChannelModelService'; - -export class OutputChannelModelService extends AbstractOutputChannelModelService implements IOutputChannelModelService { - - constructor( - @IInstantiationService instantiationService: IInstantiationService, - @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, - @IFileService fileService: IFileService, - @INativeHostService nativeHostService: INativeHostService - ) { - super(URI.file(join(environmentService.logsPath, `output_${nativeHostService.windowId}_${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}`)), fileService, instantiationService); - } - -} - -registerSingleton(IOutputChannelModelService, OutputChannelModelService, true); diff --git a/src/vs/workbench/contrib/performance/browser/performance.contribution.ts b/src/vs/workbench/contrib/performance/browser/performance.contribution.ts index ac2fe448888..b7fbee24b67 100644 --- a/src/vs/workbench/contrib/performance/browser/performance.contribution.ts +++ b/src/vs/workbench/contrib/performance/browser/performance.contribution.ts @@ -8,7 +8,7 @@ import { registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { Extensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { EditorExtensions, IEditorSerializer, IEditorFactoryRegistry } from 'vs/workbench/common/editor'; import { PerfviewContrib, PerfviewInput } from 'vs/workbench/contrib/performance/browser/perfviewEditor'; @@ -20,7 +20,6 @@ import { EventProfiling } from 'vs/base/common/event'; Registry.as(Extensions.Workbench).registerWorkbenchContribution( PerfviewContrib, - 'PerfviewContrib', LifecyclePhase.Ready ); @@ -46,7 +45,7 @@ registerAction2(class extends Action2 { super({ id: 'perfview.show', title: { value: localize('show.label', "Startup Performance"), original: 'Startup Performance' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true }); } @@ -65,7 +64,7 @@ registerAction2(class PrintServiceCycles extends Action2 { super({ id: 'perf.insta.printAsyncCycles', title: { value: localize('cycles', "Print Service Cycles"), original: 'Print Service Cycles' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true }); } @@ -89,7 +88,7 @@ registerAction2(class PrintServiceTraces extends Action2 { super({ id: 'perf.insta.printTraces', title: { value: localize('insta.trace', "Print Service Traces"), original: 'Print Service Traces' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true }); } @@ -113,7 +112,7 @@ registerAction2(class PrintEventProfiling extends Action2 { super({ id: 'perf.event.profiling', title: { value: localize('emitter', "Print Emitter Profiles"), original: 'Print Emitter Profiles' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true }); } diff --git a/src/vs/workbench/contrib/performance/browser/performance.web.contribution.ts b/src/vs/workbench/contrib/performance/browser/performance.web.contribution.ts index 25e76c1f35a..566cf8af723 100644 --- a/src/vs/workbench/contrib/performance/browser/performance.web.contribution.ts +++ b/src/vs/workbench/contrib/performance/browser/performance.web.contribution.ts @@ -30,6 +30,5 @@ class ResourcePerformanceMarks { Registry.as(Extensions.Workbench).registerWorkbenchContribution( ResourcePerformanceMarks, - 'ResourcePerformanceMarks', LifecyclePhase.Eventually ); diff --git a/src/vs/workbench/contrib/performance/electron-sandbox/performance.contribution.ts b/src/vs/workbench/contrib/performance/electron-sandbox/performance.contribution.ts index 36df52641bc..978c8c32b04 100644 --- a/src/vs/workbench/contrib/performance/electron-sandbox/performance.contribution.ts +++ b/src/vs/workbench/contrib/performance/electron-sandbox/performance.contribution.ts @@ -8,12 +8,18 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { StartupProfiler } from './startupProfiler'; import { StartupTimings } from './startupTimings'; +import { RendererProfiling } from 'vs/workbench/contrib/performance/electron-sandbox/rendererAutoProfiler'; + + +Registry.as(Extensions.Workbench).registerWorkbenchContribution( + RendererProfiling, + LifecyclePhase.Eventually +); // -- startup profiler Registry.as(Extensions.Workbench).registerWorkbenchContribution( StartupProfiler, - 'StartupProfiler', LifecyclePhase.Restored ); @@ -21,6 +27,5 @@ Registry.as(Extensions.Workbench).registerWorkb Registry.as(Extensions.Workbench).registerWorkbenchContribution( StartupTimings, - 'StartupTimings', LifecyclePhase.Eventually ); diff --git a/src/vs/workbench/contrib/performance/electron-sandbox/rendererAutoProfiler.ts b/src/vs/workbench/contrib/performance/electron-sandbox/rendererAutoProfiler.ts new file mode 100644 index 00000000000..9fc70c63922 --- /dev/null +++ b/src/vs/workbench/contrib/performance/electron-sandbox/rendererAutoProfiler.ts @@ -0,0 +1,153 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { generateUuid } from 'vs/base/common/uuid'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { ILogService } from 'vs/platform/log/common/log'; +import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IViewDescriptorService } from 'vs/workbench/common/views'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ITimerService } from 'vs/workbench/services/timer/browser/timerService'; + + +export class RendererProfiling { + + private readonly _disposables = new DisposableStore(); + + constructor( + @ITimerService timerService: ITimerService, + @INativeHostService nativeHostService: INativeHostService, + @ILogService logService: ILogService, + @ICommandService commandService: ICommandService, + @ITelemetryService telemetryService: ITelemetryService, + @IViewDescriptorService viewsDescriptorService: IViewDescriptorService, + @IEditorService editorService: IEditorService, + ) { + + timerService.whenReady().then(() => { + + // SLOW threshold + const slowThreshold = (timerService.startupMetrics.timers.ellapsedRequire / 2) | 0; + + // Keep a record of the last events + const eventHistory = new RingBuffer<{ command: string; timestamp: number }>(5); + this._disposables.add(commandService.onWillExecuteCommand(e => eventHistory.push({ command: e.commandId, timestamp: Date.now() }))); + + + const obs = new PerformanceObserver(list => { + + let maxDuration = 0; + for (const entry of list.getEntries()) { + maxDuration = Math.max(maxDuration, entry.duration); + } + obs.takeRecords(); + + if (maxDuration < slowThreshold) { + return; + } + + const sessionId = generateUuid(); + + // all visible views + const views = viewsDescriptorService.viewContainers.map(container => { + const model = viewsDescriptorService.getViewContainerModel(container); + return model.visibleViewDescriptors.map(view => view.id); + }); + + const editors = editorService.visibleEditors.map(editor => editor.typeId); + + // send telemetry event + telemetryService.publicLog2('perf.freeze.events', { + sessionId: sessionId, + timestamp: Date.now() - maxDuration, + recentCommands: JSON.stringify(eventHistory.values()), + views: JSON.stringify(views.flat()), + editors: JSON.stringify(editors), + }); + + // start heartbeat monitoring + const sessionDisposables = this._disposables.add(new DisposableStore()); + logService.warn(`[perf] Renderer reported VERY LONG TASK (${maxDuration}ms), starting auto profiling session '${sessionId}'`); + // pause observation, we'll take a detailed look + obs.disconnect(); + nativeHostService.startHeartbeat(sessionId).then(success => { + if (!success) { + logService.warn('[perf] FAILED to start heartbeat sending'); + return; + } + + // start sending a repeated heartbeat which is expected to be received by the main side + const handle1 = setInterval(() => nativeHostService.sendHeartbeat(sessionId), 500); + + // stop heartbeat after 20s + const handle2 = setTimeout(() => sessionDisposables.clear(), 20 * 1000); + + // cleanup + // - stop heartbeat + // - reconnect perf observer + sessionDisposables.add(toDisposable(() => { + clearInterval(handle1); + clearTimeout(handle2); + nativeHostService.stopHeartbeat(sessionId); + logService.warn(`[perf] STOPPING to send heartbeat`); + obs.observe({ entryTypes: ['longtask'] }); + })); + }); + }); + + this._disposables.add(toDisposable(() => obs.disconnect())); + obs.observe({ entryTypes: ['longtask'] }); + }); + } + + dispose(): void { + this._disposables.dispose(); + } +} + +type TelemetryEventData = { + sessionId: string; + timestamp: number; + recentCommands: string; + views: string; + editors: string; +}; + +type TelemetryEventClassification = { + owner: 'jrieken'; + comment: 'Insight about what happened before/while a long task was reported'; + sessionId: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Session identifier that allows to correlate CPU samples and events' }; + timestamp: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'Unix time at which the long task approximately happened' }; + recentCommands: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Events prior to the long task' }; + views: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Visible views' }; + editors: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Visible editor' }; +}; + +class RingBuffer { + + private static _value = {}; + + private readonly _data: any[]; + + private _index: number = 0; + private _size: number = 0; + + constructor(size: number) { + this._size = size; + this._data = new Array(size); + this._data.fill(RingBuffer._value, 0, size); + } + + push(value: T): void { + this._data[this._index] = value; + this._index = (this._index + 1) % this._size; + } + + values(): T[] { + return [...this._data.slice(this._index), ...this._data.slice(0, this._index)].filter(a => a !== RingBuffer._value); + } +} diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts index c6312374488..1b9edd9c0d4 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts @@ -519,7 +519,9 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP private getActionsLabels(): Map { const actionsLabels: Map = new Map(); - EditorExtensionsRegistry.getEditorActions().forEach(editorAction => actionsLabels.set(editorAction.id, editorAction.label)); + for (const editorAction of EditorExtensionsRegistry.getEditorActions()) { + actionsLabels.set(editorAction.id, editorAction.label); + } for (const menuItem of MenuRegistry.getMenuItems(MenuId.CommandPalette)) { if (isIMenuItem(menuItem)) { const title = typeof menuItem.command.title === 'string' ? menuItem.command.title : menuItem.command.title.value; diff --git a/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts b/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts index efc514e74a4..fdbef87f153 100644 --- a/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts +++ b/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts @@ -10,18 +10,17 @@ import { parseKeyboardLayoutDescription, areKeyboardLayoutsEqual, getKeyboardLay import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; -import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { KEYBOARD_LAYOUT_OPEN_PICKER } from 'vs/workbench/contrib/preferences/common/preferences'; -import { Action } from 'vs/base/common/actions'; import { isMacintosh, isWindows } from 'vs/base/common/platform'; import { QuickPickInput, IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { VSBuffer } from 'vs/base/common/buffer'; import { IEditorPane } from 'vs/workbench/common/editor'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; export class KeyboardLayoutPickerContribution extends Disposable implements IWorkbenchContribution { private readonly pickerElement = this._register(new MutableDisposable()); @@ -81,7 +80,7 @@ export class KeyboardLayoutPickerContribution extends Disposable implements IWor } const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchContributionsRegistry.registerWorkbenchContribution(KeyboardLayoutPickerContribution, 'KeyboardLayoutPickerContribution', LifecyclePhase.Starting); +workbenchContributionsRegistry.registerWorkbenchContribution(KeyboardLayoutPickerContribution, LifecyclePhase.Starting); interface LayoutQuickPickItem extends IQuickPickItem { layout: IKeyboardLayoutInfo; @@ -93,35 +92,34 @@ interface IUnknownLayout { layout?: string; } -export class KeyboardLayoutPickerAction extends Action { - static readonly ID = KEYBOARD_LAYOUT_OPEN_PICKER; - static readonly LABEL = nls.localize('keyboard.chooseLayout', "Change Keyboard Layout"); +const DEFAULT_CONTENT: string = [ + `// ${nls.localize('displayLanguage', 'Defines the keyboard layout used in VS Code in the browser environment.')}`, + `// ${nls.localize('doc', 'Open VS Code and run "Developer: Inspect Key Mappings (JSON)" from Command Palette.')}`, + ``, + `// Once you have the keyboard layout info, please paste it below.`, + '\n' +].join('\n'); - private static DEFAULT_CONTENT: string = [ - `// ${nls.localize('displayLanguage', 'Defines the keyboard layout used in VS Code in the browser environment.')}`, - `// ${nls.localize('doc', 'Open VS Code and run "Developer: Inspect Key Mappings (JSON)" from Command Palette.')}`, - ``, - `// Once you have the keyboard layout info, please paste it below.`, - '\n' - ].join('\n'); - - constructor( - actionId: string, - actionLabel: string, - @IFileService private readonly fileService: IFileService, - @IQuickInputService private readonly quickInputService: IQuickInputService, - @IKeyboardLayoutService private readonly keyboardLayoutService: IKeyboardLayoutService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, - @IEditorService private readonly editorService: IEditorService - ) { - super(actionId, actionLabel, undefined, true); +registerAction2(class extends Action2 { + constructor() { + super({ + id: KEYBOARD_LAYOUT_OPEN_PICKER, + title: { value: nls.localize('keyboard.chooseLayout', "Change Keyboard Layout"), original: 'Change Keyboard Layout' }, + f1: true + }); } - override async run(): Promise { - const layouts = this.keyboardLayoutService.getAllKeyboardLayouts(); - const currentLayout = this.keyboardLayoutService.getCurrentKeyboardLayout(); - const layoutConfig = this.configurationService.getValue('keyboard.layout'); + async run(accessor: ServicesAccessor): Promise { + const keyboardLayoutService = accessor.get(IKeyboardLayoutService); + const quickInputService = accessor.get(IQuickInputService); + const configurationService = accessor.get(IConfigurationService); + const environmentService = accessor.get(IEnvironmentService); + const editorService = accessor.get(IEditorService); + const fileService = accessor.get(IFileService); + + const layouts = keyboardLayoutService.getAllKeyboardLayouts(); + const currentLayout = keyboardLayoutService.getCurrentKeyboardLayout(); + const layoutConfig = configurationService.getValue('keyboard.layout'); const isAutoDetect = layoutConfig === 'autodetect'; const picks: QuickPickInput[] = layouts.map(layout => { @@ -156,27 +154,27 @@ export class KeyboardLayoutPickerAction extends Action { picks.unshift(autoDetectMode); - const pick = await this.quickInputService.pick(picks, { placeHolder: nls.localize('pickKeyboardLayout', "Select Keyboard Layout"), matchOnDescription: true }); + const pick = await quickInputService.pick(picks, { placeHolder: nls.localize('pickKeyboardLayout', "Select Keyboard Layout"), matchOnDescription: true }); if (!pick) { return; } if (pick === autoDetectMode) { // set keymap service to auto mode - this.configurationService.updateValue('keyboard.layout', 'autodetect'); + configurationService.updateValue('keyboard.layout', 'autodetect'); return; } if (pick === configureKeyboardLayout) { - const file = this.environmentService.keyboardLayoutResource; + const file = environmentService.keyboardLayoutResource; - await this.fileService.stat(file).then(undefined, () => { - return this.fileService.createFile(file, VSBuffer.fromString(KeyboardLayoutPickerAction.DEFAULT_CONTENT)); + await fileService.stat(file).then(undefined, () => { + return fileService.createFile(file, VSBuffer.fromString(DEFAULT_CONTENT)); }).then((stat): Promise | undefined => { if (!stat) { return undefined; } - return this.editorService.openEditor({ + return editorService.openEditor({ resource: stat.resource, languageId: 'jsonc', options: { pinned: true } @@ -188,9 +186,6 @@ export class KeyboardLayoutPickerAction extends Action { return Promise.resolve(); } - this.configurationService.updateValue('keyboard.layout', getKeyboardLayoutId((pick).layout)); + configurationService.updateValue('keyboard.layout', getKeyboardLayoutId((pick).layout)); } -} - -const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(SyncActionDescriptor.from(KeyboardLayoutPickerAction, {}), 'Preferences: Change Keyboard Layout', nls.localize('preferences', "Preferences")); +}); diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css index 11f242b2646..188004eb718 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css @@ -561,7 +561,7 @@ width: initial; font: inherit; height: 26px; - padding: 2px 8px; + padding: 2px 6px; } .settings-editor > .settings-body .settings-tree-container .setting-item-new-extensions { diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index bda672f49dd..2e3ffc352c1 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -422,6 +422,23 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon return accessor.get(IPreferencesService).openWorkspaceSettings(args); } }); + + registerAction2(class extends Action2 { + constructor() { + super({ + id: 'workbench.action.openAccessibilitySettings', + title: { value: nls.localize('openAccessibilitySettings', "Open Accessibility Settings"), original: 'Open Accessibility Settings' }, + category, + menu: { + id: MenuId.CommandPalette, + when: WorkbenchStateContext.notEqualsTo('empty') + } + }); + } + async run(accessor: ServicesAccessor) { + await accessor.get(IPreferencesService).openSettings({ jsonEditor: false, query: '@tag:accessibility' }); + } + }); registerAction2(class extends Action2 { constructor() { super({ @@ -1243,8 +1260,8 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon } const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchContributionsRegistry.registerWorkbenchContribution(PreferencesActionsContribution, 'PreferencesActionsContribution', LifecyclePhase.Starting); -workbenchContributionsRegistry.registerWorkbenchContribution(PreferencesContribution, 'PreferencesContribution', LifecyclePhase.Starting); +workbenchContributionsRegistry.registerWorkbenchContribution(PreferencesActionsContribution, LifecyclePhase.Starting); +workbenchContributionsRegistry.registerWorkbenchContribution(PreferencesContribution, LifecyclePhase.Starting); registerEditorContribution(SettingsEditorContribution.ID, SettingsEditorContribution); diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts index 2c59b8bfc44..809fefe402c 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts @@ -25,7 +25,7 @@ import { nullRange } from 'vs/workbench/services/preferences/common/preferencesM import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IStringDictionary } from 'vs/base/common/collections'; import { IProductService } from 'vs/platform/product/common/productService'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; export interface IEndpointDetails { urlBase?: string; @@ -607,4 +607,4 @@ export class SettingMatches { } } -registerSingleton(IPreferencesSearchService, PreferencesSearchService, true); +registerSingleton(IPreferencesSearchService, PreferencesSearchService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 69f441beb3d..f12a3672b7b 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -114,6 +114,7 @@ export class SettingsEditor2 extends EditorPane { '@tag:sync', '@tag:usesOnlineServices', '@tag:telemetry', + '@tag:accessibility', `@${ID_SETTING_TAG}`, `@${EXTENSION_SETTING_TAG}`, `@${FEATURE_SETTING_TAG}scm`, diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts index 73b3f4e8f3e..cf90eed08f2 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts @@ -12,11 +12,13 @@ import { Emitter } from 'vs/base/common/event'; import { IDisposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { localize } from 'vs/nls'; +import { ICommandService } from 'vs/platform/commands/common/commands'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { getIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerge'; import { getDefaultIgnoredSettings, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; import { SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; -import { MODIFIED_INDICATOR_USE_INLINE_ONLY } from 'vs/workbench/contrib/preferences/common/preferences'; +import { MODIFIED_INDICATOR_USE_INLINE_ONLY, POLICY_SETTING_TAG } from 'vs/workbench/contrib/preferences/common/preferences'; import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; const $ = DOM.$; @@ -34,20 +36,26 @@ export interface ISettingOverrideClickEvent { */ export class SettingsTreeIndicatorsLabel implements IDisposable { private indicatorsContainerElement: HTMLElement; + private workspaceTrustElement: HTMLElement; private scopeOverridesElement: HTMLElement; private scopeOverridesLabel: SimpleIconLabel; private scopeOverridesHover: MutableDisposable; private syncIgnoredElement: HTMLElement; - private syncIgnoredHover: ICustomHover | undefined; private defaultOverrideIndicatorElement: HTMLElement; private hoverDelegate: IHoverDelegate; + private profilesEnabled: boolean; + + // Holds hovers that contain a fixed message. + private simpleHoverStore: DisposableStore; constructor( container: HTMLElement, - @IConfigurationService private readonly configurationService: IConfigurationService, + @IConfigurationService configurationService: IConfigurationService, @IHoverService hoverService: IHoverService, @IUserDataSyncEnablementService private readonly userDataSyncEnablementService: IUserDataSyncEnablementService, - @ILanguageService private readonly languageService: ILanguageService) { + @ILanguageService private readonly languageService: ILanguageService, + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, + @ICommandService private readonly commandService: ICommandService) { this.indicatorsContainerElement = DOM.append(container, $('.misc-label')); this.indicatorsContainerElement.style.display = 'inline'; @@ -60,12 +68,41 @@ export class SettingsTreeIndicatorsLabel implements IDisposable { placement: 'element' }; + this.simpleHoverStore = new DisposableStore(); const scopeOverridesIndicator = this.createScopeOverridesIndicator(); + this.workspaceTrustElement = this.createWorkspaceTrustElement(); this.scopeOverridesElement = scopeOverridesIndicator.element; this.scopeOverridesLabel = scopeOverridesIndicator.label; this.syncIgnoredElement = this.createSyncIgnoredElement(); this.defaultOverrideIndicatorElement = this.createDefaultOverrideIndicator(); this.scopeOverridesHover = new MutableDisposable(); + this.profilesEnabled = this.userDataProfilesService.isEnabled(); + } + + private createWorkspaceTrustElement(): HTMLElement { + const workspaceTrustElement = $('span.setting-item-workspace-trust'); + const workspaceTrustLabel = new SimpleIconLabel(workspaceTrustElement); + workspaceTrustLabel.text = localize('workspaceUntrustedLabel', "Requires Workspace Trust"); + const contentFallback = localize('trustLabel', "This setting can only be applied in a trusted workspace."); + + const contentMarkdownString = contentFallback + ` [${localize('manageWorkspaceTrust', "Manage Workspace Trust")}](manage-workspace-trust).`; + const content: ITooltipMarkdownString = { + markdown: { + value: contentMarkdownString, + isTrusted: false, + supportHtml: false + }, + markdownNotSupportedFallback: contentFallback + }; + const options: IUpdatableHoverOptions = { + linkHandler: (url: string) => { + this.commandService.executeCommand('workbench.trust.manage'); + this.scopeOverridesHover.value?.hide(); + } + }; + + this.simpleHoverStore.add(setupCustomHover(this.hoverDelegate, workspaceTrustElement, content, options)); + return workspaceTrustElement; } private createScopeOverridesIndicator(): { element: HTMLElement; label: SimpleIconLabel } { @@ -79,7 +116,7 @@ export class SettingsTreeIndicatorsLabel implements IDisposable { const syncIgnoredLabel = new SimpleIconLabel(syncIgnoredElement); syncIgnoredLabel.text = localize('extensionSyncIgnoredLabel', 'Not synced'); const syncIgnoredHoverContent = localize('syncIgnoredTitle', "This setting is ignored during sync"); - this.syncIgnoredHover = setupCustomHover(this.hoverDelegate, syncIgnoredElement, syncIgnoredHoverContent); + this.simpleHoverStore.add(setupCustomHover(this.hoverDelegate, syncIgnoredElement, syncIgnoredHoverContent)); return syncIgnoredElement; } @@ -91,7 +128,7 @@ export class SettingsTreeIndicatorsLabel implements IDisposable { } private render() { - const elementsToShow = [this.scopeOverridesElement, this.syncIgnoredElement, this.defaultOverrideIndicatorElement].filter(element => { + const elementsToShow = [this.scopeOverridesElement, this.workspaceTrustElement, this.syncIgnoredElement, this.defaultOverrideIndicatorElement].filter(element => { return element.style.display !== 'none'; }); @@ -109,6 +146,11 @@ export class SettingsTreeIndicatorsLabel implements IDisposable { } } + updateWorkspaceTrust(element: SettingsTreeSettingElement) { + this.workspaceTrustElement.style.display = element.isUntrusted ? 'inline' : 'none'; + this.render(); + } + updateSyncIgnored(element: SettingsTreeSettingElement, ignoredSettings: string[]) { this.syncIgnoredElement.style.display = this.userDataSyncEnablementService.isEnabled() && ignoredSettings.includes(element.setting.key) ? 'inline' : 'none'; @@ -128,14 +170,38 @@ export class SettingsTreeIndicatorsLabel implements IDisposable { dispose() { this.scopeOverridesHover.dispose(); - this.syncIgnoredHover?.dispose(); + this.simpleHoverStore.dispose(); } - updateScopeOverrides(element: SettingsTreeSettingElement, elementDisposables: DisposableStore, onDidClickOverrideElement: Emitter) { + updateScopeOverrides(element: SettingsTreeSettingElement, elementDisposables: DisposableStore, onDidClickOverrideElement: Emitter, onApplyFilter: Emitter) { this.scopeOverridesElement.innerText = ''; this.scopeOverridesElement.style.display = 'none'; - const profileFeatureEnabled = this.configurationService.getValue('workbench.experimental.settingsProfiles.enabled'); - if (profileFeatureEnabled && !element.setting.isLanguageTagSetting && element.matchesScope(ConfigurationTarget.APPLICATION, false)) { + if (element.hasPolicyValue) { + // If the setting falls under a policy, then no matter what the user sets, the policy value takes effect. + this.scopeOverridesElement.style.display = 'inline'; + this.scopeOverridesElement.classList.add('with-custom-hover'); + + const policyText = localize('policyLabelText', "Managed by policy"); + this.scopeOverridesLabel.text = policyText; + + const contentFallback = localize('policyDescription', "This setting is managed by your organization."); + const contentMarkdownString = contentFallback + ` [${localize('policyFilterLink', "View policy settings")}](policy-settings).`; + const content: ITooltipMarkdownString = { + markdown: { + value: contentMarkdownString, + isTrusted: false, + supportHtml: false + }, + markdownNotSupportedFallback: contentFallback + }; + const options: IUpdatableHoverOptions = { + linkHandler: (url: string) => { + onApplyFilter.fire(`@${POLICY_SETTING_TAG}`); + this.scopeOverridesHover.value?.hide(); + } + }; + this.scopeOverridesHover.value = setupCustomHover(this.hoverDelegate, this.scopeOverridesElement, content, options); + } else if (this.profilesEnabled && element.matchesScope(ConfigurationTarget.APPLICATION, false)) { // If the setting is an application-scoped setting, there are no overrides so we can use this // indicator to display that information instead. this.scopeOverridesElement.style.display = 'inline'; @@ -290,22 +356,29 @@ function getAccessibleScopeDisplayMidSentenceText(completeScope: string, languag return localizedScope; } -export function getIndicatorsLabelAriaLabel(element: SettingsTreeSettingElement, configurationService: IConfigurationService, languageService: ILanguageService): string { +export function getIndicatorsLabelAriaLabel(element: SettingsTreeSettingElement, configurationService: IConfigurationService, userDataProfilesService: IUserDataProfilesService, languageService: ILanguageService): string { const ariaLabelSections: string[] = []; - const profileFeatureEnabled = configurationService.getValue('workbench.experimental.settingsProfiles.enabled'); - if (profileFeatureEnabled && element.matchesScope(ConfigurationTarget.APPLICATION, false)) { - ariaLabelSections.push(localize('applicationSettingDescriptionAccessible', "Setting value retained when switching profiles")); + // Add workspace trust text + if (element.isUntrusted) { + ariaLabelSections.push(localize('workspaceUntrustedAriaLabel', "Workspace untrusted; setting value will not take effect")); } - // Add other overrides text - const otherOverridesStart = element.isConfigured ? - localize('alsoConfiguredIn', "Also modified in") : - localize('configuredIn', "Modified in"); - const otherOverridesList = element.overriddenScopeList - .map(scope => getAccessibleScopeDisplayMidSentenceText(scope, languageService)).join(', '); - if (element.overriddenScopeList.length) { - ariaLabelSections.push(`${otherOverridesStart} ${otherOverridesList}`); + const profilesEnabled = userDataProfilesService.isEnabled(); + if (element.hasPolicyValue) { + ariaLabelSections.push(localize('policyDescriptionAccessible', "Managed by organization policy")); + } else if (profilesEnabled && element.matchesScope(ConfigurationTarget.APPLICATION, false)) { + ariaLabelSections.push(localize('applicationSettingDescriptionAccessible', "Setting value retained when switching profiles")); + } else { + // Add other overrides text + const otherOverridesStart = element.isConfigured ? + localize('alsoConfiguredIn', "Also modified in") : + localize('configuredIn', "Modified in"); + const otherOverridesList = element.overriddenScopeList + .map(scope => getAccessibleScopeDisplayMidSentenceText(scope, languageService)).join(', '); + if (element.overriddenScopeList.length) { + ariaLabelSections.push(`${otherOverridesStart} ${otherOverridesList}`); + } } // Add sync ignored text diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 834aab26324..4fa2edb2ff0 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -33,17 +33,16 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { ConfigurationTarget, getLanguageTagSettingPlainKey, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { editorBackground, editorErrorForeground, editorInfoForeground, errorForeground, focusBorder, foreground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground } from 'vs/platform/theme/common/colorRegistry'; -import { attachButtonStyler, attachInputBoxStyler, attachSelectBoxStyler, attachStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; +import { editorBackground, errorForeground, focusBorder, foreground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground } from 'vs/platform/theme/common/colorRegistry'; +import { attachButtonStyler, attachInputBoxStyler, attachSelectBoxStyler, attachStyler } from 'vs/platform/theme/common/styler'; import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { getIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerge'; import { ITOCEntry } from 'vs/workbench/contrib/preferences/browser/settingsLayout'; import { inspectSetting, ISettingsEditorViewState, settingKeyToDisplayFormat, SettingsTreeElement, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeNewExtensionsElement, SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; import { ExcludeSettingWidget, ISettingListChangeEvent, IListDataItem, ListSettingWidget, ObjectSettingDropdownWidget, IObjectDataItem, IObjectEnumOption, ObjectValue, IObjectValueSuggester, IObjectKeySuggester, ObjectSettingCheckboxWidget } from 'vs/workbench/contrib/preferences/browser/settingsWidgets'; -import { LANGUAGE_SETTING_TAG, POLICY_SETTING_TAG, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences'; +import { LANGUAGE_SETTING_TAG, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { ISetting, ISettingsGroup, SettingValueType } from 'vs/workbench/services/preferences/common/preferences'; import { getDefaultIgnoredSettings, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; @@ -64,6 +63,7 @@ import { focusedRowBackground, focusedRowBorder, rowHoverBackground, settingsHea import { getIndicatorsLabelAriaLabel, ISettingOverrideClickEvent, SettingsTreeIndicatorsLabel } from 'vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; const $ = DOM.$; @@ -582,7 +582,7 @@ function getFlatSettings(settingsGroups: ISettingsGroup[]) { } interface IDisposableTemplate { - toDispose: DisposableStore; + readonly toDispose: DisposableStore; } interface ISettingItemTemplate extends IDisposableTemplate { @@ -592,13 +592,12 @@ interface ISettingItemTemplate extends IDisposableTemplate { containerElement: HTMLElement; categoryElement: HTMLElement; labelElement: SimpleIconLabel; - policyWarningElement: HTMLElement; descriptionElement: HTMLElement; controlElement: HTMLElement; deprecationWarningElement: HTMLElement; indicatorsLabel: SettingsTreeIndicatorsLabel; toolbar: ToolBar; - elementDisposables: DisposableStore; + readonly elementDisposables: DisposableStore; } interface ISettingBoolItemTemplate extends ISettingItemTemplate { @@ -648,7 +647,6 @@ interface IGroupTitleTemplate extends IDisposableTemplate { parent: HTMLElement; } -const SETTINGS_UNTRUSTED_TEMPLATE_ID = 'settings.untrusted.template'; const SETTINGS_TEXT_TEMPLATE_ID = 'settings.text.template'; const SETTINGS_MULTILINE_TEXT_TEMPLATE_ID = 'settings.multilineText.template'; const SETTINGS_NUMBER_TEMPLATE_ID = 'settings.number.template'; @@ -801,19 +799,16 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre const deprecationWarningElement = DOM.append(container, $('.setting-item-deprecation-message')); - const policyWarningElement = this.renderPolicyLabel(container, toDispose); - const toolbarContainer = DOM.append(container, $('.setting-toolbar-container')); const toolbar = this.renderSettingToolbar(toolbarContainer); const template: ISettingItemTemplate = { toDispose, - elementDisposables: new DisposableStore(), + elementDisposables: toDispose.add(new DisposableStore()), containerElement: container, categoryElement, labelElement, - policyWarningElement, descriptionElement, controlElement, deprecationWarningElement, @@ -848,33 +843,6 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre }); } - protected renderPolicyLabel(container: HTMLElement, toDispose: DisposableStore): HTMLElement { - const policyWarningElement = DOM.append(container, $('.setting-item-policy-description')); - const policyIcon = DOM.append(policyWarningElement, $('span.codicon.codicon-lock')); - toDispose.add(attachStylerCallback(this._themeService, { editorInfoForeground }, colors => { - policyIcon.style.setProperty('--organization-policy-icon-color', colors.editorInfoForeground?.toString() || ''); - })); - const element = DOM.append(policyWarningElement, $('span')); - element.textContent = localize('policyLabel', "This setting is managed by your organization."); - const viewPolicyLabel = localize('viewPolicySettings', "View policy settings"); - const linkElement: HTMLAnchorElement = DOM.append(policyWarningElement, $('a')); - linkElement.textContent = viewPolicyLabel; - linkElement.setAttribute('tabindex', '0'); - linkElement.href = '#'; - toDispose.add(DOM.addStandardDisposableListener(linkElement, DOM.EventType.CLICK, (e: MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - this._onApplyFilter.fire(`@${POLICY_SETTING_TAG}`); - })); - toDispose.add(DOM.addStandardDisposableListener(linkElement, DOM.EventType.KEY_DOWN, (e: IKeyboardEvent) => { - if (e.equals(KeyCode.Enter) || e.equals(KeyCode.Space)) { - e.stopPropagation(); - this._onApplyFilter.fire(`@${POLICY_SETTING_TAG}`); - } - })); - return policyWarningElement; - } - protected renderSettingToolbar(container: HTMLElement): ToolBar { const toggleMenuKeybinding = this._keybindingService.lookupKeybinding(SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU); let toggleMenuTitle = localize('settingsContextMenuTitle', "More Actions... "); @@ -895,7 +863,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre template.context = element; template.toolbar.context = element; const actions = this.disposableActionFactory(element.setting); - actions.forEach(a => isDisposable(a) && template.elementDisposables?.add(a)); + actions.forEach(a => isDisposable(a) && template.elementDisposables.add(a)); template.toolbar.setActions([], [...this.settingActions, ...actions]); const setting = element.setting; @@ -913,15 +881,13 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre template.descriptionElement.innerText = ''; if (element.setting.descriptionIsMarkdown) { - const disposables = new DisposableStore(); - template.elementDisposables.add(disposables); - const renderedDescription = this.renderSettingMarkdown(element, template.containerElement, element.description, disposables); + const renderedDescription = this.renderSettingMarkdown(element, template.containerElement, element.description, template.elementDisposables); template.descriptionElement.appendChild(renderedDescription); } else { template.descriptionElement.innerText = element.description; } - template.indicatorsLabel.updateScopeOverrides(element, template.elementDisposables, this._onDidClickOverrideElement); + template.indicatorsLabel.updateScopeOverrides(element, template.elementDisposables, this._onDidClickOverrideElement, this._onApplyFilter); const onChange = (value: any) => this._onDidChangeSetting.fire({ key: element.setting.key, @@ -932,8 +898,6 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre }); const deprecationText = element.setting.deprecationMessage || ''; if (deprecationText && element.setting.deprecationMessageIsMarkdown) { - const disposables = new DisposableStore(); - template.elementDisposables.add(disposables); template.deprecationWarningElement.innerText = ''; template.deprecationWarningElement.appendChild(this.renderSettingMarkdown(element, template.containerElement, element.setting.deprecationMessage!, template.elementDisposables)); } else { @@ -944,14 +908,13 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre this.renderValue(element, template, onChange); + template.indicatorsLabel.updateWorkspaceTrust(element); template.indicatorsLabel.updateSyncIgnored(element, this.ignoredSettings); template.indicatorsLabel.updateDefaultOverrideIndicator(element); template.elementDisposables.add(this.onDidChangeIgnoredSettings(() => { template.indicatorsLabel.updateSyncIgnored(element, this.ignoredSettings); })); - template.policyWarningElement.hidden = !element.hasPolicyValue; - this.updateSettingTabbable(element, template); template.elementDisposables.add(element.onDidChangeTabbable(() => { this.updateSettingTabbable(element, template); @@ -966,7 +929,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre } } - private renderSettingMarkdown(element: SettingsTreeSettingElement, container: HTMLElement, text: string, disposeables: DisposableStore): HTMLElement { + private renderSettingMarkdown(element: SettingsTreeSettingElement, container: HTMLElement, text: string, disposables: DisposableStore): HTMLElement { // Rewrite `#editor.fontSize#` to link format text = fixSettingLinks(text); @@ -983,7 +946,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre this._openerService.open(content, { allowCommands: true }).catch(onUnexpectedError); } }, - disposables: disposeables + disposables }, asyncRenderCallback: () => { const height = container.clientHeight; @@ -992,7 +955,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre } }, }); - disposeables.add(renderedMarkdown); + disposables.add(renderedMarkdown); renderedMarkdown.element.classList.add('setting-item-markdown'); cleanRenderedMarkdown(renderedMarkdown.element); @@ -1002,7 +965,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre protected abstract renderValue(dataElement: SettingsTreeSettingElement, template: ISettingItemTemplate, onChange: (value: any) => void): void; disposeTemplate(template: IDisposableTemplate): void { - dispose(template.toDispose); + template.toDispose.dispose(); } disposeElement(_element: ITreeNode, _index: number, template: IDisposableTemplate, _height: number | undefined): void { @@ -1577,7 +1540,6 @@ abstract class AbstractSettingTextRenderer extends AbstractSettingRenderer imple template.onChange = undefined; template.inputBox.value = dataElement.value; template.inputBox.setAriaLabel(dataElement.setting.key); - template.inputBox.inputElement.disabled = !!dataElement.hasPolicyValue; template.onChange = value => { if (!renderValidations(dataElement, template, false)) { onChange(value); @@ -1743,10 +1705,6 @@ export class SettingEnumRenderer extends AbstractSettingRenderer implements ITre } }; - if (template.selectElement) { - template.selectElement.disabled = !!dataElement.hasPolicyValue; - } - template.enumDescriptionElement.innerText = ''; } } @@ -1798,7 +1756,6 @@ export class SettingNumberRenderer extends AbstractSettingRenderer implements IT template.onChange = undefined; template.inputBox.value = dataElement.value; template.inputBox.setAriaLabel(dataElement.setting.key); - template.inputBox.setEnabled(!dataElement.hasPolicyValue); template.onChange = value => { if (!renderValidations(dataElement, template, false)) { onChange(nullNumParseFn(value)); @@ -1860,18 +1817,15 @@ export class SettingBoolRenderer extends AbstractSettingRenderer implements ITre const toolbar = this.renderSettingToolbar(toolbarContainer); toDispose.add(toolbar); - const policyWarningElement = this.renderPolicyLabel(container, toDispose); - const template: ISettingBoolItemTemplate = { toDispose, - elementDisposables: new DisposableStore(), + elementDisposables: toDispose.add(new DisposableStore()), containerElement: container, categoryElement, labelElement, controlElement, checkbox, - policyWarningElement, descriptionElement, deprecationWarningElement, indicatorsLabel, @@ -1896,57 +1850,10 @@ export class SettingBoolRenderer extends AbstractSettingRenderer implements ITre template.onChange = undefined; template.checkbox.checked = dataElement.value; template.checkbox.setTitle(dataElement.setting.key); - if (dataElement.hasPolicyValue) { - template.checkbox.disable(); - } else { - template.checkbox.enable(); - } template.onChange = onChange; } } -export class SettingUntrustedRenderer extends AbstractSettingRenderer implements ITreeRenderer { - templateId = SETTINGS_UNTRUSTED_TEMPLATE_ID; - - renderTemplate(container: HTMLElement): ISettingItemTemplate { - const template = this.renderCommonTemplate(null, container, 'untrusted'); - - const manageWorkspaceTrustLabel = localize('manageWorkspaceTrust', "Manage Workspace Trust"); - const trustLabelElement = $('.setting-item-trust-description'); - const untrustedWorkspaceIcon = DOM.append(trustLabelElement, $('span.codicon.codicon-workspace-untrusted')); - template.toDispose.add(attachStylerCallback(this._themeService, { editorErrorForeground }, colors => { - untrustedWorkspaceIcon.style.setProperty('--workspace-trust-state-untrusted-color', colors.editorErrorForeground?.toString() || ''); - })); - const element = DOM.append(trustLabelElement, $('span')); - element.textContent = localize('trustLabel', "This setting can only be applied in a trusted workspace"); - const linkElement: HTMLAnchorElement = DOM.append(trustLabelElement, $('a')); - linkElement.textContent = manageWorkspaceTrustLabel; - linkElement.setAttribute('tabindex', '0'); - linkElement.href = '#'; - template.toDispose.add(DOM.addStandardDisposableListener(linkElement, DOM.EventType.CLICK, (e: MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - this._commandService.executeCommand('workbench.trust.manage'); - })); - template.toDispose.add(DOM.addStandardDisposableListener(linkElement, DOM.EventType.KEY_DOWN, (e: IKeyboardEvent) => { - if (e.equals(KeyCode.Enter) || e.equals(KeyCode.Space)) { - this._commandService.executeCommand('workbench.trust.manage'); - e.stopPropagation(); - } - })); - - template.containerElement.insertBefore(trustLabelElement, template.descriptionElement); - - return template; - } - - renderElement(element: ITreeNode, index: number, templateData: ISettingItemTemplate): void { - super.renderSettingElement(element, index, templateData); - } - - protected renderValue(dataElement: SettingsTreeSettingElement, template: ISettingComplexItemTemplate, onChange: (value: string) => void): void { } -} - export class SettingTreeRenderers { readonly onDidClickOverrideElement: Event; @@ -2004,7 +1911,6 @@ export class SettingTreeRenderers { this._instantiationService.createInstance(SettingEnumRenderer, this.settingActions, actionFactory), this._instantiationService.createInstance(SettingObjectRenderer, this.settingActions, actionFactory), this._instantiationService.createInstance(SettingBoolObjectRenderer, this.settingActions, actionFactory), - this._instantiationService.createInstance(SettingUntrustedRenderer, this.settingActions, actionFactory), ]; this.onDidClickOverrideElement = Event.any(...settingRenderers.map(r => r.onDidClickOverrideElement)); @@ -2215,11 +2121,6 @@ class SettingsTreeDelegate extends CachedListVirtualDelegate extends ObjectTreeModel { } class SettingsTreeAccessibilityProvider implements IListAccessibilityProvider { - constructor(private readonly configurationService: IConfigurationService, private readonly languageService: ILanguageService) { + constructor(private readonly configurationService: IConfigurationService, private readonly languageService: ILanguageService, private readonly userDataProfilesService: IUserDataProfilesService) { } getAriaLabel(element: SettingsTreeElement) { @@ -2315,7 +2216,7 @@ class SettingsTreeAccessibilityProvider implements IListAccessibilityProvider { @IThemeService themeService: IThemeService, @IConfigurationService configurationService: IConfigurationService, @IInstantiationService instantiationService: IInstantiationService, - @ILanguageService languageService: ILanguageService + @ILanguageService languageService: ILanguageService, + @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService ) { super('SettingsTree', container, new SettingsTreeDelegate(), @@ -2360,7 +2262,7 @@ export class SettingsTree extends WorkbenchObjectTree { return e.id; } }, - accessibilityProvider: new SettingsTreeAccessibilityProvider(configurationService, languageService), + accessibilityProvider: new SettingsTreeAccessibilityProvider(configurationService, languageService, userDataProfilesService), styleController: id => new DefaultStyleController(DOM.createStyleSheet(container), id), filter: instantiationService.createInstance(SettingsTreeFilter, viewState), smoothScrolling: configurationService.getValue('workbench.list.smoothScrolling'), diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts index a9ac3c6746d..308e79c7c31 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts @@ -529,7 +529,7 @@ export class SettingsTreeModel { } private updateRequireTrustedTargetElements(): void { - this.updateSettings(arrays.flatten([...this._treeElementsBySettingName.values()]).filter(s => s.isUntrusted)); + this.updateSettings([...this._treeElementsBySettingName.values()].flat().filter(s => s.isUntrusted)); } private getTargetToInspect(settingScope: ConfigurationScope | undefined): SettingsTarget { @@ -541,11 +541,11 @@ export class SettingsTreeModel { } private updateSettings(settings: SettingsTreeSettingElement[]): void { - settings.forEach(element => { + for (const element of settings) { const target = this.getTargetToInspect(element.setting.scope); const inspectResult = inspectSetting(element.setting.key, target, this._viewState.languageFilter, this._configurationService); element.update(inspectResult, this._isWorkspaceTrusted); - }); + } } private createSettingsTreeGroupElement(tocEntry: ITOCEntry, parent?: SettingsTreeGroupElement): SettingsTreeGroupElement { @@ -612,19 +612,6 @@ export function inspectSetting(key: string, target: SettingsTarget, languageFilt target === ConfigurationTarget.WORKSPACE ? 'workspace' : 'workspaceFolder'; let isConfigured = typeof inspected[targetSelector] !== 'undefined'; - if (!isConfigured) { - if (target === ConfigurationTarget.APPLICATION) { - isConfigured = !!configurationService.restrictedSettings.application?.includes(key); - } else if (target === ConfigurationTarget.USER_LOCAL) { - isConfigured = !!configurationService.restrictedSettings.userLocal?.includes(key); - } else if (target === ConfigurationTarget.USER_REMOTE) { - isConfigured = !!configurationService.restrictedSettings.userRemote?.includes(key); - } else if (target === ConfigurationTarget.WORKSPACE) { - isConfigured = !!configurationService.restrictedSettings.workspace?.includes(key); - } else if (target instanceof URI) { - isConfigured = !!configurationService.restrictedSettings.workspaceFolder?.get(target)?.includes(key); - } - } const overrideIdentifiers = inspected.overrideIdentifiers; const inspectedLanguageOverrides = new Map>(); diff --git a/src/vs/workbench/contrib/quickaccess/browser/viewQuickAccess.ts b/src/vs/workbench/contrib/quickaccess/browser/viewQuickAccess.ts index 1ee86f9e52c..c08156d9246 100644 --- a/src/vs/workbench/contrib/quickaccess/browser/viewQuickAccess.ts +++ b/src/vs/workbench/contrib/quickaccess/browser/viewQuickAccess.ts @@ -19,7 +19,7 @@ import { Action2 } from 'vs/platform/actions/common/actions'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { IDebugService, REPL_VIEW_ID } from 'vs/workbench/contrib/debug/common/debug'; @@ -235,7 +235,7 @@ export class OpenViewPickerAction extends Action2 { super({ id: OpenViewPickerAction.ID, title: { value: localize('openView', "Open View"), original: 'Open View' }, - category: CATEGORIES.View, + category: Categories.View, f1: true }); } @@ -258,7 +258,7 @@ export class QuickAccessViewPickerAction extends Action2 { super({ id: QuickAccessViewPickerAction.ID, title: { value: localize('quickOpenView', "Quick Open View"), original: 'Quick Open View' }, - category: CATEGORIES.View, + category: Categories.View, f1: false, // hide quick pickers from command palette to not confuse with the other entry that shows a input field keybinding: { weight: KeybindingWeight.WorkbenchContrib, diff --git a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts index cdde19ded70..fa262bfda14 100644 --- a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts +++ b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts @@ -27,7 +27,7 @@ interface IConfiguration extends IWindowsConfiguration { editor?: { accessibilitySupport?: 'on' | 'off' | 'auto' }; security?: { workspace?: { trust?: { enabled?: boolean } } }; window: IWindowSettings & { experimental?: { windowControlsOverlay?: { enabled?: boolean }; useSandbox?: boolean } }; - workbench?: { experimental?: { settingsProfiles?: { enabled?: boolean } } }; + workbench?: { experimental?: { settingsProfiles?: { enabled?: boolean } }; enableExperiments?: boolean }; } export class SettingsChangeRelauncher extends Disposable implements IWorkbenchContribution { @@ -42,6 +42,7 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo private accessibilitySupport: 'on' | 'off' | 'auto' | undefined; private workspaceTrustEnabled: boolean | undefined; private settingsProfilesEnabled: boolean | undefined; + private experimentsEnabled: boolean | undefined; constructor( @IHostService private readonly hostService: IHostService, @@ -123,6 +124,12 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo changed = true; } + // Experiments + if (typeof config.workbench?.enableExperiments === 'boolean' && config.workbench.enableExperiments !== this.experimentsEnabled) { + this.experimentsEnabled = config.workbench.enableExperiments; + changed = true; + } + // Notify only when changed and we are the focused window (avoids notification spam across windows) if (notify && changed) { this.doConfirm( @@ -225,5 +232,5 @@ export class WorkspaceChangeExtHostRelauncher extends Disposable implements IWor } const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(SettingsChangeRelauncher, 'SettingsChangeRelauncher', LifecyclePhase.Restored); -workbenchRegistry.registerWorkbenchContribution(WorkspaceChangeExtHostRelauncher, 'WorkspaceChangeExtHostRelauncher', LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(SettingsChangeRelauncher, LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(WorkspaceChangeExtHostRelauncher, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/remote/browser/remote.contribution.ts b/src/vs/workbench/contrib/remote/browser/remote.contribution.ts index 4bb56230f2a..63e7f2df0b4 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.contribution.ts @@ -13,11 +13,11 @@ import { RemoteStatusIndicator } from 'vs/workbench/contrib/remote/browser/remot import { AutomaticPortForwarding, ForwardedPortsView, PortRestore } from 'vs/workbench/contrib/remote/browser/remoteExplorer'; const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchContributionsRegistry.registerWorkbenchContribution(ShowCandidateContribution, 'ShowCandidateContribution', LifecyclePhase.Ready); -workbenchContributionsRegistry.registerWorkbenchContribution(TunnelFactoryContribution, 'TunnelFactoryContribution', LifecyclePhase.Ready); -workbenchContributionsRegistry.registerWorkbenchContribution(RemoteAgentConnectionStatusListener, 'RemoteAgentConnectionStatusListener', LifecyclePhase.Eventually); -workbenchContributionsRegistry.registerWorkbenchContribution(RemoteStatusIndicator, 'RemoteStatusIndicator', LifecyclePhase.Starting); -workbenchContributionsRegistry.registerWorkbenchContribution(ForwardedPortsView, 'ForwardedPortsView', LifecyclePhase.Restored); -workbenchContributionsRegistry.registerWorkbenchContribution(PortRestore, 'PortRestore', LifecyclePhase.Eventually); -workbenchContributionsRegistry.registerWorkbenchContribution(AutomaticPortForwarding, 'AutomaticPortForwarding', LifecyclePhase.Eventually); -workbenchContributionsRegistry.registerWorkbenchContribution(RemoteMarkers, 'RemoteMarkers', LifecyclePhase.Eventually); +workbenchContributionsRegistry.registerWorkbenchContribution(ShowCandidateContribution, LifecyclePhase.Ready); +workbenchContributionsRegistry.registerWorkbenchContribution(TunnelFactoryContribution, LifecyclePhase.Ready); +workbenchContributionsRegistry.registerWorkbenchContribution(RemoteAgentConnectionStatusListener, LifecyclePhase.Eventually); +workbenchContributionsRegistry.registerWorkbenchContribution(RemoteStatusIndicator, LifecyclePhase.Starting); +workbenchContributionsRegistry.registerWorkbenchContribution(ForwardedPortsView, LifecyclePhase.Restored); +workbenchContributionsRegistry.registerWorkbenchContribution(PortRestore, LifecyclePhase.Eventually); +workbenchContributionsRegistry.registerWorkbenchContribution(AutomaticPortForwarding, LifecyclePhase.Eventually); +workbenchContributionsRegistry.registerWorkbenchContribution(RemoteMarkers, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts b/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts index e9f2f1ae804..15e4776d4a9 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts @@ -339,7 +339,7 @@ class OnAutoForwardedAction extends Disposable { choices.push(this.openPreviewChoice(tunnel)); } - if ((tunnel.tunnelLocalPort !== tunnel.tunnelRemotePort) && this.tunnelService.canElevate && isPortPrivileged(tunnel.tunnelRemotePort)) { + if ((tunnel.tunnelLocalPort !== tunnel.tunnelRemotePort) && this.tunnelService.canElevate && this.tunnelService.isPortPrivileged(tunnel.tunnelRemotePort)) { // Privileged ports are not on Windows, so it's safe to use "superuser" message += nls.localize('remote.tunnelsView.elevationMessage', "You'll need to run as superuser to use port {0} locally. ", tunnel.tunnelRemotePort); choices.unshift(this.elevateChoice(tunnel)); diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index b087ee0bd88..d598e2d2ece 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -23,7 +23,7 @@ import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { ActionRunner, IAction } from 'vs/base/common/actions'; import { IMenuService, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { ILocalizedString } from 'vs/platform/action/common/action'; -import { createAndFillInContextMenuActions, createAndFillInActionBarActions, createActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { createAndFillInActionBarActions, createActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IRemoteExplorerService, TunnelModel, makeAddress, TunnelType, ITunnelItem, Tunnel, TUNNEL_VIEW_ID, parseAddress, CandidatePort, TunnelEditId, mapHasAddressLocalhostOrAllInterfaces, Attributes, TunnelSource } from 'vs/workbench/services/remote/common/remoteExplorerService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; @@ -35,7 +35,7 @@ import { IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platfor import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPane'; import { URI } from 'vs/base/common/uri'; -import { isAllInterfaces, isLocalhost, isPortPrivileged, ITunnelService, RemoteTunnel, TunnelPrivacyId, TunnelProtocol } from 'vs/platform/tunnel/common/tunnel'; +import { isAllInterfaces, isLocalhost, ITunnelService, RemoteTunnel, TunnelPrivacyId, TunnelProtocol } from 'vs/platform/tunnel/common/tunnel'; import { TunnelPrivacy } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -1023,14 +1023,11 @@ export class TunnelPanel extends ViewPane { this.portChangableContextKey.set(false); } - const menu = this.menuService.createMenu(MenuId.TunnelContext, this.table.contextKeyService); - const actions: IAction[] = []; - createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, actions); - menu.dispose(); - this.contextMenuService.showContextMenu({ + menuId: MenuId.TunnelContext, + menuActionOptions: { shouldForwardArgs: true }, + contextKeyService: this.table.contextKeyService, getAnchor: () => event.anchor, - getActions: () => actions, getActionViewItem: (action) => { const keybinding = this.keybindingService.lookupKeybinding(action.id); if (keybinding) { @@ -1141,13 +1138,13 @@ export namespace ForwardPortAction { export const TREEITEM_LABEL = nls.localize('remote.tunnel.forwardItem', "Forward Port"); const forwardPrompt = nls.localize('remote.tunnel.forwardPrompt', "Port number or address (eg. 3000 or 10.10.10.10:2000)."); - function validateInput(remoteExplorerService: IRemoteExplorerService, value: string, canElevate: boolean): { content: string; severity: Severity } | null { + function validateInput(remoteExplorerService: IRemoteExplorerService, tunnelService: ITunnelService, value: string, canElevate: boolean): { content: string; severity: Severity } | null { const parsed = parseAddress(value); if (!parsed) { return { content: invalidPortString, severity: Severity.Error }; } else if (parsed.port >= maxPortNumber) { return { content: invalidPortNumberString, severity: Severity.Error }; - } else if (canElevate && isPortPrivileged(parsed.port)) { + } else if (canElevate && tunnelService.isPortPrivileged(parsed.port)) { return { content: requiresSudoString, severity: Severity.Info }; } else if (mapHasAddressLocalhostOrAllInterfaces(remoteExplorerService.tunnelModel.forwarded, parsed.host, parsed.port)) { return { content: alreadyForwarded, severity: Severity.Error }; @@ -1177,7 +1174,7 @@ export namespace ForwardPortAction { }).then(tunnel => error(notificationService, tunnel, parsed!.host, parsed!.port)); } }, - validationMessage: (value) => validateInput(remoteExplorerService, value, tunnelService.canElevate), + validationMessage: (value) => validateInput(remoteExplorerService, tunnelService, value, tunnelService.canElevate), placeholder: forwardPrompt }); }; @@ -1193,7 +1190,7 @@ export namespace ForwardPortAction { await viewsService.openView(TunnelPanel.ID, true); const value = await quickInputService.input({ prompt: forwardPrompt, - validateInput: (value) => Promise.resolve(validateInput(remoteExplorerService, value, tunnelService.canElevate)) + validateInput: (value) => Promise.resolve(validateInput(remoteExplorerService, tunnelService, value, tunnelService.canElevate)) }); let parsed: { host: string; port: number } | undefined; if (value && (parsed = parseAddress(value))) { @@ -1439,12 +1436,12 @@ namespace ChangeLocalPortAction { export const ID = 'remote.tunnel.changeLocalPort'; export const LABEL = nls.localize('remote.tunnel.changeLocalPort', "Change Local Address Port"); - function validateInput(value: string, canElevate: boolean): { content: string; severity: Severity } | null { + function validateInput(tunnelService: ITunnelService, value: string, canElevate: boolean): { content: string; severity: Severity } | null { if (!value.match(/^[0-9]+$/)) { return { content: invalidPortString, severity: Severity.Error }; } else if (Number(value) >= maxPortNumber) { return { content: invalidPortNumberString, severity: Severity.Error }; - } else if (canElevate && isPortPrivileged(Number(value))) { + } else if (canElevate && tunnelService.isPortPrivileged(Number(value))) { return { content: requiresSudoString, severity: Severity.Info }; } return null; @@ -1487,7 +1484,7 @@ namespace ChangeLocalPortAction { } } }, - validationMessage: (value) => validateInput(value, tunnelService.canElevate), + validationMessage: (value) => validateInput(tunnelService, value, tunnelService.canElevate), placeholder: nls.localize('remote.tunnelsView.changePort', "New local port") }); } diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts index c1de0fcfdae..d95a33548dc 100644 --- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts @@ -10,7 +10,7 @@ import { ILabelService, ResourceLabelFormatting } from 'vs/platform/label/common import { OperatingSystem, isWeb, OS } from 'vs/base/common/platform'; import { Schemas } from 'vs/base/common/network'; import { IRemoteAgentService, RemoteExtensionLogFileName } from 'vs/workbench/services/remote/common/remoteAgentService'; -import { ILogService } from 'vs/platform/log/common/log'; +import { ILoggerService, ILogService } from 'vs/platform/log/common/log'; import { LogLevelChannel, LogLevelChannelClient } from 'vs/platform/log/common/logIpc'; import { IOutputChannelRegistry, Extensions as OutputExt, } from 'vs/workbench/services/output/common/output'; import { localize } from 'vs/nls'; @@ -25,7 +25,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { firstOrDefault } from 'vs/base/common/arrays'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { PersistentConnection } from 'vs/platform/remote/common/remoteAgentConnection'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { getRemoteName } from 'vs/platform/remote/common/remoteHosts'; @@ -33,6 +33,7 @@ import { IDownloadService } from 'vs/platform/download/common/download'; import { DownloadServiceChannel } from 'vs/platform/download/common/downloadIpc'; import { timeout } from 'vs/base/common/async'; import { TerminalLogConstants } from 'vs/platform/terminal/common/terminal'; +import { remotePtyHostLog, remoteServerLog } from 'vs/workbench/contrib/logs/common/logConstants'; export class LabelContribution implements IWorkbenchContribution { constructor( @@ -71,7 +72,8 @@ class RemoteChannelsContribution extends Disposable implements IWorkbenchContrib constructor( @ILogService logService: ILogService, @IRemoteAgentService remoteAgentService: IRemoteAgentService, - @IDownloadService downloadService: IDownloadService + @IDownloadService downloadService: IDownloadService, + @ILoggerService loggerService: ILoggerService, ) { super(); const updateRemoteLogLevel = () => { @@ -86,7 +88,7 @@ class RemoteChannelsContribution extends Disposable implements IWorkbenchContrib const connection = remoteAgentService.getConnection(); if (connection) { connection.registerChannel('download', new DownloadServiceChannel(downloadService)); - connection.registerChannel('logger', new LogLevelChannel(logService)); + connection.registerChannel('logger', new LogLevelChannel(logService, loggerService)); } } } @@ -99,8 +101,8 @@ class RemoteLogOutputChannels implements IWorkbenchContribution { remoteAgentService.getEnvironment().then(remoteEnv => { if (remoteEnv) { const outputChannelRegistry = Registry.as(OutputExt.OutputChannels); - outputChannelRegistry.registerChannel({ id: 'remoteExtensionLog', label: localize('remoteExtensionLog', "Remote Server"), file: joinPath(remoteEnv.logsPath, `${RemoteExtensionLogFileName}.log`), log: true }); - outputChannelRegistry.registerChannel({ id: 'remotePtyHostLog', label: localize('remotePtyHostLog', "Remote Pty Host"), file: joinPath(remoteEnv.logsPath, `${TerminalLogConstants.FileName}.log`), log: true }); + outputChannelRegistry.registerChannel({ id: remoteServerLog, label: localize('remoteExtensionLog', "Remote Server"), file: joinPath(remoteEnv.logsPath, `${RemoteExtensionLogFileName}.log`), log: true }); + outputChannelRegistry.registerChannel({ id: remotePtyHostLog, label: localize('remotePtyHostLog', "Remote Pty Host"), file: joinPath(remoteEnv.logsPath, `${TerminalLogConstants.FileName}.log`), log: true }); } }); } @@ -267,11 +269,11 @@ class InitialRemoteConnectionHealthContribution implements IWorkbenchContributio } const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchContributionsRegistry.registerWorkbenchContribution(LabelContribution, 'LabelContribution', LifecyclePhase.Starting); -workbenchContributionsRegistry.registerWorkbenchContribution(RemoteChannelsContribution, 'RemoteChannelsContribution', LifecyclePhase.Starting); -workbenchContributionsRegistry.registerWorkbenchContribution(RemoteInvalidWorkspaceDetector, 'RemoteInvalidWorkspaceDetector', LifecyclePhase.Starting); -workbenchContributionsRegistry.registerWorkbenchContribution(RemoteLogOutputChannels, 'RemoteLogOutputChannels', LifecyclePhase.Restored); -workbenchContributionsRegistry.registerWorkbenchContribution(InitialRemoteConnectionHealthContribution, 'InitialRemoteConnectionHealthContribution', LifecyclePhase.Ready); +workbenchContributionsRegistry.registerWorkbenchContribution(LabelContribution, LifecyclePhase.Starting); +workbenchContributionsRegistry.registerWorkbenchContribution(RemoteChannelsContribution, LifecyclePhase.Starting); +workbenchContributionsRegistry.registerWorkbenchContribution(RemoteInvalidWorkspaceDetector, LifecyclePhase.Starting); +workbenchContributionsRegistry.registerWorkbenchContribution(RemoteLogOutputChannels, LifecyclePhase.Restored); +workbenchContributionsRegistry.registerWorkbenchContribution(InitialRemoteConnectionHealthContribution, LifecyclePhase.Ready); const enableDiagnostics = true; @@ -281,7 +283,7 @@ if (enableDiagnostics) { super({ id: 'workbench.action.triggerReconnect', title: { value: localize('triggerReconnect', "Connection: Trigger Reconnect"), original: 'Connection: Trigger Reconnect' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true, }); } @@ -296,7 +298,7 @@ if (enableDiagnostics) { super({ id: 'workbench.action.pauseSocketWriting', title: { value: localize('pauseSocketWriting', "Connection: Pause socket writing"), original: 'Connection: Pause socket writing' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true, }); } diff --git a/src/vs/workbench/contrib/remote/electron-sandbox/remote.contribution.ts b/src/vs/workbench/contrib/remote/electron-sandbox/remote.contribution.ts index 74be20ebf6c..f884e73d9da 100644 --- a/src/vs/workbench/contrib/remote/electron-sandbox/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/electron-sandbox/remote.contribution.ts @@ -170,12 +170,12 @@ class WSLContextKeyInitializer extends Disposable implements IWorkbenchContribut } const workbenchContributionsRegistry = Registry.as(WorkbenchContributionsExtensions.Workbench); -workbenchContributionsRegistry.registerWorkbenchContribution(RemoteAgentDiagnosticListener, 'RemoteAgentDiagnosticListener', LifecyclePhase.Eventually); -workbenchContributionsRegistry.registerWorkbenchContribution(RemoteExtensionHostEnvironmentUpdater, 'RemoteExtensionHostEnvironmentUpdater', LifecyclePhase.Eventually); -workbenchContributionsRegistry.registerWorkbenchContribution(RemoteTelemetryEnablementUpdater, 'RemoteTelemetryEnablementUpdater', LifecyclePhase.Ready); -workbenchContributionsRegistry.registerWorkbenchContribution(RemoteEmptyWorkbenchPresentation, 'RemoteEmptyWorkbenchPresentation', LifecyclePhase.Ready); +workbenchContributionsRegistry.registerWorkbenchContribution(RemoteAgentDiagnosticListener, LifecyclePhase.Eventually); +workbenchContributionsRegistry.registerWorkbenchContribution(RemoteExtensionHostEnvironmentUpdater, LifecyclePhase.Eventually); +workbenchContributionsRegistry.registerWorkbenchContribution(RemoteTelemetryEnablementUpdater, LifecyclePhase.Ready); +workbenchContributionsRegistry.registerWorkbenchContribution(RemoteEmptyWorkbenchPresentation, LifecyclePhase.Ready); if (isWindows) { - workbenchContributionsRegistry.registerWorkbenchContribution(WSLContextKeyInitializer, 'WSLContextKeyInitializer', LifecyclePhase.Ready); + workbenchContributionsRegistry.registerWorkbenchContribution(WSLContextKeyInitializer, LifecyclePhase.Ready); } Registry.as(ConfigurationExtensions.Configuration) diff --git a/src/vs/workbench/contrib/remoteTunnel/electron-sandbox/remoteTunnel.contribution.ts b/src/vs/workbench/contrib/remoteTunnel/electron-sandbox/remoteTunnel.contribution.ts new file mode 100644 index 00000000000..5572254d5e2 --- /dev/null +++ b/src/vs/workbench/contrib/remoteTunnel/electron-sandbox/remoteTunnel.contribution.ts @@ -0,0 +1,382 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { IRemoteTunnelService, TunnelStatus } from 'vs/platform/remoteTunnel/common/remoteTunnel'; +import { AuthenticationSession, AuthenticationSessionsChangeEvent, IAuthenticationService } from 'vs/workbench/services/authentication/common/authentication'; +import { localize } from 'vs/nls'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { ILocalizedString } from 'vs/platform/action/common/action'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IStorageService, IStorageValueChangeEvent, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { ILogger, ILoggerService, ILogService } from 'vs/platform/log/common/log'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { URI } from 'vs/base/common/uri'; +import { join } from 'vs/base/common/path'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IStringDictionary } from 'vs/base/common/collections'; +import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; +import { registerLogChannel } from 'vs/workbench/services/output/common/output'; +import { IFileService } from 'vs/platform/files/common/files'; + +export const REMOTE_TUNNEL_CATEGORY: ILocalizedString = { + original: 'Remote Tunnel', + value: localize('remoteTunnel.category', 'Remote Tunnel') +}; + +export const REMOTE_TUNNEL_SIGNED_IN_KEY = 'remoteTunnelSignedIn'; +export const REMOTE_TUNNEL_SIGNED_IN = new RawContextKey(REMOTE_TUNNEL_SIGNED_IN_KEY, false); + +const CACHED_SESSION_STORAGE_KEY = 'remoteTunnelAccountPreference'; + +type ExistingSession = IQuickPickItem & { session: AuthenticationSession & { providerId: string } }; +type IAuthenticationProvider = { id: string; scopes: string[] }; +type AuthenticationProviderOption = IQuickPickItem & { provider: IAuthenticationProvider }; + +export class RemoteTunnelWorkbenchContribution extends Disposable implements IWorkbenchContribution { + + private readonly signedInContext: IContextKey; + + private readonly serverConfiguration: { authenticationProviders: IStringDictionary<{ scopes: string[] }> }; + + private initialized = false; + #authenticationInfo: { sessionId: string; token: string; providerId: string } | undefined; + + private readonly logger: ILogger; + + constructor( + @IAuthenticationService private readonly authenticationService: IAuthenticationService, + @IDialogService private readonly dialogService: IDialogService, + @IExtensionService private readonly extensionService: IExtensionService, + @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IProductService productService: IProductService, + @IStorageService private readonly storageService: IStorageService, + @ILoggerService loggerService: ILoggerService, + @ILogService logService: ILogService, + @IQuickInputService private readonly quickInputService: IQuickInputService, + @IEnvironmentService environmentService: IEnvironmentService, + @IFileService fileService: IFileService, + @IRemoteTunnelService private remoteTunnelService: IRemoteTunnelService + ) { + super(); + + const logPathURI = URI.file(join(environmentService.logsPath, 'remoteTunnel.log')); + + this.logger = this._register(loggerService.createLogger(logPathURI, { name: 'remoteTunnel' })); + + const promise = registerLogChannel('remoteTunnel', localize('remoteTunnel.outputTitle', "Remote Tunnel"), logPathURI, fileService, logService); + this._register(toDisposable(() => promise.cancel())); + + this.signedInContext = REMOTE_TUNNEL_SIGNED_IN.bindTo(this.contextKeyService); + + const serverConfiguration = productService.tunnelApplicationConfig; + if (!serverConfiguration || !productService.tunnelApplicationName) { + this.logger.error('Missing \'tunnelApplicationConfig\' or \'tunnelApplicationName\' in product.json. Remote tunneling is not available.'); + this.serverConfiguration = { authenticationProviders: {} }; + return; + } + this.serverConfiguration = serverConfiguration; + + this._register(this.remoteTunnelService.onDidTokenFailed(() => { + this.logger.info('Clearing authentication preference because of successive token failures.'); + this.clearAuthenticationPreference(); + })); + this._register(this.remoteTunnelService.onDidChangeTunnelStatus(status => { + if (status === TunnelStatus.Disconnected) { + this.logger.info('Clearing authentication preference because of tunnel disconnected.'); + this.clearAuthenticationPreference(); + } + })); + + // If the user signs out of the current session, reset our cached auth state in memory and on disk + this._register(this.authenticationService.onDidChangeSessions((e) => this.onDidChangeSessions(e.event))); + + // If another window changes the preferred session storage, reset our cached auth state in memory + this._register(this.storageService.onDidChangeValue(e => this.onDidChangeStorage(e))); + + this.registerTurnOnAction(); + this.registerTurnOffAction(); + + this.signedInContext.set(this.existingSessionId !== undefined); + + if (this.existingSessionId) { + this.initialize(true); + } + + } + + private get existingSessionId() { + return this.storageService.get(CACHED_SESSION_STORAGE_KEY, StorageScope.APPLICATION); + } + + private set existingSessionId(sessionId: string | undefined) { + this.logger.trace(`Saving authentication preference for ID ${sessionId}.`); + if (sessionId === undefined) { + this.storageService.remove(CACHED_SESSION_STORAGE_KEY, StorageScope.APPLICATION); + } else { + this.storageService.store(CACHED_SESSION_STORAGE_KEY, sessionId, StorageScope.APPLICATION, StorageTarget.MACHINE); + } + } + + public async initialize(silent: boolean = false) { + if (this.initialized) { + return true; + } + this.initialized = await this.doInitialize(silent); + this.signedInContext.set(this.initialized); + return this.initialized; + } + + /** + * + * Ensures that the store client is initialized, + * meaning that authentication is configured and it + * can be used to communicate with the remote storage service + */ + private async doInitialize(silent: boolean): Promise { + // Wait for authentication extensions to be registered + await this.extensionService.whenInstalledExtensionsRegistered(); + + // If we already have an existing auth session in memory, use that + if (this.#authenticationInfo !== undefined) { + return true; + } + + const authenticationSession = await this.getAuthenticationSession(silent); + if (authenticationSession !== undefined) { + this.#authenticationInfo = authenticationSession; + this.remoteTunnelService.updateAccount({ token: authenticationSession.token, authenticationProviderId: authenticationSession.providerId }); + } + + return authenticationSession !== undefined; + } + + private async getAuthenticationSession(silent: boolean) { + // If the user signed in previously and the session is still available, reuse that without prompting the user again + if (this.existingSessionId) { + this.logger.info(`Searching for existing authentication session with ID ${this.existingSessionId}`); + const existingSession = await this.getExistingSession(); + if (existingSession) { + this.logger.info(`Found existing authentication session with ID ${existingSession.session.id}`); + return { sessionId: existingSession.session.id, token: existingSession.session.idToken ?? existingSession.session.accessToken, providerId: existingSession.session.providerId }; + } else { + //this._didSignOut.fire(); + } + } + + // If we aren't supposed to prompt the user because + // we're in a silent flow, just return here + if (silent) { + return; + } + + // Ask the user to pick a preferred account + const authenticationSession = await this.getAccountPreference(); + if (authenticationSession !== undefined) { + this.existingSessionId = authenticationSession.id; + return { sessionId: authenticationSession.id, token: authenticationSession.idToken ?? authenticationSession.accessToken, providerId: authenticationSession.providerId }; + } + + return undefined; + } + + private async getAccountPreference(): Promise { + const quickpick = this.quickInputService.createQuickPick(); + quickpick.title = localize('accountPreference.title', 'Enable remote access by signing up to remote tunnels.'); + quickpick.ok = false; + quickpick.placeholder = localize('accountPreference.placeholder', "Select an account to sign in"); + quickpick.ignoreFocusOut = true; + quickpick.items = await this.createQuickpickItems(); + + return new Promise((resolve, reject) => { + quickpick.onDidHide((e) => { + resolve(undefined); + quickpick.dispose(); + }); + + quickpick.onDidAccept(async (e) => { + const selection = quickpick.selectedItems[0]; + const session = 'provider' in selection ? { ...await this.authenticationService.createSession(selection.provider.id, selection.provider.scopes), providerId: selection.provider.id } : ('session' in selection ? selection.session : undefined); + resolve(session); + quickpick.hide(); + }); + + quickpick.show(); + }); + } + + private async createQuickpickItems(): Promise<(ExistingSession | AuthenticationProviderOption | IQuickPickSeparator | IQuickPickItem & { canceledAuthentication: boolean })[]> { + const options: (ExistingSession | AuthenticationProviderOption | IQuickPickSeparator | IQuickPickItem & { canceledAuthentication: boolean })[] = []; + + options.push({ type: 'separator', label: localize('signed in', "Signed In") }); + + const sessions = await this.getAllSessions(); + options.push(...sessions); + + options.push({ type: 'separator', label: localize('others', "Others") }); + + for (const authenticationProvider of (await this.getAuthenticationProviders())) { + const signedInForProvider = sessions.some(account => account.session.providerId === authenticationProvider.id); + if (!signedInForProvider || this.authenticationService.supportsMultipleAccounts(authenticationProvider.id)) { + const providerName = this.authenticationService.getLabel(authenticationProvider.id); + options.push({ label: localize('sign in using account', "Sign in with {0}", providerName), provider: authenticationProvider }); + } + } + + return options; + } + + + private async getExistingSession() { + const accounts = await this.getAllSessions(); + return accounts.find((account) => account.session.id === this.existingSessionId); + } + + private async onDidChangeStorage(e: IStorageValueChangeEvent): Promise { + if (e.key === CACHED_SESSION_STORAGE_KEY && e.scope === StorageScope.APPLICATION) { + const newSessionId = this.existingSessionId; + const previousSessionId = this.#authenticationInfo?.sessionId; + + if (previousSessionId !== newSessionId) { + this.logger.trace(`Resetting authentication state because authentication session ID preference changed from ${previousSessionId} to ${newSessionId}.`); + this.#authenticationInfo = undefined; + this.initialized = false; + } + } + } + + private clearAuthenticationPreference(): void { + this.#authenticationInfo = undefined; + this.initialized = false; + this.existingSessionId = undefined; + this.signedInContext.set(false); + } + + private onDidChangeSessions(e: AuthenticationSessionsChangeEvent): void { + if (this.#authenticationInfo?.sessionId && e.removed.find(session => session.id === this.#authenticationInfo?.sessionId)) { + this.clearAuthenticationPreference(); + } + } + + /** + * + * Returns all authentication sessions available from {@link getAuthenticationProviders}. + */ + private async getAllSessions() { + const authenticationProviders = await this.getAuthenticationProviders(); + const accounts = new Map(); + let currentSession: ExistingSession | undefined; + + for (const provider of authenticationProviders) { + const sessions = await this.authenticationService.getSessions(provider.id, provider.scopes); + + for (const session of sessions) { + const item = { + label: session.account.label, + description: this.authenticationService.getLabel(provider.id), + session: { ...session, providerId: provider.id } + }; + accounts.set(item.session.account.id, item); + if (this.existingSessionId === session.id) { + currentSession = item; + } + } + } + + if (currentSession !== undefined) { + accounts.set(currentSession.session.account.id, currentSession); + } + + return [...accounts.values()]; + } + + /** + * Returns all authentication providers which can be used to authenticate + * to the remote storage service, based on product.json configuration + * and registered authentication providers. + */ + private async getAuthenticationProviders() { + // Get the list of authentication providers configured in product.json + const authenticationProviders = this.serverConfiguration.authenticationProviders; + const configuredAuthenticationProviders = Object.keys(authenticationProviders).reduce((result, id) => { + result.push({ id, scopes: authenticationProviders[id].scopes }); + return result; + }, []); + + // Filter out anything that isn't currently available through the authenticationService + const availableAuthenticationProviders = this.authenticationService.declaredProviders; + + return configuredAuthenticationProviders.filter(({ id }) => availableAuthenticationProviders.some(provider => provider.id === id)); + } + + private registerTurnOnAction() { + const that = this; + this._register(registerAction2(class ShareMachineAction extends Action2 { + constructor() { + super({ + id: 'workbench.remoteTunnel.actions.turnOn', + title: localize('remoteTunnel.turnOn', 'Turn on Remote Tunnel Access...'), + category: REMOTE_TUNNEL_CATEGORY, + precondition: ContextKeyExpr.equals(REMOTE_TUNNEL_SIGNED_IN_KEY, false), + menu: [{ + id: MenuId.CommandPalette, + }, + { + id: MenuId.AccountsContext, + group: '2_remoteTunnel', + when: ContextKeyExpr.equals(REMOTE_TUNNEL_SIGNED_IN_KEY, false), + }] + }); + } + + async run() { + return await that.initialize(false); + } + })); + } + + private registerTurnOffAction() { + const that = this; + this._register(registerAction2(class ResetShareMachineAuthenticationAction extends Action2 { + constructor() { + super({ + id: 'workbench.remoteTunnel.actions.turnOff', + title: localize('remoteTunnel.turnOff', 'Turn off Remote Tunnel Access...'), + category: REMOTE_TUNNEL_CATEGORY, + precondition: ContextKeyExpr.equals(REMOTE_TUNNEL_SIGNED_IN_KEY, true), + menu: [{ + id: MenuId.CommandPalette, + }, + { + id: MenuId.AccountsContext, + group: '2_remoteTunnel', + when: ContextKeyExpr.equals(REMOTE_TUNNEL_SIGNED_IN_KEY, true), + }] + }); + } + + async run() { + const result = await that.dialogService.confirm({ + type: 'info', + message: localize('remoteTunnel.turnOff.confirm', 'Do you want to turn off Remote Tunnel Access?'), + primaryButton: localize('remoteTunnel.turnOff.yesButton', 'Yes'), + }); + if (result.confirmed) { + that.clearAuthenticationPreference(); + that.remoteTunnelService.updateAccount(undefined); + } + } + })); + } + +} + +const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); +workbenchRegistry.registerWorkbenchContribution(RemoteTunnelWorkbenchContribution, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/sash/browser/sash.contribution.ts b/src/vs/workbench/contrib/sash/browser/sash.contribution.ts index ffcbfcce3fa..ca2bbe1d72b 100644 --- a/src/vs/workbench/contrib/sash/browser/sash.contribution.ts +++ b/src/vs/workbench/contrib/sash/browser/sash.contribution.ts @@ -16,7 +16,7 @@ import { isIOS } from 'vs/base/common/platform'; // Sash size contribution Registry.as(WorkbenchExtensions.Workbench) - .registerWorkbenchContribution(SashSettingsController, 'SashSettingsController', LifecyclePhase.Restored); + .registerWorkbenchContribution(SashSettingsController, LifecyclePhase.Restored); // Sash size configuration contribution Registry.as(ConfigurationExtensions.Configuration) diff --git a/src/vs/workbench/contrib/scm/browser/media/scm.css b/src/vs/workbench/contrib/scm/browser/media/scm.css index b040001c0c6..7116d4ba6e7 100644 --- a/src/vs/workbench/contrib/scm/browser/media/scm.css +++ b/src/vs/workbench/contrib/scm/browser/media/scm.css @@ -194,6 +194,19 @@ .scm-view .scm-input { height: 100%; padding-left: 11px; + border-radius: 2px; +} + +.scm-view .scm-input .margin { + width: 6px !important; +} + +.scm-input .monaco-scrollable-element { + left: 6px !important; +} + +.scm-view .scm-editor-container .monaco-editor { + border-radius: 2px !important; } .scm-view .scm-editor { @@ -285,6 +298,7 @@ box-sizing: border-box; padding: 1px; outline-offset: -1px; + border-radius: 2px; } .scm-editor-validation-container { @@ -324,7 +338,7 @@ position: absolute; pointer-events: none; z-index: 1; - padding: 2px 4px 3px 4px; + padding: 2px 6px 3px 6px; box-sizing: border-box; width: 100%; overflow: hidden; diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index 0ff79b1dc49..b2353ef9da5 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -16,7 +16,7 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions, Configur import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { SCMService } from 'vs/workbench/contrib/scm/common/scmService'; import { IViewContainersRegistry, ViewContainerLocation, Extensions as ViewContainerExtensions, IViewsRegistry } from 'vs/workbench/common/views'; import { SCMViewPaneContainer } from 'vs/workbench/contrib/scm/browser/scmViewPaneContainer'; @@ -39,7 +39,7 @@ ModesRegistry.registerLanguage({ }); Registry.as(WorkbenchExtensions.Workbench) - .registerWorkbenchContribution(DirtyDiffWorkbenchController, 'DirtyDiffWorkbenchController', LifecyclePhase.Restored); + .registerWorkbenchContribution(DirtyDiffWorkbenchController, LifecyclePhase.Restored); const sourceControlViewIcon = registerIcon('source-control-view-icon', Codicon.sourceControl, localize('sourceControlViewIcon', 'View icon of the Source Control view.')); @@ -108,10 +108,10 @@ viewsRegistry.registerViews([{ }], viewContainer); Registry.as(WorkbenchExtensions.Workbench) - .registerWorkbenchContribution(SCMActiveResourceContextKeyController, 'SCMActiveResourceContextKeyController', LifecyclePhase.Restored); + .registerWorkbenchContribution(SCMActiveResourceContextKeyController, LifecyclePhase.Restored); Registry.as(WorkbenchExtensions.Workbench) - .registerWorkbenchContribution(SCMStatusController, 'SCMStatusController', LifecyclePhase.Restored); + .registerWorkbenchContribution(SCMStatusController, LifecyclePhase.Restored); Registry.as(ConfigurationExtensions.Configuration).registerConfiguration({ id: 'scm', @@ -383,5 +383,5 @@ MenuRegistry.appendMenuItem(MenuId.SCMSourceControl, { when: ContextKeyExpr.equals('scmProviderHasRootUri', true) }); -registerSingleton(ISCMService, SCMService, true); -registerSingleton(ISCMViewService, SCMViewService, true); +registerSingleton(ISCMService, SCMService, InstantiationType.Delayed); +registerSingleton(ISCMViewService, SCMViewService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts b/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts index 3a37e9524d2..8708e7c1378 100644 --- a/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/anythingQuickAccess'; -import { IQuickInputButton, IKeyMods, quickPickItemScorerAccessor, QuickPickItemScorerAccessor, IQuickPick, IQuickPickItemWithResource, QuickInputHideReason } from 'vs/platform/quickinput/common/quickInput'; +import { IQuickInputButton, IKeyMods, quickPickItemScorerAccessor, QuickPickItemScorerAccessor, IQuickPick, IQuickPickItemWithResource, QuickInputHideReason, IQuickInputService, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { IPickerQuickAccessItem, PickerQuickAccessProvider, TriggerAction, FastAndSlowPicks, Picks, PicksWithActive } from 'vs/platform/quickinput/browser/pickerQuickAccess'; import { prepareQuery, IPreparedQuery, compareItemsByFuzzyScore, scoreItemFuzzy, FuzzyScorerCache } from 'vs/base/common/fuzzyScorer'; import { IFileQueryBuilderOptions, QueryBuilder } from 'vs/workbench/services/search/common/queryBuilder'; @@ -40,7 +40,7 @@ import { Schemas } from 'vs/base/common/network'; import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { ResourceMap } from 'vs/base/common/map'; import { SymbolsQuickAccessProvider } from 'vs/workbench/contrib/search/browser/symbolsQuickAccess'; -import { DefaultQuickAccessFilterValue } from 'vs/platform/quickinput/common/quickAccess'; +import { AnythingQuickAccessProviderRunOptions, DefaultQuickAccessFilterValue } from 'vs/platform/quickinput/common/quickAccess'; import { IWorkbenchQuickAccessConfiguration } from 'vs/workbench/browser/quickaccess'; import { GotoSymbolQuickAccessProvider } from 'vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; @@ -52,6 +52,10 @@ import { withNullAsUndefined } from 'vs/base/common/types'; import { Codicon } from 'vs/base/common/codicons'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { stripIcons } from 'vs/base/common/iconLabels'; +import { HelpQuickAccessProvider } from 'vs/platform/quickinput/browser/helpQuickAccess'; +import { CommandsQuickAccessProvider } from 'vs/workbench/contrib/quickaccess/browser/commandsQuickAccess'; +import { DEBUG_QUICK_ACCESS_PREFIX } from 'vs/workbench/contrib/debug/browser/debugCommands'; +import { TasksQuickAccessProvider } from 'vs/workbench/contrib/tasks/browser/tasksQuickAccess'; interface IAnythingQuickPickItem extends IPickerQuickAccessItem, IQuickPickItemWithResource { } @@ -178,7 +182,8 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider, token: CancellationToken): IDisposable { + override provide(picker: IQuickPick, token: CancellationToken, runOptions?: AnythingQuickAccessProviderRunOptions): IDisposable { const disposables = new DisposableStore(); // Update the pick state for this run @@ -233,7 +238,7 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider this.clearDecorations(activeEditorControl)); } - protected _getPicks(originalFilter: string, disposables: DisposableStore, token: CancellationToken): Picks | Promise> | FastAndSlowPicks | null { + protected _getPicks(originalFilter: string, disposables: DisposableStore, token: CancellationToken, runOptions?: AnythingQuickAccessProviderRunOptions): Picks | Promise> | FastAndSlowPicks | null { // Find a suitable range from the pattern looking for ":", "#" or "," // unless we have the `@` editor symbol character inside the filter @@ -316,10 +321,15 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider | Promise> | FastAndSlowPicks { + private doGetPicks( + filter: string, + options: AnythingQuickAccessProviderRunOptions & { enableEditorSymbolSearch: boolean }, + disposables: DisposableStore, + token: CancellationToken + ): Picks | Promise> | FastAndSlowPicks { const query = prepareQuery(filter); // Return early if we have editor symbol picks. We support this by: @@ -343,16 +353,24 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider; + if (this.pickState.isQuickNavigating) { + picks = historyEditorPicks; + } else { + picks = []; + if (options.includeHelp) { + picks.push(...this.getHelpPicks(query, token)); + } + if (historyEditorPicks.length !== 0) { + picks.push({ type: 'separator', label: localize('recentlyOpenedSeparator', "recently opened") } as IQuickPickSeparator); + picks.push(...historyEditorPicks); + } + } + return { - // Fast picks: editor history - picks: - (this.pickState.isQuickNavigating || historyEditorPicks.length === 0) ? - historyEditorPicks : - [ - { type: 'separator', label: localize('recentlyOpenedSeparator', "recently opened") }, - ...historyEditorPicks - ], + // Fast picks: help (if included) & editor history + picks, // Slow picks: files and symbols additionalPicks: (async (): Promise> => { @@ -732,6 +750,67 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider = this.helpQuickAccess.getQuickAccessProviders(); + const mapOfProviders = new Map(); + for (const provider of providers) { + mapOfProviders.set(provider.prefix, provider); + } + + const importantProviders: Array = []; + const AddProvider = (prefix: string, modifications: Partial = {}) => { + if (mapOfProviders.has(prefix)) { + const provider = mapOfProviders.get(prefix)!; + + // We swap the label and description in this to emphasize the ability + // not the prefix. + provider.label = provider.description!; + provider.description = provider.prefix; + + // If the user chooses 'Go to File' the help should go away as if they were + // entering a new mode + const providerSpecificOptions: AnythingQuickAccessProviderRunOptions | undefined = provider.prefix === AnythingQuickAccessProvider.PREFIX + ? undefined + : { includeHelp: true }; + + importantProviders.push({ + ...mapOfProviders.get(prefix)!, + ...modifications, + accept: () => { + this.quickInputService.quickAccess.show(provider.prefix, { + preserveValue: true, + providerOptions: providerSpecificOptions + }); + } + }); + } + }; + + // Acts as the ordering too + AddProvider(AnythingQuickAccessProvider.PREFIX); + AddProvider(CommandsQuickAccessProvider.PREFIX); + AddProvider(GotoSymbolQuickAccessProvider.PREFIX); + AddProvider(DEBUG_QUICK_ACCESS_PREFIX); + AddProvider(TasksQuickAccessProvider.PREFIX); + AddProvider(HelpQuickAccessProvider.PREFIX, { + // More concise + label: localize('more', 'More') + }); + + return importantProviders; + } + + //#endregion //#region Workspace Symbols (if enabled) diff --git a/src/vs/workbench/contrib/search/browser/replaceContributions.ts b/src/vs/workbench/contrib/search/browser/replaceContributions.ts index 5312753d6d2..42176f2aeec 100644 --- a/src/vs/workbench/contrib/search/browser/replaceContributions.ts +++ b/src/vs/workbench/contrib/search/browser/replaceContributions.ts @@ -2,7 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IReplaceService } from 'vs/workbench/contrib/search/common/replace'; import { ReplaceService, ReplacePreviewContentProvider } from 'vs/workbench/contrib/search/browser/replaceService'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -10,6 +10,6 @@ import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } fr import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; export function registerContributions(): void { - registerSingleton(IReplaceService, ReplaceService, true); - Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ReplacePreviewContentProvider, 'ReplacePreviewContentProvider', LifecyclePhase.Starting); + registerSingleton(IReplaceService, ReplaceService, InstantiationType.Delayed); + Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ReplacePreviewContentProvider, LifecyclePhase.Starting); } diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 6cedf1989b2..07e2cf84134 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -12,7 +12,7 @@ import { URI } from 'vs/base/common/uri'; import { ToggleCaseSensitiveKeybinding, TogglePreserveCaseKeybinding, ToggleRegexKeybinding, ToggleWholeWordKeybinding } from 'vs/editor/contrib/find/browser/findModel'; import { AbstractGotoLineQuickAccessProvider } from 'vs/editor/contrib/quickAccess/browser/gotoLineQuickAccess'; import * as nls from 'vs/nls'; -import { Action2, MenuId, MenuRegistry, registerAction2, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { Action2, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; import { ICommandAction } from 'vs/platform/action/common/action'; import { CommandsRegistry, ICommandHandler, ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -20,17 +20,16 @@ import { ConfigurationScope, Extensions as ConfigurationExtensions, IConfigurati import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IFileService } from 'vs/platform/files/common/files'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { IListService, WorkbenchListFocusContextKey, WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; +import { IListService, WorkbenchListFocusContextKey, WorkbenchCompressibleObjectTree } from 'vs/platform/list/browser/listService'; import { Extensions as QuickAccessExtensions, IQuickAccessRegistry } from 'vs/platform/quickinput/common/quickAccess'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { defaultQuickAccessContextKeyValue } from 'vs/workbench/browser/quickaccess'; -import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { Extensions as ViewExtensions, IViewContainersRegistry, IViewDescriptor, IViewDescriptorService, IViewsRegistry, IViewsService, ViewContainerLocation } from 'vs/workbench/common/views'; import { GotoSymbolQuickAccessProvider } from 'vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess'; @@ -39,7 +38,7 @@ import { getMultiSelectedResources, IExplorerService } from 'vs/workbench/contri import { ExplorerFolderContext, ExplorerRootContext, FilesExplorerFocusCondition, VIEWLET_ID as VIEWLET_ID_FILES } from 'vs/workbench/contrib/files/common/files'; import { AnythingQuickAccessProvider } from 'vs/workbench/contrib/search/browser/anythingQuickAccess'; import { registerContributions as replaceContributions } from 'vs/workbench/contrib/search/browser/replaceContributions'; -import { cancelSearch, clearHistoryCommand, clearSearchResults, CloseReplaceAction, collapseDeepestExpandedLevel, copyAllCommand, copyMatchCommand, copyPathCommand, expandAll, FindInFilesCommand, FocusNextInputAction, FocusNextSearchResultAction, FocusPreviousInputAction, FocusPreviousSearchResultAction, focusSearchListCommand, getSearchView, openSearchView, refreshSearch, RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction, ReplaceInFilesAction, toggleCaseSensitiveCommand, togglePreserveCaseCommand, toggleRegexCommand, ToggleSearchOnTypeAction, toggleWholeWordCommand } from 'vs/workbench/contrib/search/browser/searchActions'; +import { cancelSearch, clearHistoryCommand, clearSearchResults, CloseReplaceAction, collapseDeepestExpandedLevel, copyAllCommand, copyMatchCommand, copyPathCommand, expandAll, FindInFilesCommand, findOrReplaceInFiles, FocusNextInputAction, focusNextSearchResult, FocusPreviousInputAction, focusPreviousSearchResult, focusSearchListCommand, getMultiSelectedSearchResources, getSearchView, openSearchView, refreshSearch, RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction, toggleCaseSensitiveCommand, togglePreserveCaseCommand, toggleRegexCommand, toggleWholeWordCommand } from 'vs/workbench/contrib/search/browser/searchActions'; import { searchClearIcon, searchCollapseAllIcon, searchExpandAllIcon, searchRefreshIcon, searchStopIcon, searchShowAsTree, searchViewIcon, searchShowAsList } from 'vs/workbench/contrib/search/browser/searchIcons'; import { SearchView } from 'vs/workbench/contrib/search/browser/searchView'; import { registerContributions as searchWidgetContributions } from 'vs/workbench/contrib/search/browser/searchWidget'; @@ -48,7 +47,7 @@ import * as Constants from 'vs/workbench/contrib/search/common/constants'; import { resolveResourcesForSearchIncludes } from 'vs/workbench/services/search/common/queryBuilder'; import { getWorkspaceSymbols, IWorkspaceSymbol, SearchStateKey, SearchUIState } from 'vs/workbench/contrib/search/common/search'; import { ISearchHistoryService, SearchHistoryService } from 'vs/workbench/contrib/search/common/searchHistoryService'; -import { FileMatch, FileMatchOrMatch, FolderMatch, ISearchWorkbenchService, Match, RenderableMatch, SearchWorkbenchService } from 'vs/workbench/contrib/search/common/searchModel'; +import { FileMatch, FileMatchOrMatch, FolderMatch, FolderMatchWithResource, ISearchWorkbenchService, Match, RenderableMatch, SearchWorkbenchService } from 'vs/workbench/contrib/search/common/searchModel'; import * as SearchEditorConstants from 'vs/workbench/contrib/searchEditor/browser/constants'; import { SearchEditor } from 'vs/workbench/contrib/searchEditor/browser/searchEditor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -57,8 +56,8 @@ import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/b import { ISearchConfiguration, SearchSortOrder, SEARCH_EXCLUDE_CONFIG, VIEWLET_ID, ViewMode, VIEW_ID } from 'vs/workbench/services/search/common/search'; import { Extensions, IConfigurationMigrationRegistry } from 'vs/workbench/common/configuration'; -registerSingleton(ISearchWorkbenchService, SearchWorkbenchService, true); -registerSingleton(ISearchHistoryService, SearchHistoryService, true); +registerSingleton(ISearchWorkbenchService, SearchWorkbenchService, InstantiationType.Delayed); +registerSingleton(ISearchHistoryService, SearchHistoryService, InstantiationType.Delayed); replaceContributions(); searchWidgetContributions(); @@ -66,7 +65,7 @@ searchWidgetContributions(); const category = { value: nls.localize('search', "Search"), original: 'Search' }; KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'workbench.action.search.toggleQueryDetails', + id: Constants.ToggleQueryDetailsActionId, weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.or(Constants.SearchViewFocusedKey, SearchEditorConstants.InSearchEditor), primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyJ, @@ -104,7 +103,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ handler: (accessor) => { const searchView = getSearchView(accessor.get(IViewsService)); if (searchView) { - const tree: WorkbenchObjectTree = searchView.getControl(); + const tree: WorkbenchCompressibleObjectTree = searchView.getControl(); const viewer = searchView.getControl(); const focus = tree.getFocus()[0]; @@ -128,7 +127,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ handler: (accessor, args: any) => { const searchView = getSearchView(accessor.get(IViewsService)); if (searchView) { - const tree: WorkbenchObjectTree = searchView.getControl(); + const tree: WorkbenchCompressibleObjectTree = searchView.getControl(); searchView.open(tree.getFocus()[0], false, true, true); } } @@ -145,7 +144,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ handler: (accessor, args: any) => { const searchView = getSearchView(accessor.get(IViewsService)); if (searchView) { - const tree: WorkbenchObjectTree = searchView.getControl(); + const tree: WorkbenchCompressibleObjectTree = searchView.getControl(); accessor.get(IInstantiationService).createInstance(RemoveAction, tree, tree.getFocus()[0]!).run(); } } @@ -159,7 +158,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ handler: (accessor, args: any) => { const searchView = getSearchView(accessor.get(IViewsService)); if (searchView) { - const tree: WorkbenchObjectTree = searchView.getControl(); + const tree: WorkbenchCompressibleObjectTree = searchView.getControl(); accessor.get(IInstantiationService).createInstance(ReplaceAction, tree, tree.getFocus()[0] as Match, searchView).run(); } } @@ -174,7 +173,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ handler: (accessor, args: any) => { const searchView = getSearchView(accessor.get(IViewsService)); if (searchView) { - const tree: WorkbenchObjectTree = searchView.getControl(); + const tree: WorkbenchCompressibleObjectTree = searchView.getControl(); accessor.get(IInstantiationService).createInstance(ReplaceAllAction, searchView, tree.getFocus()[0] as FileMatch).run(); } } @@ -189,7 +188,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ handler: (accessor, args: any) => { const searchView = getSearchView(accessor.get(IViewsService)); if (searchView) { - const tree: WorkbenchObjectTree = searchView.getControl(); + const tree: WorkbenchCompressibleObjectTree = searchView.getControl(); accessor.get(IInstantiationService).createInstance(ReplaceAllInFolderAction, tree, tree.getFocus()[0] as FolderMatch).run(); } } @@ -229,6 +228,19 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } }); +const restrictSearchToFolderFromSearch: ICommandHandler = async (accessor, folderMatch?: FolderMatchWithResource) => { + return searchInFolderCommand(accessor, false, undefined, folderMatch); +}; + +const RESTRICT_SEARCH_TO_FOLDER_ID = 'search.restrictSearchToFolder'; +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: RESTRICT_SEARCH_TO_FOLDER_ID, + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.ResourceFolderFocusKey), + primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KeyF, + handler: restrictSearchToFolderFromSearch +}); + MenuRegistry.appendMenuItem(MenuId.SearchContext, { command: { id: Constants.ReplaceActionId, @@ -269,6 +281,16 @@ MenuRegistry.appendMenuItem(MenuId.SearchContext, { order: 2 }); +MenuRegistry.appendMenuItem(MenuId.SearchContext, { + group: 'search', + order: 3, + command: { + id: RESTRICT_SEARCH_TO_FOLDER_ID, + title: nls.localize('restrictResultsToFolder', "Restrict Search to Folder...") + }, + when: ContextKeyExpr.and(Constants.ResourceFolderFocusKey) +}); + KeybindingsRegistry.registerCommandAndKeybindingRule({ id: Constants.CopyMatchCommandId, weight: KeybindingWeight.WorkbenchContrib, @@ -369,7 +391,7 @@ CommandsRegistry.registerCommand({ registerAction2(class CancelSearchAction extends Action2 { constructor() { super({ - id: 'search.action.cancel', + id: Constants.CancelSearchActionId, title: { value: nls.localize('CancelSearchAction.label', "Cancel Search"), original: 'Cancel Search' @@ -399,7 +421,7 @@ registerAction2(class CancelSearchAction extends Action2 { registerAction2(class RefreshAction extends Action2 { constructor() { super({ - id: 'search.action.refreshSearchResults', + id: Constants.RefreshSearchResultsActionId, title: { value: nls.localize('RefreshAction.label', "Refresh"), original: 'Refresh' @@ -424,7 +446,7 @@ registerAction2(class RefreshAction extends Action2 { registerAction2(class CollapseDeepestExpandedLevelAction extends Action2 { constructor() { super({ - id: 'search.action.collapseSearchResults', + id: Constants.CollapseSearchResultsActionId, title: { value: nls.localize('CollapseDeepestExpandedLevelAction.label', "Collapse All"), original: 'Collapse All' @@ -449,7 +471,7 @@ registerAction2(class CollapseDeepestExpandedLevelAction extends Action2 { registerAction2(class ExpandAllAction extends Action2 { constructor() { super({ - id: 'search.action.expandSearchResults', + id: Constants.ExpandSearchResultsActionId, title: { value: nls.localize('ExpandAllAction.label', "Expand All"), original: 'Expand All' @@ -474,7 +496,7 @@ registerAction2(class ExpandAllAction extends Action2 { registerAction2(class ClearSearchResultsAction extends Action2 { constructor() { super({ - id: 'search.action.clearSearchResults', + id: Constants.ClearSearchResultsActionId, title: { value: nls.localize('ClearSearchResultsAction.label', "Clear Search Results"), original: 'Clear Search Results' @@ -499,7 +521,7 @@ registerAction2(class ClearSearchResultsAction extends Action2 { registerAction2(class ViewAsTreeAction extends Action2 { constructor() { super({ - id: 'search.action.viewAsTree', + id: Constants.ViewAsTreeActionId, title: { value: nls.localize('ViewAsTreeAction.label', "View as Tree"), original: 'View as Tree' @@ -511,7 +533,7 @@ registerAction2(class ViewAsTreeAction extends Action2 { menu: [{ id: MenuId.ViewTitle, group: 'navigation', - order: 3, + order: 2, when: ContextKeyExpr.and(ContextKeyExpr.equals('view', VIEW_ID), Constants.InTreeViewKey.toNegated()), }] }); @@ -528,7 +550,7 @@ registerAction2(class ViewAsTreeAction extends Action2 { registerAction2(class ViewAsListAction extends Action2 { constructor() { super({ - id: 'search.action.viewAsList', + id: Constants.ViewAsListActionId, title: { value: nls.localize('ViewAsListAction.label', "View as List"), original: 'View as List' @@ -540,7 +562,7 @@ registerAction2(class ViewAsListAction extends Action2 { menu: [{ id: MenuId.ViewTitle, group: 'navigation', - order: 3, + order: 2, when: ContextKeyExpr.and(ContextKeyExpr.equals('view', VIEW_ID), Constants.InTreeViewKey), }] }); @@ -584,17 +606,31 @@ const FocusSearchListCommand: ICommandAction = { }; MenuRegistry.addCommand(FocusSearchListCommand); +const searchInFolderFromExplorer: ICommandHandler = async (accessor, resource?: URI) => { + return searchInFolderCommand(accessor, true, resource); +}; -const searchInFolderCommand: ICommandHandler = async (accessor, resource?: URI) => { +const searchInFolderCommand: ICommandHandler = async (accessor, isFromExplorer: boolean, resource?: URI, folderMatch?: FolderMatchWithResource) => { const listService = accessor.get(IListService); const fileService = accessor.get(IFileService); const viewsService = accessor.get(IViewsService); const contextService = accessor.get(IWorkspaceContextService); const commandService = accessor.get(ICommandService); - const resources = getMultiSelectedResources(resource, listService, accessor.get(IEditorService), accessor.get(IExplorerService)); const searchConfig = accessor.get(IConfigurationService).getValue().search; const mode = searchConfig.mode; + let resources: URI[]; + + if (isFromExplorer) { + resources = getMultiSelectedResources(resource, listService, accessor.get(IEditorService), accessor.get(IExplorerService)); + } else { + const searchView = getSearchView(accessor.get(IViewsService)); + if (!searchView) { + return; + } + resources = getMultiSelectedSearchResources(searchView.getControl(), folderMatch, searchConfig); + } + const resolvedResources = fileService.resolveAll(resources.map(resource => ({ resource }))).then(results => { const folders: URI[] = []; results.forEach(result => { @@ -620,13 +656,13 @@ const searchInFolderCommand: ICommandHandler = async (accessor, resource?: URI) } }; -const FIND_IN_FOLDER_ID = 'filesExplorer.findInFolder'; +const FIND_IN_FOLDER_EXPLORER_ID = 'filesExplorer.findInFolder'; KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: FIND_IN_FOLDER_ID, + id: FIND_IN_FOLDER_EXPLORER_ID, weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerFolderContext), primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KeyF, - handler: searchInFolderCommand + handler: searchInFolderFromExplorer }); const FIND_IN_WORKSPACE_ID = 'filesExplorer.findInWorkspace'; @@ -653,7 +689,7 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { group: '4_search', order: 10, command: { - id: FIND_IN_FOLDER_ID, + id: FIND_IN_FOLDER_EXPLORER_ID, title: nls.localize('findInFolder', "Find in Folder...") }, when: ContextKeyExpr.and(ExplorerFolderContext) @@ -678,7 +714,7 @@ class ShowAllSymbolsAction extends Action2 { constructor( ) { super({ - id: 'workbench.action.showAllSymbols', + id: Constants.ShowAllSymbolsActionId, title: { value: nls.localize('showTriggerActions', "Go to Symbol in Workspace..."), original: 'Go to Symbol in Workspace...' @@ -746,10 +782,7 @@ class RegisterSearchViewContribution implements IWorkbenchContribution { .registerConfigurationMigrations([{ key: 'search.location', migrateFn: (value: any) => ({ value: undefined }) }]); } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(RegisterSearchViewContribution, 'RegisterSearchViewContribution', LifecyclePhase.Starting); - -// Actions -const registry = Registry.as(ActionExtensions.WorkbenchActions); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(RegisterSearchViewContribution, LifecyclePhase.Starting); // Find in Files by default is the same as View: Show Search, but can be configured to open a search editor instead with the `search.mode` binding KeybindingsRegistry.registerCommandAndKeybindingRule({ @@ -793,14 +826,77 @@ MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { order: 1 }); -registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusNextSearchResultAction, { primary: KeyCode.F4 }), 'Search: Focus Next Search Result', category.value, ContextKeyExpr.or(Constants.HasSearchResults, SearchEditorConstants.InSearchEditor)); -registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusPreviousSearchResultAction, { primary: KeyMod.Shift | KeyCode.F4 }), 'Search: Focus Previous Search Result', category.value, ContextKeyExpr.or(Constants.HasSearchResults, SearchEditorConstants.InSearchEditor)); +registerAction2(class FocusNextSearchResultAction extends Action2 { + constructor() { + super({ + id: Constants.FocusNextSearchResultActionId, + title: { + value: nls.localize('FocusNextSearchResult.label', 'Focus Next Search Result'), + original: 'Focus Next Search Result' + }, + keybinding: [{ + primary: KeyCode.F4, + weight: KeybindingWeight.WorkbenchContrib, + }], + category: category.value, + + precondition: ContextKeyExpr.or(Constants.HasSearchResults, SearchEditorConstants.InSearchEditor), + }); + } + + override async run(accessor: ServicesAccessor): Promise { + return focusNextSearchResult(accessor); + } +}); + +registerAction2(class FocusPreviousSearchResultAction extends Action2 { + constructor() { + super({ + id: Constants.FocusPreviousSearchResultActionId, + title: { + value: nls.localize('FocusPreviousSearchResult.label', 'Search: Focus Previous Search Result'), + original: 'Search: Focus Previous Search Result' + }, + keybinding: [{ + primary: KeyMod.Shift | KeyCode.F4, + weight: KeybindingWeight.WorkbenchContrib, + }], + category: category.value, + + precondition: ContextKeyExpr.or(Constants.HasSearchResults, SearchEditorConstants.InSearchEditor), + }); + } + + override async run(accessor: ServicesAccessor): Promise { + return focusPreviousSearchResult(accessor); + } +}); + +registerAction2(class ReplaceInFilesAction extends Action2 { + constructor() { + super({ + id: Constants.ReplaceInFilesActionId, + title: { + value: nls.localize('replaceInFiles', 'Search: Replace in Files'), + original: 'Search: Replace in Files' + }, + keybinding: [{ + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyH, + weight: KeybindingWeight.WorkbenchContrib, + }], + category: category.value, + }); + } + + override async run(accessor: ServicesAccessor): Promise { + return findOrReplaceInFiles(accessor, true); + } +}); -registry.registerWorkbenchAction(SyncActionDescriptor.from(ReplaceInFilesAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyH }), 'Search: Replace in Files', category.value); MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { group: '4_find_global', command: { - id: ReplaceInFilesAction.ID, + id: Constants.ReplaceInFilesActionId, title: nls.localize({ key: 'miReplaceInFiles', comment: ['&& denotes a mnemonic'] }, "Replace &&in Files") }, order: 2 @@ -852,13 +948,35 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ handler: (accessor, args: any) => { const searchView = getSearchView(accessor.get(IViewsService)); if (searchView) { - const tree: WorkbenchObjectTree = searchView.getControl(); + const tree: WorkbenchCompressibleObjectTree = searchView.getControl(); searchView.openEditorWithMultiCursor(tree.getFocus()[0]); } } }); +// --- Toggle Search On Type -registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleSearchOnTypeAction), 'Search: Toggle Search on Type', category.value); +registerAction2(class ToggleSearchOnTypeAction extends Action2 { + private static readonly searchOnTypeKey = 'search.searchOnType'; + + constructor( + ) { + super({ + id: Constants.ToggleSearchOnTypeActionId, + title: { + value: nls.localize('toggleTabs', 'Toggle Search on Type'), + original: 'Toggle Search on Type' + }, + category: category.value, + }); + + } + + override async run(accessor: ServicesAccessor): Promise { + const configurationService = accessor.get(IConfigurationService); + const searchOnType = configurationService.getValue(ToggleSearchOnTypeAction.searchOnTypeKey); + return configurationService.updateValue(ToggleSearchOnTypeAction.searchOnTypeKey, !searchOnType); + } +}); // Register Quick Access Handler const quickAccessRegistry = Registry.as(QuickAccessExtensions.Quickaccess); @@ -1128,7 +1246,7 @@ CommandsRegistry.registerCommand('_executeWorkspaceSymbolProvider', async functi MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { group: '3_global_nav', command: { - id: 'workbench.action.showAllSymbols', + id: Constants.ShowAllSymbolsActionId, title: nls.localize({ key: 'miGotoSymbolInWorkspace', comment: ['&& denotes a mnemonic'] }, "Go to Symbol in &&Workspace...") }, order: 2 diff --git a/src/vs/workbench/contrib/search/browser/searchActions.ts b/src/vs/workbench/contrib/search/browser/searchActions.ts index cd458000ec8..faa4fe8452a 100644 --- a/src/vs/workbench/contrib/search/browser/searchActions.ts +++ b/src/vs/workbench/contrib/search/browser/searchActions.ts @@ -15,7 +15,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ILabelService } from 'vs/platform/label/common/label'; -import { getSelectionKeyboardEvent, WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; +import { getSelectionKeyboardEvent, WorkbenchCompressibleObjectTree } from 'vs/platform/list/browser/listService'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IViewsService } from 'vs/workbench/common/views'; import { searchRemoveIcon, searchReplaceAllIcon, searchReplaceIcon } from 'vs/workbench/contrib/search/browser/searchIcons'; @@ -31,6 +31,7 @@ import { SearchEditorInput } from 'vs/workbench/contrib/searchEditor/browser/sea import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ISearchConfiguration, ISearchConfigurationProperties, VIEW_ID } from 'vs/workbench/services/search/common/search'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { URI } from 'vs/base/common/uri'; export function isSearchViewFocused(viewsService: IViewsService): boolean { const searchView = getSearchView(viewsService); @@ -129,26 +130,6 @@ export class FocusPreviousInputAction extends Action { } } -export abstract class FindOrReplaceInFilesAction extends Action { - - constructor(id: string, label: string, protected viewsService: IViewsService, - private expandSearchReplaceWidget: boolean - ) { - super(id, label); - } - - override run(): Promise { - return openSearchView(this.viewsService, false).then(openedView => { - if (openedView) { - const searchAndReplaceWidget = openedView.searchAndReplaceWidget; - searchAndReplaceWidget.toggleReplace(this.expandSearchReplaceWidget); - - const updatedText = openedView.updateTextFromFindWidgetOrSelection({ allowUnselectedWord: !this.expandSearchReplaceWidget }); - openedView.searchAndReplaceWidget.focus(undefined, updatedText, updatedText); - } - }); - } -} export interface IFindInFilesArgs { query?: string; replace?: string; @@ -162,6 +143,7 @@ export interface IFindInFilesArgs { useExcludeSettingsAndIgnoreFiles?: boolean; onlyOpenEditors?: boolean; } + export const FindInFilesCommand: ICommandHandler = (accessor, args: IFindInFilesArgs = {}) => { const searchConfig = accessor.get(IConfigurationService).getValue().search; const mode = searchConfig.mode; @@ -197,17 +179,6 @@ export const FindInFilesCommand: ICommandHandler = (accessor, args: IFindInFiles } }; -export class ReplaceInFilesAction extends FindOrReplaceInFilesAction { - - static readonly ID = 'workbench.action.replaceInFiles'; - static readonly LABEL = nls.localize('replaceInFiles', "Replace in Files"); - - constructor(id: string, label: string, - @IViewsService viewsService: IViewsService) { - super(id, label, viewsService, /*expandSearchReplaceWidget=*/true); - } -} - export class CloseReplaceAction extends Action { constructor(id: string, label: string, @@ -226,29 +197,6 @@ export class CloseReplaceAction extends Action { } } -// --- Toggle Search On Type - -export class ToggleSearchOnTypeAction extends Action { - - static readonly ID = 'workbench.action.toggleSearchOnType'; - static readonly LABEL = nls.localize('toggleTabs', "Toggle Search on Type"); - - private static readonly searchOnTypeKey = 'search.searchOnType'; - - constructor( - id: string, - label: string, - @IConfigurationService private readonly configurationService: IConfigurationService - ) { - super(id, label); - } - - override run(): Promise { - const searchOnType = this.configurationService.getValue(ToggleSearchOnTypeAction.searchOnTypeKey); - return this.configurationService.updateValue(ToggleSearchOnTypeAction.searchOnTypeKey, !searchOnType); - } -} - export function expandAll(accessor: ServicesAccessor) { const viewsService = accessor.get(IViewsService); const searchView = getSearchView(viewsService); @@ -292,15 +240,22 @@ export function collapseDeepestExpandedLevel(accessor: ServicesAccessor) { let canCollapseFileMatchLevel = false; let canCollapseFirstLevel = false; - if (node instanceof FolderMatch) { + if (node instanceof FolderMatchWorkspaceRoot) { while (node = navigator.next()) { if (node instanceof Match) { canCollapseFileMatchLevel = true; break; } if (searchView.isTreeLayoutViewVisible && !canCollapseFirstLevel) { - const immediateParent = node.parent(); - if (immediateParent instanceof FolderMatchWorkspaceRoot || immediateParent instanceof FolderMatchNoRoot) { + let nodeToTest = node; + + if (node instanceof FolderMatch) { + nodeToTest = node.compressionStartParent ?? node; + } + + const immediateParent = nodeToTest.parent(); + + if (!(immediateParent instanceof FolderMatchWorkspaceRoot || immediateParent instanceof FolderMatchNoRoot || immediateParent instanceof SearchResult)) { canCollapseFirstLevel = true; } } @@ -318,10 +273,17 @@ export function collapseDeepestExpandedLevel(accessor: ServicesAccessor) { node = navigator.first(); if (node) { do { - const immediateParent = node.parent(); + + let nodeToTest = node; + + if (node instanceof FolderMatch) { + nodeToTest = node.compressionStartParent ?? node; + } + const immediateParent = nodeToTest.parent(); + if (immediateParent instanceof FolderMatchWorkspaceRoot || immediateParent instanceof FolderMatchNoRoot) { - if (viewer.hasElement(immediateParent) && viewer.isCollapsed(immediateParent)) { - viewer.collapse(immediateParent, true); + if (viewer.hasElement(node)) { + viewer.collapse(node, true); } else { viewer.collapseAll(); } @@ -343,115 +305,49 @@ export function collapseDeepestExpandedLevel(accessor: ServicesAccessor) { } } -export class FocusNextSearchResultAction extends Action { - static readonly ID = 'search.action.focusNextSearchResult'; - static readonly LABEL = nls.localize('FocusNextSearchResult.label', "Focus Next Search Result"); - - constructor(id: string, label: string, - @IViewsService private readonly viewsService: IViewsService, - @IEditorService private readonly editorService: IEditorService, - ) { - super(id, label); +export async function focusNextSearchResult(accessor: ServicesAccessor): Promise { + const editorService = accessor.get(IEditorService); + const input = editorService.activeEditor; + if (input instanceof SearchEditorInput) { + // cast as we cannot import SearchEditor as a value b/c cyclic dependency. + return (editorService.activeEditorPane as SearchEditor).focusNextResult(); } - override async run(): Promise { - const input = this.editorService.activeEditor; - if (input instanceof SearchEditorInput) { - // cast as we cannot import SearchEditor as a value b/c cyclic dependency. - return (this.editorService.activeEditorPane as SearchEditor).focusNextResult(); - } - - return openSearchView(this.viewsService).then(searchView => { - searchView?.selectNextMatch(); - }); - } + return openSearchView(accessor.get(IViewsService)).then(searchView => { + searchView?.selectNextMatch(); + }); } -export class FocusPreviousSearchResultAction extends Action { - static readonly ID = 'search.action.focusPreviousSearchResult'; - static readonly LABEL = nls.localize('FocusPreviousSearchResult.label', "Focus Previous Search Result"); - - constructor(id: string, label: string, - @IViewsService private readonly viewsService: IViewsService, - @IEditorService private readonly editorService: IEditorService, - ) { - super(id, label); +export async function focusPreviousSearchResult(accessor: ServicesAccessor): Promise { + const editorService = accessor.get(IEditorService); + const input = editorService.activeEditor; + if (input instanceof SearchEditorInput) { + // cast as we cannot import SearchEditor as a value b/c cyclic dependency. + return (editorService.activeEditorPane as SearchEditor).focusPreviousResult(); } - override async run(): Promise { - const input = this.editorService.activeEditor; - if (input instanceof SearchEditorInput) { - // cast as we cannot import SearchEditor as a value b/c cyclic dependency. - return (this.editorService.activeEditorPane as SearchEditor).focusPreviousResult(); - } - - return openSearchView(this.viewsService).then(searchView => { - searchView?.selectPreviousMatch(); - }); - } + return openSearchView(accessor.get(IViewsService)).then(searchView => { + searchView?.selectPreviousMatch(); + }); } -export abstract class AbstractSearchAndReplaceAction extends Action { +export async function findOrReplaceInFiles(accessor: ServicesAccessor, expandSearchReplaceWidget: boolean): Promise { + return openSearchView(accessor.get(IViewsService), false).then(openedView => { + if (openedView) { + const searchAndReplaceWidget = openedView.searchAndReplaceWidget; + searchAndReplaceWidget.toggleReplace(expandSearchReplaceWidget); - /** - * Returns element to focus after removing the given element - */ - getElementToFocusAfterRemoved(viewer: WorkbenchObjectTree, elementToRemove: RenderableMatch, isTreeViewVisible: boolean): RenderableMatch { - const elementToFocus = this.getNextElementAfterRemoved(viewer, elementToRemove); - return elementToFocus || this.getPreviousElementAfterRemoved(viewer, elementToRemove, isTreeViewVisible); - } - - getNextElementAfterRemoved(viewer: WorkbenchObjectTree, element: RenderableMatch): RenderableMatch { - const navigator: ITreeNavigator = viewer.navigate(element); - if (element instanceof FolderMatch) { - while (!!navigator.next() && !(navigator.current() instanceof FolderMatch)) { } - } else if (element instanceof FileMatch) { - while (!!navigator.next() && !(navigator.current() instanceof FileMatch)) { } - } else { - while (navigator.next() && !(navigator.current() instanceof Match)) { - viewer.expand(navigator.current()); - } + const updatedText = openedView.updateTextFromFindWidgetOrSelection({ allowUnselectedWord: !expandSearchReplaceWidget }); + openedView.searchAndReplaceWidget.focus(undefined, updatedText, updatedText); } - return navigator.current(); - } - - getPreviousElementAfterRemoved(viewer: WorkbenchObjectTree, element: RenderableMatch, isTreeViewVisible: boolean): RenderableMatch { - const navigator: ITreeNavigator = viewer.navigate(element); - let previousElement = navigator.previous(); - // Hence take the previous element. - const parent = getVisibleParent(element, isTreeViewVisible); - if (parent === previousElement) { - previousElement = navigator.previous(); - } - - if (parent instanceof FileMatch && getVisibleParent(parent, isTreeViewVisible) === previousElement) { - previousElement = navigator.previous(); - } - - // If the previous element is a File or Folder, expand it and go to its last child. - // Spell out the two cases, would be too easy to create an infinite loop, like by adding another level... - if (element instanceof Match && previousElement && previousElement instanceof FolderMatch) { - navigator.next(); - viewer.expand(previousElement); - previousElement = navigator.previous(); - } - - if (element instanceof Match && previousElement && previousElement instanceof FileMatch) { - navigator.next(); - viewer.expand(previousElement); - previousElement = navigator.previous(); - } - - return previousElement; - } + }); } + class ReplaceActionRunner { constructor( - private viewer: WorkbenchObjectTree, + private viewer: WorkbenchCompressibleObjectTree, private viewlet: SearchView | undefined, - private getElementToFocusAfterRemoved: (viewer: WorkbenchObjectTree, lastElementToBeRemoved: RenderableMatch, isTreeViewVisible: boolean) => RenderableMatch, - private getPreviousElementAfterRemoved: (viewer: WorkbenchObjectTree, element: RenderableMatch, isTreeViewVisible: boolean) => RenderableMatch, // Services @IReplaceService private readonly replaceService: IReplaceService, @IEditorService private readonly editorService: IEditorService, @@ -460,99 +356,55 @@ class ReplaceActionRunner { @IViewsService private readonly viewsService: IViewsService ) { } - async performReplace(element: FolderMatch | FileMatch | Match): Promise { + async performReplace(element: RenderableMatch): Promise { // since multiple elements can be selected, we need to check the type of the FolderMatch/FileMatch/Match before we perform the replace. const opInfo = getElementsToOperateOnInfo(this.viewer, element, this.configurationService.getValue('search')); const elementsToReplace = opInfo.elements; + let focusElement = this.viewer.getFocus()[0]; - const searchView = getSearchView(this.viewsService); - const searchResult = searchView?.searchResult; + if (!focusElement || (focusElement && !arrayContainsElementOrParent(focusElement, elementsToReplace)) || (focusElement instanceof SearchResult)) { + focusElement = element; + } + + if (elementsToReplace.length === 0) { + return; + } + let nextFocusElement; + if (focusElement) { + nextFocusElement = getElementToFocusAfterRemoved(this.viewer, focusElement, elementsToReplace); + } + + const searchResult = getSearchView(this.viewsService)?.searchResult; if (searchResult) { searchResult.batchReplace(elementsToReplace); } - const currentBottomFocusElement = elementsToReplace[elementsToReplace.length - 1]; - - if (currentBottomFocusElement instanceof Match) { - const elementToFocus = this.getElementToFocusAfterReplace(currentBottomFocusElement); - - if (elementToFocus) { - this.viewer.setFocus([elementToFocus], getSelectionKeyboardEvent()); - this.viewer.setSelection([elementToFocus], getSelectionKeyboardEvent()); + if (focusElement) { + if (!nextFocusElement) { + nextFocusElement = getLastNodeFromSameType(this.viewer, focusElement); } - const elementToShowReplacePreview = this.getElementToShowReplacePreview(elementToFocus, currentBottomFocusElement, searchView?.isTreeLayoutViewVisible ?? false); - - this.viewer.domFocus(); - - const useReplacePreview = this.configurationService.getValue().search.useReplacePreview; - if (!useReplacePreview || !elementToShowReplacePreview || this.hasToOpenFile(currentBottomFocusElement)) { - this.viewlet?.open(currentBottomFocusElement, true); - } else { - this.replaceService.openReplacePreview(elementToShowReplacePreview, true); - } - return; - } else { - const nextFocusElement = this.getElementToFocusAfterRemoved(this.viewer, currentBottomFocusElement, searchView?.isTreeLayoutViewVisible ?? false); if (nextFocusElement) { + this.viewer.reveal(nextFocusElement); this.viewer.setFocus([nextFocusElement], getSelectionKeyboardEvent()); this.viewer.setSelection([nextFocusElement], getSelectionKeyboardEvent()); - } - this.viewer.domFocus(); - - if (element instanceof FileMatch) { - this.viewlet?.open(element, true); - } - - return; - } - } - - private getElementToFocusAfterReplace(currElement: Match): RenderableMatch { - const navigator: ITreeNavigator = this.viewer.navigate(); - let fileMatched = false; - let elementToFocus: RenderableMatch | null = null; - do { - elementToFocus = navigator.current(); - if (elementToFocus instanceof Match) { - if (elementToFocus.parent().id() === currElement.parent().id()) { - fileMatched = true; - if (currElement.range().getStartPosition().isBeforeOrEqual(elementToFocus.range().getStartPosition())) { - // Closest next match in the same file - break; + if (nextFocusElement instanceof Match) { + const useReplacePreview = this.configurationService.getValue().search.useReplacePreview; + if (!useReplacePreview || this.hasToOpenFile(nextFocusElement)) { + this.viewlet?.open(nextFocusElement, true); + } else { + this.replaceService.openReplacePreview(nextFocusElement, true); } - } else if (fileMatched) { - // First match in the next file (if expanded) - break; - } - } else if (fileMatched) { - if (this.viewer.isCollapsed(elementToFocus)) { - // Next file match (if collapsed) - break; + } else if (nextFocusElement instanceof FileMatch) { + this.viewlet?.open(nextFocusElement, true); } } - } while (!!navigator.next()); - return elementToFocus!; - } - private getElementToShowReplacePreview(elementToFocus: RenderableMatch, currBottomElem: RenderableMatch, isTreeViewVisible: boolean): Match | null { - if (this.hasSameParent(elementToFocus, currBottomElem)) { - return elementToFocus; } - const previousElement = this.getPreviousElementAfterRemoved(this.viewer, elementToFocus, isTreeViewVisible); - if (this.hasSameParent(previousElement, currBottomElem)) { - return previousElement; - } - return null; - } - private hasSameParent(element: RenderableMatch, currBottomElem: RenderableMatch): boolean { - if (!(currBottomElem instanceof Match)) { - return false; - } - return element && element instanceof Match && this.uriIdentityService.extUri.isEqual(element.parent().resource, currBottomElem.parent().resource); + this.viewer.domFocus(); } private hasToOpenFile(currBottomElem: RenderableMatch): boolean { @@ -568,12 +420,12 @@ class ReplaceActionRunner { } } -export class RemoveAction extends AbstractSearchAndReplaceAction { +export class RemoveAction extends Action { static readonly LABEL = nls.localize('RemoveAction.label', "Dismiss"); constructor( - private viewer: WorkbenchObjectTree, + private viewer: WorkbenchCompressibleObjectTree, private element: RenderableMatch, @IKeybindingService keyBindingService: IKeybindingService, @IConfigurationService private readonly configurationService: IConfigurationService, @@ -585,38 +437,65 @@ export class RemoveAction extends AbstractSearchAndReplaceAction { override async run(): Promise { const opInfo = getElementsToOperateOnInfo(this.viewer, this.element, this.configurationService.getValue('search')); const elementsToRemove = opInfo.elements; - const searchView = getSearchView(this.viewsService); + let focusElement = this.viewer.getFocus()[0]; + if (elementsToRemove.length === 0) { return; } - if (opInfo.mustReselect) { - for (const currentElement of elementsToRemove) { - const nextFocusElement = !currentElement || currentElement instanceof SearchResult || arrayContainsElementOrParent(currentElement, elementsToRemove) ? - this.getElementToFocusAfterRemoved(this.viewer, currentElement, searchView?.isTreeLayoutViewVisible ?? false) : - null; - if (nextFocusElement && !arrayContainsElementOrParent(nextFocusElement, elementsToRemove)) { - this.viewer.reveal(nextFocusElement); - this.viewer.setFocus([nextFocusElement], getSelectionKeyboardEvent()); - this.viewer.setSelection([nextFocusElement], getSelectionKeyboardEvent()); - break; - } - } + if (!focusElement || (focusElement instanceof SearchResult)) { + focusElement = this.element; } - const searchResult = searchView?.searchResult; + let nextFocusElement; + if (opInfo.mustReselect && focusElement) { + nextFocusElement = getElementToFocusAfterRemoved(this.viewer, focusElement, elementsToRemove); + } + + const searchResult = getSearchView(this.viewsService)?.searchResult; if (searchResult) { searchResult.batchRemove(elementsToRemove); } + if (opInfo.mustReselect && focusElement) { + if (!nextFocusElement) { + nextFocusElement = getLastNodeFromSameType(this.viewer, focusElement); + } + + if (nextFocusElement && !arrayContainsElementOrParent(nextFocusElement, elementsToRemove)) { + this.viewer.reveal(nextFocusElement); + this.viewer.setFocus([nextFocusElement], getSelectionKeyboardEvent()); + this.viewer.setSelection([nextFocusElement], getSelectionKeyboardEvent()); + } + } + this.viewer.domFocus(); return; } } +export class ReplaceAction extends Action { -export class ReplaceAllAction extends AbstractSearchAndReplaceAction { + static readonly LABEL = nls.localize('match.replace.label', "Replace"); + + static runQ = Promise.resolve(); + private replaceRunner: ReplaceActionRunner; + + constructor(viewer: WorkbenchCompressibleObjectTree, private element: Match, viewlet: SearchView, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IKeybindingService keyBindingService: IKeybindingService, + ) { + super(Constants.ReplaceActionId, appendKeyBindingLabel(ReplaceAction.LABEL, keyBindingService.lookupKeybinding(Constants.ReplaceActionId), keyBindingService), ThemeIcon.asClassName(searchReplaceIcon)); + this.replaceRunner = this.instantiationService.createInstance(ReplaceActionRunner, viewer, viewlet); + } + + override async run(): Promise { + return this.replaceRunner.performReplace(this.element); + } +} + +export class ReplaceAllAction extends Action { static readonly LABEL = nls.localize('file.replaceAll.label', "Replace All"); private replaceRunner: ReplaceActionRunner; @@ -627,7 +506,7 @@ export class ReplaceAllAction extends AbstractSearchAndReplaceAction { @IKeybindingService keyBindingService: IKeybindingService, ) { super(Constants.ReplaceAllInFileActionId, appendKeyBindingLabel(ReplaceAllAction.LABEL, keyBindingService.lookupKeybinding(Constants.ReplaceAllInFileActionId), keyBindingService), ThemeIcon.asClassName(searchReplaceAllIcon)); - this.replaceRunner = this.instantiationService.createInstance(ReplaceActionRunner, viewlet.getControl(), viewlet, this.getElementToFocusAfterRemoved, this.getPreviousElementAfterRemoved); + this.replaceRunner = this.instantiationService.createInstance(ReplaceActionRunner, viewlet.getControl(), viewlet); } override async run(): Promise { @@ -635,17 +514,17 @@ export class ReplaceAllAction extends AbstractSearchAndReplaceAction { } } -export class ReplaceAllInFolderAction extends AbstractSearchAndReplaceAction { +export class ReplaceAllInFolderAction extends Action { static readonly LABEL = nls.localize('file.replaceAll.label', "Replace All"); private replaceRunner: ReplaceActionRunner; - constructor(viewer: WorkbenchObjectTree, private folderMatch: FolderMatch, + constructor(viewer: WorkbenchCompressibleObjectTree, private folderMatch: FolderMatch, @IInstantiationService private readonly instantiationService: IInstantiationService, @IKeybindingService keyBindingService: IKeybindingService ) { super(Constants.ReplaceAllInFolderActionId, appendKeyBindingLabel(ReplaceAllInFolderAction.LABEL, keyBindingService.lookupKeybinding(Constants.ReplaceAllInFolderActionId), keyBindingService), ThemeIcon.asClassName(searchReplaceAllIcon)); - this.replaceRunner = this.instantiationService.createInstance(ReplaceActionRunner, viewer, undefined, this.getElementToFocusAfterRemoved, this.getPreviousElementAfterRemoved); + this.replaceRunner = this.instantiationService.createInstance(ReplaceActionRunner, viewer, undefined); } override run(): Promise { @@ -653,26 +532,6 @@ export class ReplaceAllInFolderAction extends AbstractSearchAndReplaceAction { } } -export class ReplaceAction extends AbstractSearchAndReplaceAction { - - static readonly LABEL = nls.localize('match.replace.label', "Replace"); - - static runQ = Promise.resolve(); - private replaceRunner: ReplaceActionRunner; - - constructor(viewer: WorkbenchObjectTree, private element: Match, viewlet: SearchView, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IKeybindingService keyBindingService: IKeybindingService, - ) { - super(Constants.ReplaceActionId, appendKeyBindingLabel(ReplaceAction.LABEL, keyBindingService.lookupKeybinding(Constants.ReplaceActionId), keyBindingService), ThemeIcon.asClassName(searchReplaceIcon)); - this.replaceRunner = this.instantiationService.createInstance(ReplaceActionRunner, viewer, viewlet, this.getElementToFocusAfterRemoved, this.getPreviousElementAfterRemoved); - } - - override async run(): Promise { - return this.replaceRunner.performReplace(this.element); - } -} - export const copyPathCommand: ICommandHandler = async (accessor, fileMatch: FileMatch | FolderMatchWithResource | undefined) => { if (!fileMatch) { const selection = getSelectedRow(accessor); @@ -825,19 +684,89 @@ export const focusSearchListCommand: ICommandHandler = accessor => { }); }; -function getElementsToOperateOnInfo(viewer: WorkbenchObjectTree, currElement: RenderableMatch, sortConfig: ISearchConfigurationProperties): { elements: RenderableMatch[]; mustReselect: boolean } { +export function getMultiSelectedSearchResources(viewer: WorkbenchCompressibleObjectTree, currElement: RenderableMatch | undefined, sortConfig: ISearchConfigurationProperties): URI[] { + return getElementsToOperateOnInfo(viewer, currElement, sortConfig).elements + .map((renderableMatch) => ((renderableMatch instanceof Match) ? null : renderableMatch.resource)) + .filter((renderableMatch): renderableMatch is URI => (renderableMatch !== null)); +} + +function getElementsToOperateOnInfo(viewer: WorkbenchCompressibleObjectTree, currElement: RenderableMatch | undefined, sortConfig: ISearchConfigurationProperties): { elements: RenderableMatch[]; mustReselect: boolean } { let elements: RenderableMatch[] = viewer.getSelection().filter((x): x is RenderableMatch => x !== null).sort((a, b) => searchComparer(a, b, sortConfig.sortOrder)); - const mustReselect = elements.includes(currElement); // this indicates whether we need to re-focus/re-select on a remove. + const mustReselect = !currElement || elements.includes(currElement); // this indicates whether we need to re-focus/re-select on a remove. // if selection doesn't include multiple elements, just return current focus element. - if (!(elements.length > 1 && elements.includes(currElement))) { + if (currElement && !(elements.length > 1 && elements.includes(currElement))) { elements = [currElement]; } return { elements, mustReselect }; } -function getVisibleParent(element: RenderableMatch, isTreeViewVisible: boolean): FileMatch | FolderMatch | SearchResult | null { - return (isTreeViewVisible || element instanceof Match) ? element.parent() : element.closestRoot; + +function compareLevels(elem1: RenderableMatch, elem2: RenderableMatch) { + if (elem1 instanceof Match) { + if (elem2 instanceof Match) { + return 0; + } else { + return -1; + } + + } else if (elem1 instanceof FileMatch) { + if (elem2 instanceof Match) { + return 1; + } else if (elem2 instanceof FileMatch) { + return 0; + } else { + return -1; + } + + } else { + // FolderMatch + if (elem2 instanceof FolderMatch) { + return 0; + } else { + return 1; + } + } +} + +/** + * Returns element to focus after removing the given element + */ +export function getElementToFocusAfterRemoved(viewer: WorkbenchCompressibleObjectTree, element: RenderableMatch, elementsToRemove: RenderableMatch[]): RenderableMatch | undefined { + const navigator: ITreeNavigator = viewer.navigate(element); + if (element instanceof FolderMatch) { + while (!!navigator.next() && (!(navigator.current() instanceof FolderMatch) || arrayContainsElementOrParent(navigator.current(), elementsToRemove))) { } + } else if (element instanceof FileMatch) { + while (!!navigator.next() && (!(navigator.current() instanceof FileMatch) || arrayContainsElementOrParent(navigator.current(), elementsToRemove))) { + viewer.expand(navigator.current()); + } + } else { + while (navigator.next() && (!(navigator.current() instanceof Match) || arrayContainsElementOrParent(navigator.current(), elementsToRemove))) { + viewer.expand(navigator.current()); + } + } + return navigator.current(); +} + +/*** + * Finds the last element in the tree with the same type as `element` + */ +export function getLastNodeFromSameType(viewer: WorkbenchCompressibleObjectTree, element: RenderableMatch): RenderableMatch | undefined { + let lastElem: RenderableMatch | null = viewer.lastVisibleElement ?? null; + + while (lastElem) { + const compareVal = compareLevels(element, lastElem); + if (compareVal === -1) { + viewer.expand(lastElem); + lastElem = viewer.lastVisibleElement; + } else if (compareVal === 1) { + lastElem = viewer.getParentElement(lastElem); + } else { + return lastElem; + } + } + + return undefined; } diff --git a/src/vs/workbench/contrib/search/browser/searchResultsView.ts b/src/vs/workbench/contrib/search/browser/searchResultsView.ts index d2bbfc89c65..54584f9a82c 100644 --- a/src/vs/workbench/contrib/search/browser/searchResultsView.ts +++ b/src/vs/workbench/contrib/search/browser/searchResultsView.ts @@ -8,9 +8,9 @@ import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; -import { ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; +import { ITreeNode } from 'vs/base/browser/ui/tree/tree'; import { IAction } from 'vs/base/common/actions'; -import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import * as paths from 'vs/base/common/path'; import * as nls from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -24,14 +24,17 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels'; import { RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction } from 'vs/workbench/contrib/search/browser/searchActions'; import { SearchView } from 'vs/workbench/contrib/search/browser/searchView'; -import { FileMatch, Match, RenderableMatch, SearchModel, FolderMatch, FolderMatchNoRoot } from 'vs/workbench/contrib/search/common/searchModel'; +import { FileMatch, Match, RenderableMatch, SearchModel, FolderMatch, FolderMatchNoRoot, FolderMatchWorkspaceRoot } from 'vs/workbench/contrib/search/common/searchModel'; import { isEqual } from 'vs/base/common/resources'; +import { ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree'; +import { ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; interface IFolderMatchTemplate { label: IResourceLabel; badge: CountBadge; actions: ActionBar; - disposables: IDisposable[]; + disposables: DisposableStore; + disposableActions: DisposableStore; } interface IFileMatchTemplate { @@ -39,7 +42,7 @@ interface IFileMatchTemplate { label: IResourceLabel; badge: CountBadge; actions: ActionBar; - disposables: IDisposable[]; + disposables: DisposableStore; } interface IMatchTemplate { @@ -73,8 +76,7 @@ export class SearchDelegate implements IListVirtualDelegate { throw new Error('Invalid search tree element'); } } - -export class FolderMatchRenderer extends Disposable implements ITreeRenderer { +export class FolderMatchRenderer extends Disposable implements ICompressibleTreeRenderer { static readonly TEMPLATE_ID = 'folderMatch'; readonly templateId = FolderMatchRenderer.TEMPLATE_ID; @@ -85,33 +87,60 @@ export class FolderMatchRenderer extends Disposable implements ITreeRenderer, any>, index: number, templateData: IFolderMatchTemplate, height: number | undefined): void { + const compressed = node.element; + const folder = compressed.elements[compressed.elements.length - 1]; + folder.compressionStartParent = compressed.elements[0]; + const label = compressed.elements.map(e => e.name()); + + if (folder.resource) { + const fileKind = (folder instanceof FolderMatchWorkspaceRoot) ? FileKind.ROOT_FOLDER : FileKind.FOLDER; + templateData.label.setResource({ resource: folder.resource, name: label }, { + fileKind, + separator: this.labelService.getSeparator(folder.resource.scheme), + }); + } else { + templateData.label.setLabel(nls.localize('searchFolderMatch.other.label', "Other files")); + } + + templateData.actions.clear(); + templateData.actions.context = folder; + this.renderFolderDetails(folder, templateData); + } + renderTemplate(container: HTMLElement): IFolderMatchTemplate { - const disposables: IDisposable[] = []; + const disposables = new DisposableStore(); const folderMatchElement = DOM.append(container, DOM.$('.foldermatch')); - const label = this.labels.create(folderMatchElement); - disposables.push(label); + const label = this.labels.create(folderMatchElement, { supportDescriptionHighlights: true, supportHighlights: true }); + disposables.add(label); const badge = new CountBadge(DOM.append(folderMatchElement, DOM.$('.badge'))); - disposables.push(attachBadgeStyler(badge, this.themeService)); + disposables.add(attachBadgeStyler(badge, this.themeService)); const actionBarContainer = DOM.append(folderMatchElement, DOM.$('.actionBarContainer')); const actions = new ActionBar(actionBarContainer, { animated: false }); - disposables.push(actions); + disposables.add(actions); + + const disposableElements = new DisposableStore(); + disposables.add(disposableElements); return { label, badge, actions, - disposables + disposables, + disposableActions: disposableElements }; } renderElement(node: ITreeNode, index: number, templateData: IFolderMatchTemplate): void { const folderMatch = node.element; + folderMatch.compressionStartParent = undefined; if (folderMatch.resource) { const workspaceFolder = this.contextService.getWorkspaceFolder(folderMatch.resource); if (workspaceFolder && isEqual(workspaceFolder.uri, folderMatch.resource)) { @@ -122,30 +151,42 @@ export class FolderMatchRenderer extends Disposable implements ITreeRenderer 1 ? nls.localize('searchFileMatches', "{0} files found", count) : nls.localize('searchFileMatch', "{0} file found", count)); - templateData.actions.clear(); - - const actions: IAction[] = []; - if (this.searchModel.isReplaceActive() && count > 0) { - actions.push(this.instantiationService.createInstance(ReplaceAllInFolderAction, this.searchView.getControl(), folderMatch)); - } - - actions.push(this.instantiationService.createInstance(RemoveAction, this.searchView.getControl(), folderMatch)); - templateData.actions.push(actions, { icon: true, label: false }); + this.renderFolderDetails(folderMatch, templateData); } disposeElement(element: ITreeNode, index: number, templateData: IFolderMatchTemplate): void { + templateData.disposableActions.clear(); + } + + disposeCompressedElements(node: ITreeNode, any>, index: number, templateData: IFolderMatchTemplate, height: number | undefined): void { + templateData.disposableActions.clear(); } disposeTemplate(templateData: IFolderMatchTemplate): void { - dispose(templateData.disposables); + templateData.disposables.dispose(); + } + + private renderFolderDetails(folder: FolderMatch, templateData: IFolderMatchTemplate) { + const count = folder.recursiveMatchCount(); + templateData.badge.setCount(count); + templateData.badge.setTitleFormat(count > 1 ? nls.localize('searchFileMatches', "{0} files found", count) : nls.localize('searchFileMatch', "{0} file found", count)); + + const actions: IAction[] = []; + if (this.searchModel.isReplaceActive() && count > 0) { + const replaceAction = this.instantiationService.createInstance(ReplaceAllInFolderAction, this.searchView.getControl(), folder); + actions.push(replaceAction); + templateData.disposableActions.add(replaceAction); + } + const removeAction = this.instantiationService.createInstance(RemoveAction, this.searchView.getControl(), folder); + actions.push(removeAction); + templateData.disposableActions.add(removeAction); + + templateData.actions.push(actions, { icon: true, label: false }); } } -export class FileMatchRenderer extends Disposable implements ITreeRenderer { +export class FileMatchRenderer extends Disposable implements ICompressibleTreeRenderer { static readonly TEMPLATE_ID = 'fileMatch'; readonly templateId = FileMatchRenderer.TEMPLATE_ID; @@ -162,16 +203,20 @@ export class FileMatchRenderer extends Disposable implements ITreeRenderer, any>, index: number, templateData: IFileMatchTemplate, height: number | undefined): void { + throw new Error('Should never happen since node is incompressible.'); + } + renderTemplate(container: HTMLElement): IFileMatchTemplate { - const disposables: IDisposable[] = []; + const disposables = new DisposableStore(); const fileMatchElement = DOM.append(container, DOM.$('.filematch')); const label = this.labels.create(fileMatchElement); - disposables.push(label); + disposables.add(label); const badge = new CountBadge(DOM.append(fileMatchElement, DOM.$('.badge'))); - disposables.push(attachBadgeStyler(badge, this.themeService)); + disposables.add(attachBadgeStyler(badge, this.themeService)); const actionBarContainer = DOM.append(fileMatchElement, DOM.$('.actionBarContainer')); const actions = new ActionBar(actionBarContainer, { animated: false }); - disposables.push(actions); + disposables.add(actions); return { el: fileMatchElement, @@ -206,11 +251,11 @@ export class FileMatchRenderer extends Disposable implements ITreeRenderer { +export class MatchRenderer extends Disposable implements ICompressibleTreeRenderer { static readonly TEMPLATE_ID = 'match'; readonly templateId = MatchRenderer.TEMPLATE_ID; @@ -224,6 +269,9 @@ export class MatchRenderer extends Disposable implements ITreeRenderer, void>, index: number, templateData: IMatchTemplate, height: number | undefined): void { + throw new Error('Should never happen since node is incompressible.'); + } renderTemplate(container: HTMLElement): IMatchTemplate { container.classList.add('linematch'); diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index e209f70f451..a74cfaff012 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -8,8 +8,8 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; import { IIdentityProvider } from 'vs/base/browser/ui/list/list'; -import { ITreeContextMenuEvent, ITreeElement } from 'vs/base/browser/ui/tree/tree'; -import { IAction } from 'vs/base/common/actions'; +import { ICompressedTreeElement } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; +import { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; import { Delayer } from 'vs/base/common/async'; import { Color, RGBA } from 'vs/base/common/color'; import * as errors from 'vs/base/common/errors'; @@ -32,8 +32,7 @@ import { CommonFindController } from 'vs/editor/contrib/find/browser/findControl import { MultiCursorSelectionController } from 'vs/editor/contrib/multicursor/browser/multicursor'; import * as nls from 'vs/nls'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; -import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { MenuId } from 'vs/platform/actions/common/actions'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -43,7 +42,7 @@ import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/file import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { getSelectionKeyboardEvent, WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; +import { getSelectionKeyboardEvent, WorkbenchCompressibleObjectTree } from 'vs/platform/list/browser/listService'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IOpenerService, withSelection } from 'vs/platform/opener/common/opener'; import { IProgress, IProgressService, IProgressStep } from 'vs/platform/progress/common/progress'; @@ -111,6 +110,7 @@ export class SearchView extends ViewPane { private fileMatchOrFolderMatchWithResourceFocus: IContextKey; private fileMatchFocused: IContextKey; private folderMatchFocused: IContextKey; + private folderMatchWithResourceFocused: IContextKey; private matchFocused: IContextKey; private hasSearchResultsKey: IContextKey; private lastFocusState: 'input' | 'tree' = 'input'; @@ -121,9 +121,7 @@ export class SearchView extends ViewPane { private hasFilePatternKey: IContextKey; private hasSomeCollapsibleResultKey: IContextKey; - private contextMenu: IMenu | null = null; - - private tree!: WorkbenchObjectTree; + private tree!: WorkbenchCompressibleObjectTree; private treeLabels!: ResourceLabels; private viewletState: MementoObject; private messagesElement!: HTMLElement; @@ -156,6 +154,9 @@ export class SearchView extends ViewPane { private treeViewKey: IContextKey; + private _uiRefreshHandle: any; + private _visibleMatches: number = 0; + constructor( options: IViewPaneOptions, @IFileService private readonly fileService: IFileService, @@ -178,7 +179,6 @@ export class SearchView extends ViewPane { @IThemeService themeService: IThemeService, @ISearchHistoryService private readonly searchHistoryService: ISearchHistoryService, @IContextMenuService contextMenuService: IContextMenuService, - @IMenuService private readonly menuService: IMenuService, @IAccessibilityService private readonly accessibilityService: IAccessibilityService, @IKeybindingService keybindingService: IKeybindingService, @IStorageService storageService: IStorageService, @@ -198,6 +198,7 @@ export class SearchView extends ViewPane { this.fileMatchOrFolderMatchWithResourceFocus = Constants.FileMatchOrFolderMatchWithResourceFocusKey.bindTo(this.contextKeyService); this.fileMatchFocused = Constants.FileFocusKey.bindTo(this.contextKeyService); this.folderMatchFocused = Constants.FolderFocusKey.bindTo(this.contextKeyService); + this.folderMatchWithResourceFocused = Constants.ResourceFolderFocusKey.bindTo(this.contextKeyService); this.hasSearchResultsKey = Constants.HasSearchResults.bindTo(this.contextKeyService); this.matchFocused = Constants.MatchFocusKey.bindTo(this.contextKeyService); this.searchStateKey = SearchStateKey.bindTo(this.contextKeyService); @@ -531,7 +532,7 @@ export class SearchView extends ViewPane { private refreshAndUpdateCount(event?: IChangeEvent): void { this.searchWidget.setReplaceAllActionState(!this.viewModel.searchResult.isEmpty()); - this.updateSearchResultCount(this.viewModel.searchResult.query!.userDisabledExcludesAndIgnoreFiles, this.viewModel.searchResult.query?.onlyOpenEditors); + this.updateSearchResultCount(this.viewModel.searchResult.query!.userDisabledExcludesAndIgnoreFiles, this.viewModel.searchResult.query?.onlyOpenEditors, event?.clearingAll); return this.refreshTree(event); } @@ -561,22 +562,22 @@ export class SearchView extends ViewPane { } } - private createResultIterator(collapseResults: ISearchConfigurationProperties['collapseResults']): Iterable> { + private createResultIterator(collapseResults: ISearchConfigurationProperties['collapseResults']): Iterable> { const folderMatches = this.searchResult.folderMatches() .filter(fm => !fm.isEmpty()) .sort(searchMatchComparer); if (folderMatches.length === 1) { - return this.createFolderIterator(folderMatches[0], collapseResults); + return this.createFolderIterator(folderMatches[0], collapseResults, true); } return Iterable.map(folderMatches, folderMatch => { - const children = this.createFolderIterator(folderMatch, collapseResults); - return >{ element: folderMatch, children }; + const children = this.createFolderIterator(folderMatch, collapseResults, true); + return >{ element: folderMatch, children, incompressible: true }; // roots should always be incompressible }); } - private createFolderIterator(folderMatch: FolderMatch, collapseResults: ISearchConfigurationProperties['collapseResults']): Iterable> { + private createFolderIterator(folderMatch: FolderMatch, collapseResults: ISearchConfigurationProperties['collapseResults'], childFolderIncompressible: boolean): Iterable> { const sortOrder = this.searchConfig.sortOrder; const matchArray = this.isTreeLayoutViewVisible ? folderMatch.matches() : folderMatch.downstreamFileMatches(); @@ -587,7 +588,7 @@ export class SearchView extends ViewPane { if (match instanceof FileMatch) { children = this.createFileIterator(match); } else { - children = this.createFolderIterator(match, collapseResults); + children = this.createFolderIterator(match, collapseResults, false); } let nodeExists = true; try { this.tree.getNode(match); } catch (e) { nodeExists = false; } @@ -595,18 +596,18 @@ export class SearchView extends ViewPane { const collapsed = nodeExists ? undefined : (collapseResults === 'alwaysCollapse' || (match.count() > 10 && collapseResults !== 'alwaysExpand')); - return >{ element: match, children, collapsed }; + return >{ element: match, children, collapsed, incompressible: (match instanceof FileMatch) ? true : childFolderIncompressible }; }); } - private createFileIterator(fileMatch: FileMatch): Iterable> { + private createFileIterator(fileMatch: FileMatch): Iterable> { const matches = fileMatch.matches().sort(searchMatchComparer); - return Iterable.map(matches, r => (>{ element: r })); + return Iterable.map(matches, r => (>{ element: r, incompressible: true })); } - private createIterator(match: FolderMatch | FileMatch | SearchResult, collapseResults: ISearchConfigurationProperties['collapseResults']): Iterable> { + private createIterator(match: FolderMatch | FileMatch | SearchResult, collapseResults: ISearchConfigurationProperties['collapseResults']): Iterable> { return match instanceof SearchResult ? this.createResultIterator(collapseResults) : - match instanceof FolderMatch ? this.createFolderIterator(match, collapseResults) : + match instanceof FolderMatch ? this.createFolderIterator(match, collapseResults, false) : this.createFileIterator(match); } @@ -744,7 +745,7 @@ export class SearchView extends ViewPane { }; this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility })); - this.tree = this._register(>this.instantiationService.createInstance(WorkbenchObjectTree, + this.tree = this._register(>this.instantiationService.createInstance(WorkbenchCompressibleObjectTree, 'SearchView', this.resultsElement, delegate, @@ -807,6 +808,7 @@ export class SearchView extends ViewPane { this.matchFocused.set(focus instanceof Match); this.fileMatchOrFolderMatchFocus.set(focus instanceof FileMatch || focus instanceof FolderMatch); this.fileMatchOrFolderMatchWithResourceFocus.set(focus instanceof FileMatch || focus instanceof FolderMatchWithResource); + this.folderMatchWithResourceFocused.set(focus instanceof FolderMatchWithResource); this.lastFocusState = 'tree'; } })); @@ -819,23 +821,20 @@ export class SearchView extends ViewPane { this.matchFocused.reset(); this.fileMatchOrFolderMatchFocus.reset(); this.fileMatchOrFolderMatchWithResourceFocus.reset(); + this.folderMatchWithResourceFocused.reset(); })); } private onContextMenu(e: ITreeContextMenuEvent): void { - if (!this.contextMenu) { - this.contextMenu = this._register(this.menuService.createMenu(MenuId.SearchContext, this.contextKeyService)); - } e.browserEvent.preventDefault(); e.browserEvent.stopPropagation(); - const actions: IAction[] = []; - createAndFillInContextMenuActions(this.contextMenu, { shouldForwardArgs: true }, actions); - this.contextMenuService.showContextMenu({ + menuId: MenuId.SearchContext, + menuActionOptions: { shouldForwardArgs: true }, + contextKeyService: this.contextKeyService, getAnchor: () => e.anchor, - getActions: () => actions, getActionsContext: () => e.element, }); } @@ -1596,22 +1595,25 @@ export class SearchView extends ViewPane { } }; - let visibleMatches = 0; + this._visibleMatches = 0; // Handle UI updates in an interval to show frequent progress and results - const uiRefreshHandle: any = setInterval(() => { - if (this.state === SearchUIState.Idle) { - window.clearInterval(uiRefreshHandle); - return; - } + if (!this._uiRefreshHandle) { + this._uiRefreshHandle = setInterval(() => { + if (this.state === SearchUIState.Idle) { + window.clearInterval(this._uiRefreshHandle); + this._uiRefreshHandle = undefined; + return; + } - // Search result tree update - const fileCount = this.viewModel.searchResult.fileCount(); - if (visibleMatches !== fileCount) { - visibleMatches = fileCount; - this.refreshAndUpdateCount(); - } - }, 100); + // Search result tree update + const fileCount = this.viewModel.searchResult.fileCount(); + if (this._visibleMatches !== fileCount) { + this._visibleMatches = fileCount; + this.refreshAndUpdateCount(); + } + }, 100); + } this.searchWidget.setReplaceAllActionState(false); @@ -1654,14 +1656,14 @@ export class SearchView extends ViewPane { this.inputPatternIncludes.setOnlySearchInOpenEditors(false); } - private updateSearchResultCount(disregardExcludesAndIgnores?: boolean, onlyOpenEditors?: boolean): void { + private updateSearchResultCount(disregardExcludesAndIgnores?: boolean, onlyOpenEditors?: boolean, clear: boolean = false): void { const fileCount = this.viewModel.searchResult.fileCount(); this.hasSearchResultsKey.set(fileCount > 0); const msgWasHidden = this.messagesElement.style.display === 'none'; const messageEl = this.clearMessage(); - const resultMsg = this.buildResultCountMessage(this.viewModel.searchResult.count(), fileCount); + const resultMsg = clear ? '' : this.buildResultCountMessage(this.viewModel.searchResult.count(), fileCount); this.tree.ariaLabel = resultMsg + nls.localize('forTerm', " - Search: {0}", this.searchResult.query?.contentPattern.pattern ?? ''); dom.append(messageEl, resultMsg); diff --git a/src/vs/workbench/contrib/search/common/constants.ts b/src/vs/workbench/contrib/search/common/constants.ts index 5a4dfd6ed45..bd145765be8 100644 --- a/src/vs/workbench/contrib/search/common/constants.ts +++ b/src/vs/workbench/contrib/search/common/constants.ts @@ -28,6 +28,19 @@ export const ToggleRegexCommandId = 'toggleSearchRegex'; export const TogglePreserveCaseId = 'toggleSearchPreserveCase'; export const AddCursorsAtSearchResults = 'addCursorsAtSearchResults'; export const RevealInSideBarForSearchResults = 'search.action.revealInSideBar'; +export const ReplaceInFilesActionId = 'workbench.action.replaceInFiles'; +export const ShowAllSymbolsActionId = 'workbench.action.showAllSymbols'; +export const CancelSearchActionId = 'search.action.cancel'; +export const RefreshSearchResultsActionId = 'search.action.refreshSearchResults'; +export const FocusNextSearchResultActionId = 'search.action.focusNextSearchResult'; +export const FocusPreviousSearchResultActionId = 'search.action.focusPreviousSearchResult'; +export const ToggleSearchOnTypeActionId = 'workbench.action.toggleSearchOnType'; +export const CollapseSearchResultsActionId = 'search.action.collapseSearchResults'; +export const ExpandSearchResultsActionId = 'search.action.expandSearchResults'; +export const ClearSearchResultsActionId = 'search.action.clearSearchResults'; +export const ViewAsTreeActionId = 'search.action.viewAsTree'; +export const ViewAsListActionId = 'search.action.viewAsList'; +export const ToggleQueryDetailsActionId = 'workbench.action.search.toggleQueryDetails'; export const SearchViewVisibleKey = new RawContextKey('searchViewletVisible', true); export const SearchViewFocusedKey = new RawContextKey('searchViewletFocus', false); @@ -44,6 +57,7 @@ export const FileMatchOrFolderMatchFocusKey = new RawContextKey('fileMa export const FileMatchOrFolderMatchWithResourceFocusKey = new RawContextKey('fileMatchOrFolderMatchWithResourceFocus', false); // Excludes "Other files" export const FileFocusKey = new RawContextKey('fileMatchFocus', false); export const FolderFocusKey = new RawContextKey('folderMatchFocus', false); +export const ResourceFolderFocusKey = new RawContextKey('folderMatchWithResourceFocus', false); export const MatchFocusKey = new RawContextKey('matchFocus', false); export const ViewHasSearchPatternKey = new RawContextKey('viewHasSearchPattern', false); export const ViewHasReplacePatternKey = new RawContextKey('viewHasReplacePattern', false); diff --git a/src/vs/workbench/contrib/search/common/searchModel.ts b/src/vs/workbench/contrib/search/common/searchModel.ts index 0aafd7dbff6..ab6b3513570 100644 --- a/src/vs/workbench/contrib/search/common/searchModel.ts +++ b/src/vs/workbench/contrib/search/common/searchModel.ts @@ -476,6 +476,7 @@ export interface IChangeEvent { elements: FileMatch[]; added?: boolean; removed?: boolean; + clearingAll?: boolean; } export class FolderMatch extends Disposable { @@ -492,10 +493,14 @@ export class FolderMatch extends Disposable { protected _unDisposedFileMatches: ResourceMap; protected _unDisposedFolderMatches: ResourceMap; private _replacingAll: boolean = false; + + // if this is compressed in a node with other FolderMatches, then this is set to the parent where compression starts + public compressionStartParent: FolderMatch | undefined; + constructor( protected _resource: URI | null, private _id: string, - protected _index: number | null, + protected _index: number, protected _query: ITextQuery, private _parent: SearchResult | FolderMatch, private _searchModel: SearchModel, @@ -537,7 +542,7 @@ export class FolderMatch extends Disposable { return this._resource; } - index(): number | null { + index(): number { return this._index; } @@ -555,13 +560,14 @@ export class FolderMatch extends Disposable { if (fileMatch) { fileMatch.bindModel(model); } else { - this.folderMatches().forEach(e => { - e.bindModel(model); - }); + const folderMatches = this.folderMatchesIterator(); + for (const elem of folderMatches) { + elem.bindModel(model); + } } } - public createIntermediateFolderMatch(resource: URI, id: string, index: number | null, query: ITextQuery, baseWorkspaceFolder: FolderMatchWorkspaceRoot): FolderMatchWithResource { + public createIntermediateFolderMatch(resource: URI, id: string, index: number, query: ITextQuery, baseWorkspaceFolder: FolderMatchWorkspaceRoot): FolderMatchWithResource { const folderMatch = this.instantiationService.createInstance(FolderMatchWithResource, resource, id, index, query, this, this._searchModel, baseWorkspaceFolder); this.configureIntermediateMatch(folderMatch); this.doAddFolder(folderMatch); @@ -573,10 +579,10 @@ export class FolderMatch extends Disposable { folderMatch.onDispose(() => disposable.dispose()); } - clear(): void { + clear(clearingAll = false): void { const changed: FileMatch[] = this.downstreamFileMatches(); this.disposeMatches(); - this._onChange.fire({ elements: changed, removed: true, added: false }); + this._onChange.fire({ elements: changed, removed: true, added: false, clearingAll }); } remove(matches: FileMatch | FolderMatchWithResource | (FileMatch | FolderMatchWithResource)[]): void { @@ -599,15 +605,15 @@ export class FolderMatch extends Disposable { } matches(): (FileMatch | FolderMatchWithResource)[] { - return [...this.fileMatches(), ...this.folderMatches()]; + return [...this.fileMatchesIterator(), ...this.folderMatchesIterator()]; } - fileMatches(): FileMatch[] { - return [...this._fileMatches.values()]; + fileMatchesIterator(): IterableIterator { + return this._fileMatches.values(); } - folderMatches(): FolderMatchWithResource[] { - return [...this._folderMatches.values()]; + folderMatchesIterator(): IterableIterator { + return this._folderMatches.values(); } isEmpty(): boolean { @@ -619,8 +625,10 @@ export class FolderMatch extends Disposable { if (directChildFileMatch) { return directChildFileMatch; } - for (let i = 0; i < this.folderMatches().length; i++) { - const match = this.folderMatches()[i].hasFileUriDownstream(uri); + + const folderMatches = this.folderMatchesIterator(); + for (const elem of folderMatches) { + const match = elem.hasFileUriDownstream(uri); if (match) { return match; } @@ -629,8 +637,13 @@ export class FolderMatch extends Disposable { } downstreamFileMatches(): FileMatch[] { - const recursiveChildren = this.folderMatches().map(e => e.downstreamFileMatches()).flat(); - return [...this.fileMatches(), ...recursiveChildren]; + let recursiveChildren: FileMatch[] = []; + const iterator = this.folderMatchesIterator(); + for (const elem of iterator) { + recursiveChildren = recursiveChildren.concat(elem.downstreamFileMatches()); + } + + return [...this.fileMatchesIterator(), ...recursiveChildren]; } private fileCount(): number { @@ -649,6 +662,10 @@ export class FolderMatch extends Disposable { return this.downstreamFileMatches().length; } + recursiveMatchCount(): number { + return this.downstreamFileMatches().reduce((prev, match) => prev + match.count(), 0); + } + get query(): ITextQuery | null { return this._query; } @@ -708,7 +725,7 @@ export class FolderMatch extends Disposable { return false; } - private getFolderMatch(resource: URI): FolderMatchWithResource | undefined { + public getFolderMatch(resource: URI): FolderMatchWithResource | undefined { const folderMatch = this._folderMatchesMap.findSubstr(resource); return folderMatch; } @@ -766,7 +783,7 @@ export class FolderMatch extends Disposable { const removed = []; for (const match of fileMatches as FileMatch[]) { - if (this.fileMatches().includes(match)) { + if (this._fileMatches.get(match.resource)) { this._fileMatches.delete(match.resource); if (dispose) { match.dispose(); @@ -809,7 +826,7 @@ export class FolderMatch extends Disposable { export class FolderMatchWithResource extends FolderMatch { - constructor(_resource: URI, _id: string, _index: number | null, _query: ITextQuery, _parent: SearchResult | FolderMatch, _searchModel: SearchModel, _closestRoot: FolderMatchWorkspaceRoot | null, + constructor(_resource: URI, _id: string, _index: number, _query: ITextQuery, _parent: SearchResult | FolderMatch, _searchModel: SearchModel, _closestRoot: FolderMatchWorkspaceRoot | null, @IReplaceService replaceService: IReplaceService, @IInstantiationService instantiationService: IInstantiationService, @ILabelService labelService: ILabelService, @@ -873,19 +890,15 @@ export class FolderMatchWorkspaceRoot extends FolderMatchWithResource { const root = this.closestRoot ?? this; let parent: FolderMatch = this; for (let i = 0; i < fileMatchParentParts.length; i++) { - let folderMatch: FolderMatchWithResource | undefined = parent.folderMatches().find(e => e.resource && (this.uriEquals(e.resource, fileMatchParentParts[i]))); + let folderMatch: FolderMatchWithResource | undefined = parent.getFolderMatch(fileMatchParentParts[i]); if (!folderMatch) { - folderMatch = parent.createIntermediateFolderMatch(fileMatchParentParts[i], fileMatchParentParts[i].toString(), null, this._query, root); + folderMatch = parent.createIntermediateFolderMatch(fileMatchParentParts[i], fileMatchParentParts[i].toString(), -1, this._query, root); } parent = folderMatch; } return this.createFileMatch(this._query.contentPattern, this._query.previewOptions, this._query.maxResults, parent, rawFileMatch, root); } - - override index(): number { - return this._index!; - } } /** @@ -911,17 +924,45 @@ export class FolderMatchNoRoot extends FolderMatch { } } +let elemAIndex: number = -1; +let elemBIndex: number = -1; /** * Compares instances of the same match type. Different match types should not be siblings * and their sort order is undefined. */ export function searchMatchComparer(elementA: RenderableMatch, elementB: RenderableMatch, sortOrder: SearchSortOrder = SearchSortOrder.Default): number { + + if (elementA instanceof FileMatch && elementB instanceof FolderMatch) { + return 1; + } + + if (elementB instanceof FileMatch && elementA instanceof FolderMatch) { + return -1; + } + if (elementA instanceof FolderMatch && elementB instanceof FolderMatch) { - const elemAIndex = elementA.index(); - const elemBIndex = elementB.index(); - if (elemAIndex !== null && elemBIndex !== null) { + elemAIndex = elementA.index(); + elemBIndex = elementB.index(); + if (elemAIndex !== -1 && elemBIndex !== -1) { return elemAIndex - elemBIndex; } + + switch (sortOrder) { + case SearchSortOrder.CountDescending: + return elementB.count() - elementA.count(); + case SearchSortOrder.CountAscending: + return elementA.count() - elementB.count(); + case SearchSortOrder.Type: + return compareFileExtensions(elementA.name(), elementB.name()); + case SearchSortOrder.FileNames: + return compareFileNames(elementA.name(), elementB.name()); + // Fall through otherwise + default: + if (!elementA.resource || !elementB.resource) { + return 0; + } + return comparePaths(elementA.resource.fsPath, elementB.resource.fsPath) || compareFileNames(elementA.name(), elementB.name()); + } } if (elementA instanceof FileMatch && elementB instanceof FileMatch) { @@ -935,12 +976,11 @@ export function searchMatchComparer(elementA: RenderableMatch, elementB: Rendera case SearchSortOrder.FileNames: return compareFileNames(elementA.name(), elementB.name()); case SearchSortOrder.Modified: { - if (!(elementA instanceof FolderMatch) || !(elementB instanceof FolderMatch)) { - const fileStatA = elementA.fileStat; - const fileStatB = elementB.fileStat; - if (fileStatA && fileStatB) { - return fileStatB.mtime - fileStatA.mtime; - } + const fileStatA = elementA.fileStat; + const fileStatB = elementB.fileStat; + if (fileStatA && fileStatB) { + return fileStatB.mtime - fileStatA.mtime; + } } // Fall through otherwise @@ -1154,7 +1194,7 @@ export class SearchResult extends Disposable { } clear(): void { - this.folderMatches().forEach((folderMatch) => folderMatch.clear()); + this.folderMatches().forEach((folderMatch) => folderMatch.clear(true)); this.disposeMatches(); this._folderMatches = []; this._otherFilesMatch = null; @@ -1328,7 +1368,7 @@ export class SearchModel extends Disposable { private _replacePattern: ReplacePattern | null = null; private _preserveCase: boolean = false; private _startStreamDelay: Promise = Promise.resolve(); - private _resultQueue: IFileMatch[] = []; + private readonly _resultQueue: IFileMatch[] = []; private readonly _onReplaceTermChanged: Emitter = this._register(new Emitter()); readonly onReplaceTermChanged: Event = this._onReplaceTermChanged.event; @@ -1444,7 +1484,7 @@ export class SearchModel extends Disposable { } this._searchResult.add(this._resultQueue); - this._resultQueue = []; + this._resultQueue.length = 0; const options: IPatternInfo = Object.assign({}, this._searchQuery.contentPattern); delete (options as any).pattern; @@ -1498,7 +1538,7 @@ export class SearchModel extends Disposable { await this._startStreamDelay; if (this._resultQueue.length) { this._searchResult.add(this._resultQueue, true); - this._resultQueue = []; + this._resultQueue.length = 0; } } } diff --git a/src/vs/workbench/contrib/search/test/browser/mockSearchTree.ts b/src/vs/workbench/contrib/search/test/browser/mockSearchTree.ts index 5717a23bfbe..681dae8164c 100644 --- a/src/vs/workbench/contrib/search/test/browser/mockSearchTree.ts +++ b/src/vs/workbench/contrib/search/test/browser/mockSearchTree.ts @@ -6,6 +6,7 @@ import { ITreeNavigator } from 'vs/base/browser/ui/tree/tree'; import { Emitter } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { RenderableMatch } from 'vs/workbench/contrib/search/common/searchModel'; const someEvent = new Emitter().event; @@ -33,6 +34,7 @@ export class MockObjectTree implements IDisposable { get onDidChangeRenderNodeCount() { return someEvent; } get onDidDispose() { return someEvent; } + get lastVisibleElement() { return this.elements[this.elements.length - 1]; } constructor(private elements: any[]) { } @@ -53,6 +55,10 @@ export class MockObjectTree implements IDisposable { return new ArrayNavigator(this.elements, startIdx); } + getParentElement(elem: RenderableMatch) { + return elem.parent(); + } + dispose(): void { } } diff --git a/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts b/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts index 623a2b6f972..d152f109924 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts @@ -15,7 +15,7 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; import { IFileMatch } from 'vs/workbench/services/search/common/search'; -import { ReplaceAction } from 'vs/workbench/contrib/search/browser/searchActions'; +import { getElementToFocusAfterRemoved, getLastNodeFromSameType } from 'vs/workbench/contrib/search/browser/searchActions'; import { FileMatch, FileMatchOrMatch, Match } from 'vs/workbench/contrib/search/common/searchModel'; import { MockObjectTree } from 'vs/workbench/contrib/search/test/browser/mockSearchTree'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -32,6 +32,7 @@ suite('Search Actions', () => { instantiationService.stub(IKeybindingService, {}); instantiationService.stub(IKeybindingService, 'resolveKeybinding', (keybinding: Keybinding) => [new USLayoutResolvedKeybinding(keybinding, OS)]); instantiationService.stub(IKeybindingService, 'lookupKeybinding', (id: string) => null); + instantiationService.stub(IKeybindingService, 'lookupKeybinding', (id: string) => null); counter = 0; }); @@ -41,44 +42,18 @@ suite('Search Actions', () => { const data = [fileMatch1, aMatch(fileMatch1), aMatch(fileMatch1), fileMatch2, aMatch(fileMatch2), aMatch(fileMatch2)]; const tree = aTree(data); const target = data[2]; - const testObject: ReplaceAction = instantiationService.createInstance(ReplaceAction, tree, target, null); - const actual = testObject.getElementToFocusAfterRemoved(tree, target, false); + const actual = getElementToFocusAfterRemoved(tree, target, [target]); assert.strictEqual(data[4], actual); }); - test('get next element to focus after removing a match when it does not have next sibling match', function () { - const fileMatch1 = aFileMatch(); - const fileMatch2 = aFileMatch(); - const data = [fileMatch1, aMatch(fileMatch1), aMatch(fileMatch1), fileMatch2, aMatch(fileMatch2), aMatch(fileMatch2)]; - const tree = aTree(data); - const target = data[5]; - const testObject: ReplaceAction = instantiationService.createInstance(ReplaceAction, tree, target, null); - - const actual = testObject.getElementToFocusAfterRemoved(tree, target, false); - assert.strictEqual(data[4], actual); - }); - - test('get next element to focus after removing a match when it does not have next sibling match and previous match is file match', function () { - const fileMatch1 = aFileMatch(); - const fileMatch2 = aFileMatch(); - const data = [fileMatch1, aMatch(fileMatch1), aMatch(fileMatch1), fileMatch2, aMatch(fileMatch2)]; - const tree = aTree(data); - const target = data[4]; - const testObject: ReplaceAction = instantiationService.createInstance(ReplaceAction, tree, target, null); - - const actual = testObject.getElementToFocusAfterRemoved(tree, target, false); - assert.strictEqual(data[2], actual); - }); - test('get next element to focus after removing a match when it is the only match', function () { const fileMatch1 = aFileMatch(); const data = [fileMatch1, aMatch(fileMatch1)]; const tree = aTree(data); const target = data[1]; - const testObject: ReplaceAction = instantiationService.createInstance(ReplaceAction, tree, target, null); - const actual = testObject.getElementToFocusAfterRemoved(tree, target, false); + const actual = getElementToFocusAfterRemoved(tree, target, [target]); assert.strictEqual(undefined, actual); }); @@ -89,23 +64,31 @@ suite('Search Actions', () => { const data = [fileMatch1, aMatch(fileMatch1), fileMatch2, aMatch(fileMatch2), fileMatch3, aMatch(fileMatch3)]; const tree = aTree(data); const target = data[2]; - const testObject: ReplaceAction = instantiationService.createInstance(ReplaceAction, tree, target, null); - const actual = testObject.getElementToFocusAfterRemoved(tree, target, false); + const actual = getElementToFocusAfterRemoved(tree, target, []); assert.strictEqual(data[4], actual); }); - test('get next element to focus after removing a file match when it has no next sibling', function () { + test('Find last FileMatch in Tree', function () { const fileMatch1 = aFileMatch(); const fileMatch2 = aFileMatch(); const fileMatch3 = aFileMatch(); const data = [fileMatch1, aMatch(fileMatch1), fileMatch2, aMatch(fileMatch2), fileMatch3, aMatch(fileMatch3)]; const tree = aTree(data); - const target = data[4]; - const testObject: ReplaceAction = instantiationService.createInstance(ReplaceAction, tree, target, null); - const actual = testObject.getElementToFocusAfterRemoved(tree, target, false); - assert.strictEqual(data[3], actual); + const actual = getLastNodeFromSameType(tree, fileMatch1); + assert.strictEqual(fileMatch3, actual); + }); + + test('Find last Match in Tree', function () { + const fileMatch1 = aFileMatch(); + const fileMatch2 = aFileMatch(); + const fileMatch3 = aFileMatch(); + const data = [fileMatch1, aMatch(fileMatch1), fileMatch2, aMatch(fileMatch2), fileMatch3, aMatch(fileMatch3)]; + const tree = aTree(data); + + const actual = getLastNodeFromSameType(tree, aMatch(fileMatch1)); + assert.strictEqual(data[5], actual); }); test('get next element to focus after removing a file match when it is only match', function () { @@ -113,9 +96,9 @@ suite('Search Actions', () => { const data = [fileMatch1, aMatch(fileMatch1)]; const tree = aTree(data); const target = data[0]; - const testObject: ReplaceAction = instantiationService.createInstance(ReplaceAction, tree, target, null); + // const testObject: ReplaceAction = instantiationService.createInstance(ReplaceAction, tree, target, null); - const actual = testObject.getElementToFocusAfterRemoved(tree, target, false); + const actual = getElementToFocusAfterRemoved(tree, target, []); assert.strictEqual(undefined, actual); }); @@ -124,7 +107,7 @@ suite('Search Actions', () => { resource: URI.file('somepath' + ++counter), results: [] }; - return instantiationService.createInstance(FileMatch, null, null, null, null, rawMatch); + return instantiationService.createInstance(FileMatch, null, null, null, null, rawMatch, null); } function aMatch(fileMatch: FileMatch): Match { diff --git a/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts b/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts index 41a1754d7c0..d003bb48d85 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts @@ -168,7 +168,7 @@ suite('Search - Viewlet', () => { resource: createFileUriFromPathFromRoot(path), results: lineMatches }; - return instantiation.createInstance(FileMatch, null, null, null, parentFolder, rawMatch); + return instantiation.createInstance(FileMatch, null, null, null, parentFolder, rawMatch, parentFolder); } function aFolderMatch(path: string, index: number, parent?: SearchResult): FolderMatch { diff --git a/src/vs/workbench/contrib/search/test/common/searchResult.test.ts b/src/vs/workbench/contrib/search/test/common/searchResult.test.ts index 15ed87adcce..19a836683ee 100644 --- a/src/vs/workbench/contrib/search/test/common/searchResult.test.ts +++ b/src/vs/workbench/contrib/search/test/common/searchResult.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import * as sinon from 'sinon'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { Match, FileMatch, SearchResult, SearchModel } from 'vs/workbench/contrib/search/common/searchModel'; +import { Match, FileMatch, SearchResult, SearchModel, FolderMatch } from 'vs/workbench/contrib/search/common/searchModel'; import { URI } from 'vs/base/common/uri'; import { IFileMatch, TextSearchMatch, OneLineRange, ITextSearchMatch, QueryType } from 'vs/workbench/services/search/common/search'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -402,36 +402,36 @@ suite('SearchResult', () => { const root3 = testObject.folderMatches()[3]; const root0DownstreamFiles = root0.downstreamFileMatches(); - assert.deepStrictEqual(root0DownstreamFiles, root0.fileMatches().concat(root0.folderMatches()[0].fileMatches())); - assert.deepStrictEqual(root0.folderMatches()[0].downstreamFileMatches(), root0.folderMatches()[0].fileMatches()); - assert.deepStrictEqual(root0.folderMatches()[0].fileMatches()[0].parent(), root0.folderMatches()[0]); - assert.deepStrictEqual(root0.folderMatches()[0].parent(), root0); - assert.deepStrictEqual(root0.folderMatches()[0].closestRoot, root0); + assert.deepStrictEqual(root0DownstreamFiles, [...root0.fileMatchesIterator(), ...getFolderMatchAtIndex(root0, 0).fileMatchesIterator()]); + assert.deepStrictEqual(getFolderMatchAtIndex(root0, 0).downstreamFileMatches(), Array.from(getFolderMatchAtIndex(root0, 0).fileMatchesIterator())); + assert.deepStrictEqual(getFileMatchAtIndex(getFolderMatchAtIndex(root0, 0), 0).parent(), getFolderMatchAtIndex(root0, 0)); + assert.deepStrictEqual(getFolderMatchAtIndex(root0, 0).parent(), root0); + assert.deepStrictEqual(getFolderMatchAtIndex(root0, 0).closestRoot, root0); root0DownstreamFiles.forEach((e) => { assert.deepStrictEqual(e.closestRoot, root0); }); const root1DownstreamFiles = root1.downstreamFileMatches(); - assert.deepStrictEqual(root1.downstreamFileMatches(), root1.fileMatches().concat(root1.folderMatches()[0].fileMatches())); // excludes the matches from nested root - assert.deepStrictEqual(root1.folderMatches()[0].fileMatches()[0].parent(), root1.folderMatches()[0]); + assert.deepStrictEqual(root1.downstreamFileMatches(), [...root1.fileMatchesIterator(), ...getFolderMatchAtIndex(root1, 0).fileMatchesIterator()]); // excludes the matches from nested root + assert.deepStrictEqual(getFileMatchAtIndex(getFolderMatchAtIndex(root1, 0), 0).parent(), getFolderMatchAtIndex(root1, 0)); root1DownstreamFiles.forEach((e) => { assert.deepStrictEqual(e.closestRoot, root1); }); const root2DownstreamFiles = root2.downstreamFileMatches(); - assert.deepStrictEqual(root2DownstreamFiles, root2.fileMatches()); - assert.deepStrictEqual(root2.fileMatches()[0].parent(), root2); - assert.deepStrictEqual(root2.fileMatches()[0].closestRoot, root2); + assert.deepStrictEqual(root2DownstreamFiles, Array.from(root2.fileMatchesIterator())); + assert.deepStrictEqual(getFileMatchAtIndex(root2, 0).parent(), root2); + assert.deepStrictEqual(getFileMatchAtIndex(root2, 0).closestRoot, root2); const root3DownstreamFiles = root3.downstreamFileMatches(); - const root3Level3Folder = root3.folderMatches()[0].folderMatches()[0]; - assert.deepStrictEqual(root3DownstreamFiles, [root3.fileMatches(), ...root3Level3Folder.folderMatches()[0].fileMatches(), ...root3Level3Folder.folderMatches()[1].fileMatches()].flat()); - assert.deepStrictEqual(root3Level3Folder.downstreamFileMatches(), root3.folderMatches()[0].downstreamFileMatches()); + const root3Level3Folder = getFolderMatchAtIndex(getFolderMatchAtIndex(root3, 0), 0); + assert.deepStrictEqual(root3DownstreamFiles, [...root3.fileMatchesIterator(), ...getFolderMatchAtIndex(root3Level3Folder, 0).fileMatchesIterator(), ...getFolderMatchAtIndex(root3Level3Folder, 1).fileMatchesIterator()].flat()); + assert.deepStrictEqual(root3Level3Folder.downstreamFileMatches(), getFolderMatchAtIndex(root3, 0).downstreamFileMatches()); - assert.deepStrictEqual(root3Level3Folder.folderMatches()[1].fileMatches()[0].parent(), root3Level3Folder.folderMatches()[1]); - assert.deepStrictEqual(root3Level3Folder.folderMatches()[1].parent(), root3Level3Folder); - assert.deepStrictEqual(root3Level3Folder.parent(), root3.folderMatches()[0]); + assert.deepStrictEqual(getFileMatchAtIndex(getFolderMatchAtIndex(root3Level3Folder, 1), 0).parent(), getFolderMatchAtIndex(root3Level3Folder, 1)); + assert.deepStrictEqual(getFolderMatchAtIndex(root3Level3Folder, 1).parent(), root3Level3Folder); + assert.deepStrictEqual(root3Level3Folder.parent(), getFolderMatchAtIndex(root3, 0)); root3DownstreamFiles.forEach((e) => { assert.deepStrictEqual(e.closestRoot, root3); @@ -442,21 +442,21 @@ suite('SearchResult', () => { const target = sinon.spy(); const testObject = getPopulatedSearchResultForTreeTesting(); - const folderMatch = testObject.folderMatches()[3].folderMatches()[0].folderMatches()[0].folderMatches()[0]; + const folderMatch = getFolderMatchAtIndex(getFolderMatchAtIndex(getFolderMatchAtIndex(testObject.folderMatches()[3], 0), 0), 0); const expectedArrayResult = folderMatch.downstreamFileMatches(); testObject.onChange(target); testObject.remove(folderMatch); assert.ok(target.calledOnce); - assert.deepStrictEqual([{ elements: expectedArrayResult, removed: true, added: false }], target.args[0]); + assert.deepStrictEqual([{ elements: expectedArrayResult, removed: true, added: false, clearingAll: false }], target.args[0]); }); test('Replacing an intermediate folder should remove all downstream folders and file matches', async function () { const target = sinon.spy(); const testObject = getPopulatedSearchResultForTreeTesting(); - const folderMatch = testObject.folderMatches()[3].folderMatches()[0]; + const folderMatch = getFolderMatchAtIndex(testObject.folderMatches()[3], 0); const expectedArrayResult = folderMatch.downstreamFileMatches(); @@ -471,7 +471,7 @@ suite('SearchResult', () => { resource: URI.file('/' + path), results: lineMatches }; - return instantiationService.createInstance(FileMatch, null, null, null, searchResult, rawMatch); + return instantiationService.createInstance(FileMatch, null, null, null, searchResult, rawMatch, searchResult); } function aSearchResult(): SearchResult { @@ -600,4 +600,12 @@ suite('SearchResult', () => { ]); return testObject; } + + function getFolderMatchAtIndex(parent: FolderMatch, index: number) { + return Array.from(parent.folderMatchesIterator())[index]; + } + + function getFileMatchAtIndex(parent: FolderMatch, index: number) { + return Array.from(parent.fileMatchesIterator())[index]; + } }); diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts index 6e72f838ba0..a8a22c38b18 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts @@ -100,7 +100,7 @@ class SearchEditorContribution implements IWorkbenchContribution { } const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchContributionsRegistry.registerWorkbenchContribution(SearchEditorContribution, 'SearchEditorContribution', LifecyclePhase.Starting); +workbenchContributionsRegistry.registerWorkbenchContribution(SearchEditorContribution, LifecyclePhase.Starting); //#endregion //#region Input Serializer @@ -592,5 +592,5 @@ class SearchEditorWorkingCopyEditorHandler extends Disposable implements IWorkbe } } -workbenchContributionsRegistry.registerWorkbenchContribution(SearchEditorWorkingCopyEditorHandler, 'SearchEditorWorkingCopyEditorHandler', LifecyclePhase.Ready); +workbenchContributionsRegistry.registerWorkbenchContribution(SearchEditorWorkingCopyEditorHandler, LifecyclePhase.Ready); //#endregion diff --git a/src/vs/workbench/contrib/snippets/browser/commands/abstractSnippetsActions.ts b/src/vs/workbench/contrib/snippets/browser/commands/abstractSnippetsActions.ts index 46f62be9e1c..cf919a8c50b 100644 --- a/src/vs/workbench/contrib/snippets/browser/commands/abstractSnippetsActions.ts +++ b/src/vs/workbench/contrib/snippets/browser/commands/abstractSnippetsActions.ts @@ -7,12 +7,12 @@ import { EditorAction2 } from 'vs/editor/browser/editorExtensions'; import { localize } from 'vs/nls'; import { Action2, IAction2Options } from 'vs/platform/actions/common/actions'; -const defaultOptions: Partial = { +const defaultOptions = { category: { value: localize('snippets', 'Snippets'), original: 'Snippets' }, -}; +} as const; export abstract class SnippetsAction extends Action2 { diff --git a/src/vs/workbench/contrib/snippets/browser/commands/configureSnippets.ts b/src/vs/workbench/contrib/snippets/browser/commands/configureSnippets.ts index 3bc1b0b6b89..baee9823a1f 100644 --- a/src/vs/workbench/contrib/snippets/browser/commands/configureSnippets.ts +++ b/src/vs/workbench/contrib/snippets/browser/commands/configureSnippets.ts @@ -214,8 +214,8 @@ export class ConfigureSnippets extends SnippetsAction { mnemonicTitle: nls.localize({ key: 'miOpenSnippets', comment: ['&& denotes a mnemonic'] }, "User &&Snippets"), original: 'User Snippets' }, + f1: true, menu: [ - { id: MenuId.CommandPalette }, { id: MenuId.MenubarPreferencesMenu, group: '3_snippets', order: 1 }, { id: MenuId.GlobalActivity, group: '3_snippets', order: 1 }, ] diff --git a/src/vs/workbench/contrib/snippets/browser/commands/insertSnippet.ts b/src/vs/workbench/contrib/snippets/browser/commands/insertSnippet.ts index 5b10727b169..d8b905d5f79 100644 --- a/src/vs/workbench/contrib/snippets/browser/commands/insertSnippet.ts +++ b/src/vs/workbench/contrib/snippets/browser/commands/insertSnippet.ts @@ -147,6 +147,7 @@ export class InsertSnippetAction extends SnippetEditorAction { if (snippet.needsClipboard) { clipboardText = await clipboardService.readText(); } + editor.focus(); SnippetController2.get(editor)?.insert(snippet.codeSnippet, { clipboardText }); snippetService.updateUsageTimestamp(snippet); } diff --git a/src/vs/workbench/contrib/snippets/browser/commands/surroundWithSnippet.ts b/src/vs/workbench/contrib/snippets/browser/commands/surroundWithSnippet.ts index bdd36872158..e42370ce4fa 100644 --- a/src/vs/workbench/contrib/snippets/browser/commands/surroundWithSnippet.ts +++ b/src/vs/workbench/contrib/snippets/browser/commands/surroundWithSnippet.ts @@ -72,6 +72,7 @@ export class SurroundWithSnippetEditorAction extends SnippetEditorAction { clipboardText = await clipboardService.readText(); } + editor.focus(); SnippetController2.get(editor)?.insert(snippet.codeSnippet, { clipboardText }); snippetsService.updateUsageTimestamp(snippet); } diff --git a/src/vs/workbench/contrib/snippets/browser/snippets.contribution.ts b/src/vs/workbench/contrib/snippets/browser/snippets.contribution.ts index 24d7e406307..cb5aa0de617 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippets.contribution.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippets.contribution.ts @@ -7,7 +7,7 @@ import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema'; import * as nls from 'vs/nls'; import { registerAction2 } from 'vs/platform/actions/common/actions'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import * as JSONContributionRegistry from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; @@ -25,7 +25,7 @@ import 'vs/workbench/contrib/snippets/browser/tabCompletion'; import { editorConfigurationBaseNode } from 'vs/editor/common/config/editorConfigurationSchema'; // service -registerSingleton(ISnippetsService, SnippetsService, true); +registerSingleton(ISnippetsService, SnippetsService, InstantiationType.Delayed); // actions registerAction2(InsertSnippetAction); @@ -36,7 +36,7 @@ registerAction2(ApplyFileSnippetAction); // workbench contribs Registry.as(WorkbenchExtensions.Workbench) - .registerWorkbenchContribution(SnippetCodeActions, 'SnippetCodeActions', LifecyclePhase.Restored); + .registerWorkbenchContribution(SnippetCodeActions, LifecyclePhase.Restored); // config Registry diff --git a/src/vs/workbench/contrib/snippets/browser/snippetsFile.ts b/src/vs/workbench/contrib/snippets/browser/snippetsFile.ts index 070fd986cdf..7d88688f68d 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippetsFile.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippetsFile.ts @@ -12,7 +12,7 @@ import { URI } from 'vs/base/common/uri'; import { IFileService } from 'vs/platform/files/common/files'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { IdleValue } from 'vs/base/common/async'; -import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; +import { IExtensionResourceLoaderService } from 'vs/platform/extensionResourceLoader/common/extensionResourceLoader'; import { relativePath } from 'vs/base/common/resources'; import { isObject } from 'vs/base/common/types'; import { Iterable } from 'vs/base/common/iterator'; diff --git a/src/vs/workbench/contrib/snippets/browser/snippetsService.ts b/src/vs/workbench/contrib/snippets/browser/snippetsService.ts index 069f3bf44f3..c26746632e3 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippetsService.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippetsService.ts @@ -22,7 +22,7 @@ import { Snippet, SnippetFile, SnippetSource } from 'vs/workbench/contrib/snippe import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { languagesExtPoint } from 'vs/workbench/services/language/common/languageService'; import { SnippetCompletionProvider } from './snippetCompletionProvider'; -import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; +import { IExtensionResourceLoaderService } from 'vs/platform/extensionResourceLoader/common/extensionResourceLoader'; import { ResourceMap } from 'vs/base/common/map'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { isStringArray } from 'vs/base/common/types'; diff --git a/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts b/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts index a414f31246d..b4581ae1494 100644 --- a/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts +++ b/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import { SnippetCompletion, SnippetCompletionProvider } from 'vs/workbench/contrib/snippets/browser/snippetCompletionProvider'; import { Position } from 'vs/editor/common/core/position'; import { createModelServices, instantiateTextModel } from 'vs/editor/test/common/testTextModel'; -import { ISnippetsService } from "vs/workbench/contrib/snippets/browser/snippets"; +import { ISnippetsService } from 'vs/workbench/contrib/snippets/browser/snippets'; import { Snippet, SnippetSource } from 'vs/workbench/contrib/snippets/browser/snippetsFile'; import { CompletionContext, CompletionItemLabel, CompletionItemRanges, CompletionTriggerKind } from 'vs/editor/common/languages'; import { DisposableStore } from 'vs/base/common/lifecycle'; diff --git a/src/vs/workbench/contrib/splash/browser/splash.contribution.ts b/src/vs/workbench/contrib/splash/browser/splash.contribution.ts index e427bc2e0e2..a6b4093aed9 100644 --- a/src/vs/workbench/contrib/splash/browser/splash.contribution.ts +++ b/src/vs/workbench/contrib/splash/browser/splash.contribution.ts @@ -7,7 +7,7 @@ import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { ISplashStorageService } from 'vs/workbench/contrib/splash/browser/splash'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { PartsSplash } from 'vs/workbench/contrib/splash/browser/partsSplash'; import { IPartsSplash } from 'vs/platform/theme/common/themeService'; @@ -18,10 +18,9 @@ registerSingleton(ISplashStorageService, class SplashStorageService implements I const raw = JSON.stringify(splash); localStorage.setItem('monaco-parts-splash', raw); } -}, true); +}, InstantiationType.Delayed); Registry.as(Extensions.Workbench).registerWorkbenchContribution( PartsSplash, - 'PartsSplash', LifecyclePhase.Starting ); diff --git a/src/vs/workbench/contrib/splash/electron-sandbox/splash.contribution.ts b/src/vs/workbench/contrib/splash/electron-sandbox/splash.contribution.ts index 904e65ce6cb..a2a88ad20e5 100644 --- a/src/vs/workbench/contrib/splash/electron-sandbox/splash.contribution.ts +++ b/src/vs/workbench/contrib/splash/electron-sandbox/splash.contribution.ts @@ -8,7 +8,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { ISplashStorageService } from 'vs/workbench/contrib/splash/browser/splash'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { PartsSplash } from 'vs/workbench/contrib/splash/browser/partsSplash'; import { IPartsSplash } from 'vs/platform/theme/common/themeService'; @@ -21,10 +21,9 @@ class SplashStorageService implements ISplashStorageService { } } -registerSingleton(ISplashStorageService, SplashStorageService, true); +registerSingleton(ISplashStorageService, SplashStorageService, InstantiationType.Delayed); Registry.as(Extensions.Workbench).registerWorkbenchContribution( PartsSplash, - 'PartsSplash', LifecyclePhase.Starting ); diff --git a/src/vs/workbench/contrib/surveys/browser/ces.contribution.ts b/src/vs/workbench/contrib/surveys/browser/ces.contribution.ts index aa6e5e53f50..3320a020843 100644 --- a/src/vs/workbench/contrib/surveys/browser/ces.contribution.ts +++ b/src/vs/workbench/contrib/surveys/browser/ces.contribution.ts @@ -160,5 +160,5 @@ class CESContribution extends Disposable implements IWorkbenchContribution { if (language === 'en') { const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); - workbenchRegistry.registerWorkbenchContribution(CESContribution, 'CESContribution', LifecyclePhase.Restored); + workbenchRegistry.registerWorkbenchContribution(CESContribution, LifecyclePhase.Restored); } diff --git a/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts b/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts index c05d75f92e7..ae6e3b498bc 100644 --- a/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts +++ b/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts @@ -160,5 +160,5 @@ class LanguageSurveysContribution implements IWorkbenchContribution { if (language === 'en') { const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); - workbenchRegistry.registerWorkbenchContribution(LanguageSurveysContribution, 'LanguageSurveysContribution', LifecyclePhase.Restored); + workbenchRegistry.registerWorkbenchContribution(LanguageSurveysContribution, LifecyclePhase.Restored); } diff --git a/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts b/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts index df89651c60d..bb1e79f25e4 100644 --- a/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts +++ b/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts @@ -94,5 +94,5 @@ class NPSContribution implements IWorkbenchContribution { if (language === 'en') { const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); - workbenchRegistry.registerWorkbenchContribution(NPSContribution, 'NPSContribution', LifecyclePhase.Restored); + workbenchRegistry.registerWorkbenchContribution(NPSContribution, LifecyclePhase.Restored); } diff --git a/src/vs/workbench/contrib/tags/browser/workspaceTagsService.ts b/src/vs/workbench/contrib/tags/browser/workspaceTagsService.ts index 0fbc6faa520..a955a05b091 100644 --- a/src/vs/workbench/contrib/tags/browser/workspaceTagsService.ts +++ b/src/vs/workbench/contrib/tags/browser/workspaceTagsService.ts @@ -5,7 +5,7 @@ import { WorkbenchState, IWorkspace } from 'vs/platform/workspace/common/workspace'; import { URI } from 'vs/base/common/uri'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkspaceTagsService, Tags } from 'vs/workbench/contrib/tags/common/workspaceTags'; export class NoOpWorkspaceTagsService implements IWorkspaceTagsService { @@ -25,4 +25,4 @@ export class NoOpWorkspaceTagsService implements IWorkspaceTagsService { } } -registerSingleton(IWorkspaceTagsService, NoOpWorkspaceTagsService, true); +registerSingleton(IWorkspaceTagsService, NoOpWorkspaceTagsService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/tags/electron-sandbox/tags.contribution.ts b/src/vs/workbench/contrib/tags/electron-sandbox/tags.contribution.ts index 61ce8213cd8..887b429b74c 100644 --- a/src/vs/workbench/contrib/tags/electron-sandbox/tags.contribution.ts +++ b/src/vs/workbench/contrib/tags/electron-sandbox/tags.contribution.ts @@ -9,4 +9,4 @@ import { WorkspaceTags } from 'vs/workbench/contrib/tags/electron-sandbox/worksp import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; // Register Workspace Tags Contribution -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(WorkspaceTags, 'WorkspaceTags', LifecyclePhase.Eventually); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(WorkspaceTags, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts b/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts index 603020d57eb..6859b738e2a 100644 --- a/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts +++ b/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts @@ -10,7 +10,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { ITextFileService, ITextFileContent } from 'vs/workbench/services/textfile/common/textfiles'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkspaceTagsService, Tags } from 'vs/workbench/contrib/tags/common/workspaceTags'; import { getHashedRemotesFromConfig } from 'vs/workbench/contrib/tags/electron-sandbox/workspaceTags'; import { splitLines } from 'vs/base/common/strings'; @@ -812,4 +812,4 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { } } -registerSingleton(IWorkspaceTagsService, WorkspaceTagsService, true); +registerSingleton(IWorkspaceTagsService, WorkspaceTagsService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 4c78b403fa1..45e556780fc 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -327,7 +327,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this._register(this.onDidStateChange(e => { if ((this._willRestart || e.exitReason === TerminalExitReason.User) && e.taskId) { this.removePersistentTask(e.taskId); - } else if (e.kind === TaskEventKind.Start && e.__task) { + } else if (e.kind === TaskEventKind.Start && e.__task && e.__task.getWorkspaceFolder()) { this._setPersistentTask(e.__task); } })); @@ -1815,7 +1815,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (saveBeforeRunTaskConfig === SaveBeforeRunConfigOptions.Never) { return false; - } else if (saveBeforeRunTaskConfig === SaveBeforeRunConfigOptions.Prompt) { + } else if (saveBeforeRunTaskConfig === SaveBeforeRunConfigOptions.Prompt && this._editorService.editors.some(e => e.isDirty())) { const dialogOptions = await this._dialogService.show( Severity.Info, nls.localize('TaskSystem.saveBeforeRun.prompt.title', 'Save all editors?'), @@ -2354,8 +2354,8 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return { workspaceFolder, set: undefined, configurations: undefined, hasErrors: false }; } - private async _computeTasksForSingleConfig(workspaceFolder: IWorkspaceFolder, config: TaskConfig.IExternalTaskRunnerConfiguration | undefined, runSource: TaskRunSource, custom: CustomTask[], customized: IStringDictionary, source: TaskConfig.TaskConfigSource, isRecentTask: boolean = false): Promise { - if (!config) { + private async _computeTasksForSingleConfig(workspaceFolder: IWorkspaceFolder | undefined, config: TaskConfig.IExternalTaskRunnerConfiguration | undefined, runSource: TaskRunSource, custom: CustomTask[], customized: IStringDictionary, source: TaskConfig.TaskConfigSource, isRecentTask: boolean = false): Promise { + if (!config || !workspaceFolder) { return false; } const taskSystemInfo: ITaskSystemInfo | undefined = workspaceFolder ? this._getTaskSystemInfo(workspaceFolder.uri.scheme) : undefined; @@ -2782,6 +2782,9 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer const identifier = this._getTaskIdentifier(arg); const type = arg && typeof arg !== 'string' && 'type' in arg ? arg.type : undefined; const task = arg && typeof arg !== 'string' && 'task' in arg ? arg.task : arg === 'string' ? arg : undefined; + if (!identifier && !task && !type) { + return this._doRunTaskCommand(); + } this._getGroupedTasks().then(async (grouped) => { const tasks = grouped.all(); const resolver = this._createResolver(grouped); @@ -2800,7 +2803,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } } const exactMatchTask = tasks.find(t => task && (t.getDefinition(true)?.configurationProperties?.identifier === task || t.configurationProperties?.identifier === task || t._label === task)); - const filteredTasks = tasks.filter(t => t._label.includes(task)); if (exactMatchTask) { const id = exactMatchTask.configurationProperties?.identifier || exactMatchTask.getDefinition(true)?.configurationProperties?.identifier; if (id) { @@ -2812,11 +2814,20 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } } } - } else if (filteredTasks?.length > 1) { - return this._doRunTaskCommand(tasks, type, task); - } else { - return this._doRunTaskCommand(); } + const atLeastOneMatch = tasks.some(t => { + if (task) { + if (t._label.includes(task)) { + if (!type || t.type === type) { + return true; + } + } + } else if (type && t.type === type) { + return true; + } + return false; + }); + return atLeastOneMatch ? this._doRunTaskCommand(tasks, type, task) : this._doRunTaskCommand(); }); } @@ -2953,7 +2964,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer // eat the error, it has already been surfaced to the user and we don't care about it here }); } - const chooseAndRunTask = (tasks: Task[]) => { this._showIgnoredFoldersMessage().then(() => { this._showQuickPick(tasks, @@ -2968,7 +2978,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return; } if (task === null) { - configure(); + configure.apply(this); return; } runSingleTask(task, { attachProblemMatcher: true }, this); diff --git a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts index 25f1e3c891d..a8b5100b47b 100644 --- a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts +++ b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts @@ -41,7 +41,7 @@ import { TerminalMenuBarGroup } from 'vs/workbench/contrib/terminal/browser/term import { isString } from 'vs/base/common/types'; const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(RunAutomaticTasks, 'RunAutomaticTasks', LifecyclePhase.Eventually); +workbenchRegistry.registerWorkbenchContribution(RunAutomaticTasks, LifecyclePhase.Eventually); registerAction2(ManageAutomaticTaskRunning); MenuRegistry.appendMenuItem(MenuId.CommandPalette, { @@ -161,7 +161,7 @@ export class TaskStatusBarContributions extends Disposable implements IWorkbench } } -workbenchRegistry.registerWorkbenchContribution(TaskStatusBarContributions, 'TaskStatusBarContributions', LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(TaskStatusBarContributions, LifecyclePhase.Restored); MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { group: TerminalMenuBarGroup.Run, diff --git a/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts b/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts index dc5551150c3..2fedd4e2d70 100644 --- a/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts +++ b/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts @@ -273,7 +273,6 @@ export class TaskQuickPick extends Disposable { firstLevelTask = await this._doPickerFirstLevel(picker, taskQuickPickEntries); } do { - if (Types.isString(firstLevelTask)) { if (name) { await this._doPickerFirstLevel(picker, (await this.getTopLevelEntries(defaultEntry)).entries); diff --git a/src/vs/workbench/contrib/tasks/browser/taskService.ts b/src/vs/workbench/contrib/tasks/browser/taskService.ts index 75fac4be22a..2107166b2ce 100644 --- a/src/vs/workbench/contrib/tasks/browser/taskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/taskService.ts @@ -9,7 +9,7 @@ import { ITaskSystem } from 'vs/workbench/contrib/tasks/common/taskSystem'; import { ExecutionEngine } from 'vs/workbench/contrib/tasks/common/tasks'; import { AbstractTaskService, IWorkspaceFolderConfigurationResult } from 'vs/workbench/contrib/tasks/browser/abstractTaskService'; import { ITaskFilter, ITaskService } from 'vs/workbench/contrib/tasks/common/taskService'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class TaskService extends AbstractTaskService { private static readonly ProcessTaskSystemSupportMessage = nls.localize('taskService.processTaskSystem', 'Process task system is not support in the web.'); @@ -44,4 +44,4 @@ export class TaskService extends AbstractTaskService { } } -registerSingleton(ITaskService, TaskService, true); +registerSingleton(ITaskService, TaskService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/tasks/browser/taskTerminalStatus.ts b/src/vs/workbench/contrib/tasks/browser/taskTerminalStatus.ts index 793e69845e9..c38cc7bcc09 100644 --- a/src/vs/workbench/contrib/tasks/browser/taskTerminalStatus.ts +++ b/src/vs/workbench/contrib/tasks/browser/taskTerminalStatus.ts @@ -48,17 +48,10 @@ export class TaskTerminalStatus extends Disposable { case TaskEventKind.Active: this.eventActive(event); break; case TaskEventKind.Inactive: this.eventInactive(event); break; case TaskEventKind.ProcessEnded: this.eventEnd(event); break; - case TaskEventKind.End: this._playEndSound(event.exitCode); break; } })); } - private _playEndSound(exitCode?: number): void { - //TODO: determine sound based on exit code - this._audioCueService.playAudioCue(AudioCue.taskEnded); - } - - addTerminal(task: Task, terminal: ITerminalInstance, problemMatcher: AbstractProblemCollector) { const status: ITerminalStatus = { id: TASK_TERMINAL_STATUS_ID, severity: Severity.Info }; terminal.statusList.add(status); @@ -93,6 +86,7 @@ export class TaskTerminalStatus extends Disposable { terminalData.taskRunEnded = true; terminalData.terminal.statusList.remove(terminalData.status); if ((event.exitCode === 0) && (terminalData.problemMatcher.numberOfMatches === 0)) { + this._audioCueService.playAudioCue(AudioCue.taskCompleted); if (terminalData.task.configurationProperties.isBackground) { for (const status of terminalData.terminal.statusList.statuses) { terminalData.terminal.statusList.remove(status); @@ -101,6 +95,7 @@ export class TaskTerminalStatus extends Disposable { terminalData.terminal.statusList.add(SUCCEEDED_TASK_STATUS); } } else if (event.exitCode || terminalData.problemMatcher.maxMarkerSeverity === MarkerSeverity.Error) { + this._audioCueService.playAudioCue(AudioCue.taskFailed); terminalData.terminal.statusList.add(FAILED_TASK_STATUS); } else if (terminalData.problemMatcher.maxMarkerSeverity === MarkerSeverity.Warning) { terminalData.terminal.statusList.add(WARNING_TASK_STATUS); @@ -116,8 +111,10 @@ export class TaskTerminalStatus extends Disposable { } terminalData.terminal.statusList.remove(terminalData.status); if (terminalData.problemMatcher.numberOfMatches === 0) { + this._audioCueService.playAudioCue(AudioCue.taskCompleted); terminalData.terminal.statusList.add(SUCCEEDED_INACTIVE_TASK_STATUS); } else if (terminalData.problemMatcher.maxMarkerSeverity === MarkerSeverity.Error) { + this._audioCueService.playAudioCue(AudioCue.taskFailed); terminalData.terminal.statusList.add(FAILED_INACTIVE_TASK_STATUS); } else if (terminalData.problemMatcher.maxMarkerSeverity === MarkerSeverity.Warning) { terminalData.terminal.statusList.add(WARNING_INACTIVE_TASK_STATUS); diff --git a/src/vs/workbench/contrib/tasks/electron-sandbox/taskService.ts b/src/vs/workbench/contrib/tasks/electron-sandbox/taskService.ts index 5ae81823bcf..76f88556c89 100644 --- a/src/vs/workbench/contrib/tasks/electron-sandbox/taskService.ts +++ b/src/vs/workbench/contrib/tasks/electron-sandbox/taskService.ts @@ -11,7 +11,7 @@ import { ExecutionEngine } from 'vs/workbench/contrib/tasks/common/tasks'; import * as TaskConfig from '../common/taskConfiguration'; import { AbstractTaskService } from 'vs/workbench/contrib/tasks/browser/abstractTaskService'; import { ITaskFilter, ITaskService } from 'vs/workbench/contrib/tasks/common/taskService'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { TerminalTaskSystem } from 'vs/workbench/contrib/tasks/browser/terminalTaskSystem'; import { IConfirmationResult, IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { TerminateResponseCode } from 'vs/base/common/processes'; @@ -227,4 +227,4 @@ export class TaskService extends AbstractTaskService { } } -registerSingleton(ITaskService, TaskService, true); +registerSingleton(ITaskService, TaskService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts index 21cd8e46f4e..ad17e2c08ac 100644 --- a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts +++ b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts @@ -234,4 +234,4 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TelemetryContribution, 'TelemetryContribution', LifecyclePhase.Restored); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TelemetryContribution, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh index e3ce0fa6771..eb7da181fe3 100755 --- a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh +++ b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh @@ -98,7 +98,7 @@ __vsc_update_prompt() { # means the user re-exported the PS1 so we should re-wrap it if [[ "$__vsc_custom_PS1" == "" || "$__vsc_custom_PS1" != "$PS1" ]]; then __vsc_original_PS1=$PS1 - __vsc_custom_PS1="\[$(__vsc_prompt_start)\]$PREFIX$__vsc_original_PS1\[$(__vsc_prompt_end)\]" + __vsc_custom_PS1="\[$(__vsc_prompt_start)\]$__vsc_original_PS1\[$(__vsc_prompt_end)\]" PS1="$__vsc_custom_PS1" fi if [[ "$__vsc_custom_PS2" == "" || "$__vsc_custom_PS2" != "$PS2" ]]; then @@ -176,22 +176,17 @@ fi __vsc_update_prompt __vsc_restore_exit_code() { - return $1 + return "$1" } __vsc_prompt_cmd_original() { __vsc_status="$?" + __vsc_restore_exit_code "${__vsc_status}" # Evaluate the original PROMPT_COMMAND similarly to how bash would normally # See https://unix.stackexchange.com/a/672843 for technique - if [[ ${#__vsc_original_prompt_command[@]} -gt 1 ]]; then - for cmd in "${__vsc_original_prompt_command[@]}"; do - __vsc_status="$?" - eval "${cmd:-}" - done - else - __vsc_restore_exit_code "${__vsc_status}" - eval "${__vsc_original_prompt_command:-}" - fi + for cmd in "${__vsc_original_prompt_command[@]}"; do + eval "${cmd:-}" + done __vsc_precmd } diff --git a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-login.zsh b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-login.zsh index a0e118f7adb..128bd648616 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-login.zsh +++ b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-login.zsh @@ -2,8 +2,8 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # --------------------------------------------------------------------------------------------- -if [[ $options[norcs] = off && -o "login" && -f $USER_ZDOTDIR/.zlogin ]]; then - VSCODE_ZDOTDIR=$ZDOTDIR - ZDOTDIR=$USER_ZDOTDIR - . $USER_ZDOTDIR/.zlogin + +ZDOTDIR=$USER_ZDOTDIR +if [[ $options[norcs] = off && -o "login" && -f $ZDOTDIR/.zlogin ]]; then + . $ZDOTDIR/.zlogin fi diff --git a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-rc.zsh b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-rc.zsh index 84d7ef55c8a..90612eb545b 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-rc.zsh +++ b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-rc.zsh @@ -84,14 +84,12 @@ if [[ -o NOUNSET ]]; then if [ -z "${RPROMPT-}" ]; then RPROMPT="" fi - if [ -z "${PREFIX-}" ]; then - PREFIX="" - fi fi __vsc_update_prompt() { __vsc_prior_prompt="$PS1" + __vsc_prior_prompt2="$PS2" __vsc_in_command_execution="" - PS1="%{$(__vsc_prompt_start)%}$PREFIX$PS1%{$(__vsc_prompt_end)%}" + PS1="%{$(__vsc_prompt_start)%}$PS1%{$(__vsc_prompt_end)%}" PS2="%{$(__vsc_continuation_start)%}$PS2%{$(__vsc_continuation_end)%}" if [ -n "$RPROMPT" ]; then __vsc_prior_rprompt="$RPROMPT" @@ -118,6 +116,7 @@ __vsc_precmd() { __vsc_preexec() { PS1="$__vsc_prior_prompt" + PS2="$__vsc_prior_prompt2" if [ -n "$RPROMPT" ]; then RPROMPT="$__vsc_prior_rprompt" fi diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index da532922828..237a81753bf 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -248,6 +248,10 @@ border-style: solid; } +.xterm-screen .xterm-decoration-container .xterm-decoration.quick-fix { + z-index: 7; +} + .monaco-workbench .part.sidebar > .title > .title-actions .switch-terminal { display: flex; align-items: center; diff --git a/src/vs/workbench/contrib/terminal/browser/remotePty.ts b/src/vs/workbench/contrib/terminal/browser/remotePty.ts index 7b7ac69688f..98d5ca5aa11 100644 --- a/src/vs/workbench/contrib/terminal/browser/remotePty.ts +++ b/src/vs/workbench/contrib/terminal/browser/remotePty.ts @@ -111,7 +111,7 @@ export class RemotePty extends Disposable implements ITerminalChildProcess { if (!this._remoteTerminalChannel.freePortKillProcess) { throw new Error('freePortKillProcess does not exist on the local pty service'); } - return this._remoteTerminalChannel.freePortKillProcess(this.id, port); + return this._remoteTerminalChannel.freePortKillProcess(port); } acknowledgeDataEvent(charCount: number): void { diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 8af479229ed..b8f232c2ee4 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -23,7 +23,7 @@ import { TERMINAL_VIEW_ID, TerminalCommandId, ITerminalProfileService } from 'vs import { registerColors } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { setupTerminalCommands } from 'vs/workbench/contrib/terminal/browser/terminalCommands'; import { TerminalService } from 'vs/workbench/contrib/terminal/browser/terminalService'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ITerminalEditorService, ITerminalGroupService, ITerminalInstanceService, ITerminalService, TerminalDataTransfers, terminalEditorId } from 'vs/workbench/contrib/terminal/browser/terminal'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; @@ -54,11 +54,11 @@ import { TerminalMainContribution } from 'vs/workbench/contrib/terminal/browser/ import { Schemas } from 'vs/base/common/network'; // Register services -registerSingleton(ITerminalService, TerminalService, true); -registerSingleton(ITerminalEditorService, TerminalEditorService, true); -registerSingleton(ITerminalGroupService, TerminalGroupService, true); -registerSingleton(ITerminalInstanceService, TerminalInstanceService, true); -registerSingleton(ITerminalProfileService, TerminalProfileService, true); +registerSingleton(ITerminalService, TerminalService, InstantiationType.Delayed); +registerSingleton(ITerminalEditorService, TerminalEditorService, InstantiationType.Delayed); +registerSingleton(ITerminalGroupService, TerminalGroupService, InstantiationType.Delayed); +registerSingleton(ITerminalInstanceService, TerminalInstanceService, InstantiationType.Delayed); +registerSingleton(ITerminalProfileService, TerminalProfileService, InstantiationType.Delayed); // Register quick accesses const quickAccessRegistry = (Registry.as(QuickAccessExtensions.Quickaccess)); @@ -77,8 +77,8 @@ CommandsRegistry.registerCommand({ id: quickAccessNavigatePreviousInTerminalPick // Register workbench contributions const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(TerminalMainContribution, 'TerminalMainContribution', LifecyclePhase.Restored); -workbenchRegistry.registerWorkbenchContribution(RemoteTerminalBackendContribution, 'RemoteTerminalBackendContribution', LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(TerminalMainContribution, LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(RemoteTerminalBackendContribution, LifecyclePhase.Restored); // Register configurations registerTerminalPlatformConfiguration(); diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index b3900ffd571..873b5c9bb30 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -2,7 +2,6 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - import { Orientation } from 'vs/base/browser/ui/splitview/splitview'; import { IAction } from 'vs/base/common/actions'; import { Event } from 'vs/base/common/event'; @@ -12,14 +11,14 @@ import { OperatingSystem } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IKeyMods } from 'vs/platform/quickinput/common/quickInput'; -import { IMarkProperties, ITerminalCapabilityStore, ITerminalCommand } from 'vs/platform/terminal/common/capabilities/capabilities'; +import { IMarkProperties, ITerminalCapabilityStore, ITerminalCommand, ITerminalOutputMatcher } from 'vs/platform/terminal/common/capabilities/capabilities'; import { IExtensionTerminalProfile, IReconnectionProperties, IShellIntegration, IShellLaunchConfig, ITerminalDimensions, ITerminalLaunchError, ITerminalProfile, ITerminalTabLayoutInfoById, TerminalExitReason, TerminalIcon, TerminalLocation, TerminalShellType, TerminalType, TitleEventSource, WaitOnExitValue } from 'vs/platform/terminal/common/terminal'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { IEditableData } from 'vs/workbench/common/views'; import { TerminalFindWidget } from 'vs/workbench/contrib/terminal/browser/terminalFindWidget'; import { ITerminalStatusList } from 'vs/workbench/contrib/terminal/browser/terminalStatusList'; -import { IContextualAction } from 'vs/workbench/contrib/terminal/browser/xterm/contextualActionAddon'; +import { ITerminalQuickFix } from 'vs/workbench/contrib/terminal/browser/xterm/quickFixAddon'; import { INavigationMode, IRegisterContributedProfileArgs, IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalBackend, ITerminalConfigHelper, ITerminalFont, ITerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/common/terminal'; import { EditorGroupColumn } from 'vs/workbench/services/editor/common/editorGroupColumn'; import { IMarker } from 'xterm'; @@ -242,7 +241,7 @@ export interface ITerminalEditorService extends ITerminalInstanceHost { detachActiveEditorInstance(): ITerminalInstance; detachInstance(instance: ITerminalInstance): void; splitInstance(instanceToSplit: ITerminalInstance, shellLaunchConfig?: IShellLaunchConfig): ITerminalInstance; - revealActiveEditor(preserveFocus?: boolean): void; + revealActiveEditor(preserveFocus?: boolean): Promise; resolveResource(instance: ITerminalInstance | URI): URI; reviveInput(deserializedInput: IDeserializedTerminalEditorInput): EditorInput; getInputFromResource(resource: URI): EditorInput; @@ -456,10 +455,11 @@ export interface ITerminalInstance { readonly initialCwd?: string; readonly os?: OperatingSystem; readonly capabilities: ITerminalCapabilityStore; + readonly usedShellIntegrationInjection: boolean; readonly statusList: ITerminalStatusList; - contextualActions: IContextualAction | undefined; + quickFix: ITerminalQuickFix | undefined; readonly findWidget: Lazy; @@ -586,10 +586,23 @@ export interface ITerminalInstance { onDidFocusFindWidget: Event; + /** + * The exit code or undefined when the terminal process hasn't yet exited or + * the process exit code could not be determined. Use {@link exitReason} to see + * why the process has exited. + */ readonly exitCode: number | undefined; + /** + * The reason the terminal process exited, this will be undefined if the process is still + * running. + */ readonly exitReason: TerminalExitReason | undefined; + /** + * Whether links in the terminal are ready, links aren't available until after the process is + * ready. + */ readonly areLinksReady: boolean; /** @@ -650,11 +663,21 @@ export interface ITerminalInstance { */ disableLayout: boolean; + /** + * Access to the navigation mode accessibility feature. + */ readonly navigationMode: INavigationMode | undefined; + /** + * The description of the terminal, this is typically displayed next to {@link title}. + */ description: string | undefined; + /** + * The remote-aware $HOME directory (or Windows equivalent) of the terminal. + */ userHome: string | undefined; + /** * Shows the environment information hover if the widget exists. */ @@ -778,6 +801,8 @@ export interface ITerminalInstance { */ sendPath(originalPath: string, addNewLine: boolean): Promise; + runCommand(command: string, addNewLine?: boolean): void; + /** * Takes a path and returns the properly escaped path to send to a given shell. On Windows, this * includes trying to prepare the path for WSL if needed. @@ -855,8 +880,14 @@ export interface ITerminalInstance { */ toggleSizeToContentWidth(): Promise; + /** + * Toggles escape sequence logging in the devtools console. + */ toggleEscapeSequenceLogging(): Promise; + /** + * Sets whether escape seqeunce logging is enabled in the devtools console. + */ setEscapeSequenceLogging(enable: boolean): void; /** @@ -912,34 +943,35 @@ export interface ITerminalInstance { openRecentLink(type: 'localFile' | 'url'): Promise; /** - * Registers contextual action listeners + * Registers quick fix providers */ - registerContextualActions(...options: ITerminalContextualActionOptions[]): void; + registerQuickFixProvider(...options: ITerminalQuickFixOptions[]): void; + /** + * Attempts to detect and kill the process listening on specified port. + */ freePortKillProcess(port: string): Promise; } -export interface ITerminalContextualActionOptions { - actionName: string | DynamicActionName; +export interface ITerminalQuickFixOptions { commandLineMatcher: string | RegExp; outputMatcher?: ITerminalOutputMatcher; - getActions: ContextualActionCallback; + getQuickFixes: TerminalQuickFixCallback; exitStatus?: boolean; } -export type ContextualMatchResult = { commandLineMatch: RegExpMatchArray; outputMatch?: RegExpMatchArray | null }; -export type DynamicActionName = (matchResult: ContextualMatchResult) => string; -export type ContextualActionCallback = (matchResult: ContextualMatchResult, command: ITerminalCommand) => ICommandAction[] | undefined; +export type TerminalQuickFixMatchResult = { commandLineMatch: RegExpMatchArray; outputMatch?: RegExpMatchArray | null }; +export type TerminalQuickFixAction = IAction | ITerminalQuickFixCommandAction | ITerminalQuickFixOpenerAction; +export type TerminalQuickFixCallback = (matchResult: TerminalQuickFixMatchResult, command: ITerminalCommand) => TerminalQuickFixAction[] | TerminalQuickFixAction | undefined; -export interface ICommandAction extends IAction { - commandToRunInTerminal?: string; - addNewLine?: boolean; +export interface ITerminalQuickFixCommandAction { + type: 'command'; + command: string; + // TODO: Should this depend on whether alt is held? + addNewLine: boolean; } - -export interface ITerminalOutputMatcher { - lineMatcher: string | RegExp; - anchor?: 'top' | 'bottom'; - offset?: number; - length?: number; +export interface ITerminalQuickFixOpenerAction { + type: 'opener'; + uri: URI; } export interface IXtermTerminal { @@ -956,6 +988,11 @@ export interface IXtermTerminal { readonly onDidChangeSelection: Event; + /** + * Gets a view of the current texture atlas used by the renderers. + */ + readonly textureAtlas: Promise | undefined; + /** * The position of the terminal. */ diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.web.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.web.contribution.ts index 6818669dbe7..ba94df376a0 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.web.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.web.contribution.ts @@ -6,11 +6,11 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { KeybindingWeight, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ITerminalProfileResolverService, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { BrowserTerminalProfileResolverService } from 'vs/workbench/contrib/terminal/browser/terminalProfileResolverService'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; -registerSingleton(ITerminalProfileResolverService, BrowserTerminalProfileResolverService, true); +registerSingleton(ITerminalProfileResolverService, BrowserTerminalProfileResolverService, InstantiationType.Delayed); // Register standard external terminal keybinding as integrated terminal when in web as the // external terminal is not available diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index f0128ece068..3dd0ba3046a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -52,9 +52,11 @@ import { ITerminalQuickPickItem } from 'vs/workbench/contrib/terminal/browser/te import { IThemeService } from 'vs/platform/theme/common/themeService'; import { getIconId, getColorClass, getUriClasses } from 'vs/workbench/contrib/terminal/browser/terminalIcon'; import { clearShellFileHistory, getCommandHistory } from 'vs/workbench/contrib/terminal/common/history'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; +import { IFileService } from 'vs/platform/files/common/files'; +import { VSBuffer } from 'vs/base/common/buffer'; export const switchTerminalActionViewItemSeparator = '\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500'; export const switchTerminalShowTabsTitle = localize('showTerminalTabs', "Show Tabs"); @@ -159,7 +161,7 @@ export function registerTerminalActions() { }); } async run(accessor: ServicesAccessor) { - accessor.get(ITerminalService).activeInstance?.contextualActions?.showQuickFixMenu(); + accessor.get(ITerminalService).activeInstance?.quickFix?.showMenu(); } }); @@ -342,9 +344,9 @@ export function registerTerminalActions() { if (instance) { await instance.runRecent('command'); if (instance?.target === TerminalLocation.Editor) { - terminalEditorService.revealActiveEditor(); + await terminalEditorService.revealActiveEditor(); } else { - terminalGroupService.showPanel(false); + await terminalGroupService.showPanel(false); } } } @@ -392,9 +394,9 @@ export function registerTerminalActions() { if (instance) { await instance.runRecent('cwd'); if (instance?.target === TerminalLocation.Editor) { - terminalEditorService.revealActiveEditor(); + await terminalEditorService.revealActiveEditor(); } else { - terminalGroupService.showPanel(false); + await terminalGroupService.showPanel(false); } } } @@ -596,9 +598,9 @@ export function registerTerminalActions() { } instance.sendText(text, true); if (instance.target === TerminalLocation.Editor) { - terminalEditorService.revealActiveEditor(); + await terminalEditorService.revealActiveEditor(); } else { - terminalGroupService.showPanel(); + await terminalGroupService.showPanel(); } } }); @@ -2226,13 +2228,50 @@ export function registerTerminalActions() { clearShellFileHistory(); } }); + registerAction2(class extends Action2 { + constructor() { + super({ + id: TerminalCommandId.ShowTextureAtlas, + title: { value: localize('workbench.action.terminal.showTextureAtlas', "Show Terminal Texture Atlas"), original: 'Show Terminal Texture Atlas' }, + f1: true, + category: Categories.Developer, + precondition: ContextKeyExpr.or(TerminalContextKeys.isOpen) + }); + } + async run(accessor: ServicesAccessor) { + const terminalService = accessor.get(ITerminalService); + const fileService = accessor.get(IFileService); + const openerService = accessor.get(IOpenerService); + const workspaceContextService = accessor.get(IWorkspaceContextService); + const bitmap = await terminalService.activeInstance?.xterm?.textureAtlas; + if (!bitmap) { + return; + } + const cwdUri = workspaceContextService.getWorkspace().folders[0].uri; + const fileUri = URI.joinPath(cwdUri, 'textureAtlas.png'); + const canvas = document.createElement('canvas'); + canvas.width = bitmap.width; + canvas.height = bitmap.height; + const ctx = canvas.getContext('bitmaprenderer'); + if (!ctx) { + return; + } + ctx.transferFromImageBitmap(bitmap); + const blob = await new Promise((res) => canvas.toBlob(res)); + if (!blob) { + return; + } + await fileService.writeFile(fileUri, VSBuffer.wrap(new Uint8Array(await blob.arrayBuffer()))); + openerService.open(fileUri); + } + }); registerAction2(class extends Action2 { constructor() { super({ id: TerminalCommandId.WriteDataToTerminal, title: { value: localize('workbench.action.terminal.writeDataToTerminal', "Write Data to Terminal"), original: 'Write Data to Terminal' }, f1: true, - category: CATEGORIES.Developer.value + category: Categories.Developer }); } async run(accessor: ServicesAccessor) { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalBaseContextualActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalBaseContextualActions.ts deleted file mode 100644 index 8e2344fe159..00000000000 --- a/src/vs/workbench/contrib/terminal/browser/terminalBaseContextualActions.ts +++ /dev/null @@ -1,117 +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 { IAction } from 'vs/base/common/actions'; -import { isWindows } from 'vs/base/common/platform'; -import { localize } from 'vs/nls'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { ContextualMatchResult, ICommandAction, ITerminalContextualActionOptions, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { ITerminalCommand } from 'vs/workbench/contrib/terminal/common/terminal'; - -export const GitCommandLineRegex = /git/; -export const GitPushCommandLineRegex = /git\s+push/; -export const AnyCommandLineRegex = /.{4,}/; -export const GitSimilarOutputRegex = /most similar command is\s+([^\s]{3,})/; -export const FreePortOutputRegex = /address already in use \d\.\d\.\d\.\d:(\d\d\d\d)\s+|Unable to bind [^ ]*:(\d+)|can't listen on port (\d+)|listen EADDRINUSE [^ ]*:(\d+)/; -export const GitPushOutputRegex = /git push --set-upstream origin ([^\s]+)\s+/; -export const GitCreatePrOutputRegex = /Create a pull request for \'([^\s]+)\' on GitHub by visiting:\s+remote:\s+(https:.+pull.+)\s+/; - -export function gitSimilarCommand(): ITerminalContextualActionOptions { - return { - commandLineMatcher: GitCommandLineRegex, - outputMatcher: { lineMatcher: GitSimilarOutputRegex, anchor: 'bottom' }, - actionName: (matchResult: ContextualMatchResult) => matchResult.outputMatch ? `Run git ${matchResult.outputMatch[1]}` : ``, - exitStatus: false, - getActions: (matchResult: ContextualMatchResult, command: ITerminalCommand) => { - const actions: ICommandAction[] = []; - const fixedCommand = matchResult?.outputMatch?.[1]; - if (!fixedCommand) { - return; - } - const label = localize("terminal.gitSimilarCommand", "Run git {0}", fixedCommand); - actions.push({ - class: undefined, tooltip: label, id: 'terminal.gitSimilarCommand', label, enabled: true, - commandToRunInTerminal: `git ${fixedCommand}`, - addNewLine: true, - run: () => { } - }); - return actions; - } - }; -} -export function freePort(terminalInstance?: Partial): ITerminalContextualActionOptions { - return { - actionName: (matchResult: ContextualMatchResult) => matchResult.outputMatch ? `Free port ${matchResult.outputMatch[1]}` : '', - commandLineMatcher: AnyCommandLineRegex, - outputMatcher: !isWindows ? { lineMatcher: FreePortOutputRegex, anchor: 'bottom' } : undefined, - getActions: (matchResult: ContextualMatchResult, command: ITerminalCommand) => { - const port = matchResult?.outputMatch?.[1]; - if (!port) { - return; - } - const actions: ICommandAction[] = []; - const label = localize("terminal.freePort", "Free port {0}", port); - actions.push({ - class: undefined, tooltip: label, id: 'terminal.freePort', label, enabled: true, - run: async () => { - await terminalInstance?.freePortKillProcess?.(port); - }, - commandToRunInTerminal: command.command, - addNewLine: false - }); - return actions; - } - }; -} -export function gitPushSetUpstream(): ITerminalContextualActionOptions { - return { - actionName: (matchResult: ContextualMatchResult) => matchResult.outputMatch ? `Git push ${matchResult.outputMatch[1]}` : '', - commandLineMatcher: GitPushCommandLineRegex, - outputMatcher: { lineMatcher: GitPushOutputRegex, anchor: 'bottom' }, - exitStatus: false, - getActions: (matchResult: ContextualMatchResult, command: ITerminalCommand) => { - const branch = matchResult?.outputMatch?.[1]; - if (!branch) { - return; - } - const actions: ICommandAction[] = []; - const label = localize("terminal.gitPush", "Git push {0}", branch); - command.command = `git push --set-upstream origin ${branch}`; - actions.push({ - class: undefined, tooltip: label, id: 'terminal.gitPush', label, enabled: true, - commandToRunInTerminal: command.command, - addNewLine: true, - run: () => { } - }); - return actions; - } - }; -} - -export function gitCreatePr(openerService: IOpenerService): ITerminalContextualActionOptions { - return { - actionName: (matchResult: ContextualMatchResult) => matchResult.outputMatch ? `Create PR for ${matchResult.outputMatch[1]}` : '', - commandLineMatcher: GitPushCommandLineRegex, - outputMatcher: { lineMatcher: GitCreatePrOutputRegex, anchor: 'bottom' }, - exitStatus: true, - getActions: (matchResult: ContextualMatchResult, command?: ITerminalCommand) => { - if (!command) { - return; - } - const branch = matchResult?.outputMatch?.[1]; - const link = matchResult?.outputMatch?.[2]; - if (!branch || !link) { - return; - } - const actions: IAction[] = []; - const label = localize("terminal.gitCreatePr", "Create PR"); - actions.push({ - class: undefined, tooltip: label, id: 'terminal.gitCreatePr', label, enabled: true, - run: () => openerService.open(link) - }); - return actions; - } - }; -} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalEditor.ts b/src/vs/workbench/contrib/terminal/browser/terminalEditor.ts index b71eabb4d8b..e429e11b712 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalEditor.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalEditor.ts @@ -15,7 +15,7 @@ import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IColorTheme, ICssStyleCollector, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { IEditorOpenContext } from 'vs/workbench/common/editor'; import { ITerminalEditorService, ITerminalService, terminalEditorId } from 'vs/workbench/contrib/terminal/browser/terminal'; @@ -29,6 +29,8 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { openContextMenu } from 'vs/workbench/contrib/terminal/browser/terminalContextMenu'; import { ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; +import { TERMINAL_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; +import { EDITOR_PANE_BACKGROUND } from 'vs/workbench/common/theme'; export class TerminalEditor extends EditorPane { @@ -220,3 +222,10 @@ export class TerminalEditor extends EditorPane { return defaultProfileName!; } } + +registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { + const backgroundColor = theme.getColor(TERMINAL_BACKGROUND_COLOR) || theme.getColor(EDITOR_PANE_BACKGROUND); + if (backgroundColor) { + collector.addRule(`.monaco-workbench .editor-instance .terminal-wrapper { background-color: ${backgroundColor.toString()}; }`); + } +}); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalEditorService.ts b/src/vs/workbench/contrib/terminal/browser/terminalEditorService.ts index c4b5c781128..dbd16927109 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalEditorService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalEditorService.ts @@ -10,6 +10,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c import { EditorActivation } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IShellLaunchConfig, TerminalLocation } from 'vs/platform/terminal/common/terminal'; +import { IEditorPane } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { IDeserializedTerminalEditorInput, ITerminalEditorService, ITerminalInstance, ITerminalInstanceService, TerminalEditorLocation } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalEditorInput } from 'vs/workbench/contrib/terminal/browser/terminalEditorInput'; @@ -26,6 +27,7 @@ export class TerminalEditorService extends Disposable implements ITerminalEditor instances: ITerminalInstance[] = []; private _activeInstanceIndex: number = -1; private _isShuttingDown = false; + private _activeOpenEditorRequest?: { instanceId: number; promise: Promise }; private _terminalEditorActive: IContextKey; @@ -140,16 +142,21 @@ export class TerminalEditorService extends Disposable implements ITerminalEditor async openEditor(instance: ITerminalInstance, editorOptions?: TerminalEditorLocation): Promise { const resource = this.resolveResource(instance); if (resource) { - await this._editorService.openEditor({ - resource, - description: instance.description || instance.shellLaunchConfig.type, - options: - { - pinned: true, - forceReload: true, - preserveFocus: editorOptions?.preserveFocus - } - }, editorOptions?.viewColumn || ACTIVE_GROUP); + await this._activeOpenEditorRequest?.promise; + this._activeOpenEditorRequest = { + instanceId: instance.instanceId, + promise: this._editorService.openEditor({ + resource, + description: instance.description || instance.shellLaunchConfig.type, + options: { + pinned: true, + forceReload: true, + preserveFocus: editorOptions?.preserveFocus + } + }, editorOptions?.viewColumn || ACTIVE_GROUP) + }; + await this._activeOpenEditorRequest?.promise; + this._activeOpenEditorRequest = undefined; } } @@ -173,9 +180,7 @@ export class TerminalEditorService extends Disposable implements ITerminalEditor this._editorService.openEditor(input, { pinned: true, forceReload: true - }, - input.group - ); + }, input.group); this._registerInstance(inputKey, input, instance); return instanceOrUri; }); @@ -232,13 +237,11 @@ export class TerminalEditorService extends Disposable implements ITerminalEditor this._editorService.openEditor({ resource: URI.revive(resource), description: instance.description, - options: - { + options: { pinned: true, forceReload: true } - }, - SIDE_GROUP); + }, SIDE_GROUP); } return instance; } @@ -294,12 +297,17 @@ export class TerminalEditorService extends Disposable implements ITerminalEditor this._onDidChangeInstances.fire(); } - revealActiveEditor(preserveFocus?: boolean): void { + async revealActiveEditor(preserveFocus?: boolean): Promise { const instance = this.activeInstance; if (!instance) { return; } + // If there is an active openEditor call for this instance it will be revealed by that + if (this._activeOpenEditorRequest?.instanceId === instance.instanceId) { + return; + } + const editorInput = this._editorInputs.get(instance.resource.path)!; this._editorService.openEditor( editorInput, @@ -308,8 +316,7 @@ export class TerminalEditorService extends Disposable implements ITerminalEditor forceReload: true, preserveFocus, activation: EditorActivation.PRESERVE - }, - editorInput.group + } ); } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts b/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts index 1e7b0333e87..9835ac1fbca 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts @@ -179,7 +179,9 @@ export class TerminalGroupService extends Disposable implements ITerminalGroupSe if (pane && !pane.isVisible()) { await this._viewsService.openView(TERMINAL_VIEW_ID, focus); } - await instance.focusWhenReady(true); + // Do not await the focus as setVisible must be called immediately to ensure + // something else didn't steal focus + instance.focusWhenReady(true); // HACK: as a workaround for https://github.com/microsoft/vscode/issues/134692, // this will trigger a forced refresh of the viewport to sync the viewport and scroll bar. // This can likely be removed after https://github.com/xtermjs/xterm.js/issues/291 is diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index c17ac12ef00..f1e4b3b2b3b 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -10,7 +10,7 @@ import * as dom from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { Orientation } from 'vs/base/browser/ui/sash/sash'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; -import { AutoOpenBarrier, Promises } from 'vs/base/common/async'; +import { AutoOpenBarrier, Promises, timeout } from 'vs/base/common/async'; import { Codicon } from 'vs/base/common/codicons'; import { debounce } from 'vs/base/common/decorators'; import { ErrorNoTelemetry } from 'vs/base/common/errors'; @@ -60,9 +60,9 @@ import { AudioCue, IAudioCueService } from 'vs/workbench/contrib/audioCues/brows import { TaskSettingId } from 'vs/workbench/contrib/tasks/common/tasks'; import { IDetectedLinks, TerminalLinkManager } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkManager'; import { TerminalLinkQuickpick } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkQuickpick'; -import { IRequestAddInstanceToGroupEvent, ITerminalContextualActionOptions, ITerminalExternalLinkProvider, ITerminalInstance, TerminalDataTransfers } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { IRequestAddInstanceToGroupEvent, ITerminalQuickFixOptions, ITerminalExternalLinkProvider, ITerminalInstance, TerminalDataTransfers } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalLaunchHelpAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; -import { gitSimilarCommand, gitCreatePr, gitPushSetUpstream, freePort } from 'vs/workbench/contrib/terminal/browser/terminalBaseContextualActions'; +import { gitSimilarCommand, gitCreatePr, gitPushSetUpstream, freePort, gitTwoDashes } from 'vs/workbench/contrib/terminal/browser/terminalQuickFixBuiltinActions'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { TerminalEditorInput } from 'vs/workbench/contrib/terminal/browser/terminalEditorInput'; import { TerminalFindWidget } from 'vs/workbench/contrib/terminal/browser/terminalFindWidget'; @@ -74,7 +74,7 @@ import { TypeAheadAddon } from 'vs/workbench/contrib/terminal/browser/terminalTy import { getTerminalResourcesFromDragEvent, getTerminalUri } from 'vs/workbench/contrib/terminal/browser/terminalUri'; import { EnvironmentVariableInfoWidget } from 'vs/workbench/contrib/terminal/browser/widgets/environmentVariableInfoWidget'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; -import { ContextualActionAddon, IContextualAction } from 'vs/workbench/contrib/terminal/browser/xterm/contextualActionAddon'; +import { ITerminalQuickFix, TerminalQuickFixAddon } from 'vs/workbench/contrib/terminal/browser/xterm/quickFixAddon'; import { LineDataEventAddon } from 'vs/workbench/contrib/terminal/browser/xterm/lineDataEventAddon'; import { NavigationModeAddon } from 'vs/workbench/contrib/terminal/browser/xterm/navigationModeAddon'; import { XtermTerminal } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal'; @@ -207,7 +207,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { private _target?: TerminalLocation | undefined; private _disableShellIntegrationReporting: boolean | undefined; private _usedShellIntegrationInjection: boolean = false; - private _contextualActionAddon: ContextualActionAddon | undefined; + get usedShellIntegrationInjection(): boolean { return this._usedShellIntegrationInjection; } + private _quickFixAddon: TerminalQuickFixAddon | undefined; readonly capabilities = new TerminalCapabilityStoreMultiplexer(); readonly statusList: ITerminalStatusList; @@ -216,7 +217,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { * Enables opening the contextual actions, if any, that are available * and registering of command finished listeners */ - get contextualActions(): IContextualAction | undefined { return this._contextualActionAddon; } + get quickFix(): ITerminalQuickFix | undefined { return this._quickFixAddon; } readonly findWidget: Lazy; @@ -610,9 +611,9 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { return undefined; } - registerContextualActions(...actionOptions: ITerminalContextualActionOptions[]): void { - for (const actionOption of actionOptions) { - this.contextualActions?.registerCommandFinishedListener(actionOption); + registerQuickFixProvider(...options: ITerminalQuickFixOptions[]): void { + for (const actionOption of options) { + this.quickFix?.registerCommandFinishedListener(actionOption); } } @@ -728,10 +729,10 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { const xterm = this._scopedInstantiationService.createInstance(XtermTerminal, Terminal, this._configHelper, this._cols, this._rows, this.target || TerminalLocation.Panel, this.capabilities, this.disableShellIntegrationReporting); this.xterm = xterm; - this._contextualActionAddon = this._scopedInstantiationService.createInstance(ContextualActionAddon, this.capabilities); - this.xterm?.raw.loadAddon(this._contextualActionAddon); - this.registerContextualActions(gitSimilarCommand(), gitCreatePr(this._openerService), gitPushSetUpstream(), freePort(this)); - this._register(this._contextualActionAddon.onDidRequestRerunCommand((e) => this.sendText(e.command, e.addNewLine || false))); + this._quickFixAddon = this._scopedInstantiationService.createInstance(TerminalQuickFixAddon, this.capabilities); + this.xterm?.raw.loadAddon(this._quickFixAddon); + this.registerQuickFixProvider(gitSimilarCommand(), gitTwoDashes(), gitCreatePr(), gitPushSetUpstream(), freePort(this)); + this._register(this._quickFixAddon.onDidRequestRerunCommand(async (e) => await this.runCommand(e.command, e.addNewLine || false))); const lineDataEventAddon = new LineDataEventAddon(); this.xterm.raw.loadAddon(lineDataEventAddon); this.updateAccessibilitySupport(); @@ -820,6 +821,21 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { return xterm; } + async runCommand(commandLine: string, addNewLine: boolean): Promise { + // Determine whether to send ETX (ctrl+c) before running the command. This should always + // happen unless command detection can reliably say that a command is being entered and + // there is no content in the prompt + if (this.capabilities.get(TerminalCapability.CommandDetection)?.hasInput !== false) { + await this.sendText('\x03', false); + // Wait a little before running the command to avoid the sequences being echoed while the ^C + // is being evaluated + await timeout(100); + } + // Use bracketed paste mode only when not running the command + await this.sendText(commandLine, addNewLine, !addNewLine); + } + + private _loadTypeAheadAddon(xterm: XtermTerminal): void { const enabled = this._configHelper.config.localEchoEnabled; const isRemote = !!this.remoteAuthority; @@ -1193,6 +1209,10 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } override dispose(reason?: TerminalExitReason): void { + if (this._isDisposed) { + return; + } + this._isDisposed = true; this._logService.trace(`terminalInstance#dispose (instanceId: ${this.instanceId})`); dispose(this._linkManager); this._linkManager = undefined; @@ -1209,7 +1229,13 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._horizontalScrollbar.dispose(); this._horizontalScrollbar = undefined; } - this.xterm?.dispose(); + + try { + this.xterm?.dispose(); + } catch (err: unknown) { + // See https://github.com/microsoft/vscode/issues/153486 + this._logService.error('Exception occurred during xterm disposal', err); + } // HACK: Workaround for Firefox bug https://bugzilla.mozilla.org/show_bug.cgi?id=559561, // as 'blur' event in xterm.raw.textarea is not triggered on xterm.dispose() @@ -1234,10 +1260,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // hasn't happened yet this._onProcessExit(undefined); - if (!this._isDisposed) { - this._isDisposed = true; - this._onDisposed.fire(this); - } + this._onDisposed.fire(this); + super.dispose(); } @@ -1709,6 +1733,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // Print initialText if specified if (shell.initialText) { + this._shellLaunchConfig.initialText = shell.initialText; await new Promise(r => this._writeInitialText(xterm, r)); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts index 66b8abe772a..0ee01518589 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ITerminalInstance, ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Disposable } from 'vs/base/common/lifecycle'; import { IShellLaunchConfig, ITerminalProfile, TerminalLocation } from 'vs/platform/terminal/common/terminal'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -90,4 +90,4 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst } } -registerSingleton(ITerminalInstanceService, TerminalInstanceService, true); +registerSingleton(ITerminalInstanceService, TerminalInstanceService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index 08da73cc57b..4eb15699743 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -178,10 +178,10 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce try { if (this._process?.freePortKillProcess) { const result = await this._process?.freePortKillProcess(port); - this._notificationService.notify({ message: `Killed process w ID: ${result.processId} to free port ${result.port}`, severity: Severity.Info }); + this._notificationService.notify({ message: localize('killportsuccess', 'Killed process with PID {0} listening on port {1}', result.processId, result.port), severity: Severity.Info }); } } catch (e) { - this._notificationService.notify({ message: `Could not kill process for port ${port} wth error ${e}`, severity: Severity.Warning }); + this._notificationService.notify({ message: localize('killportfailure', 'Could not kill process listening on port {0}, command exited with error {1}', port, e), severity: Severity.Warning }); } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalQuickFixBuiltinActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalQuickFixBuiltinActions.ts new file mode 100644 index 00000000000..04fc6eb724a --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/terminalQuickFixBuiltinActions.ts @@ -0,0 +1,150 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import { TerminalQuickFixMatchResult, ITerminalQuickFixOptions, ITerminalInstance, TerminalQuickFixAction } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalCommand } from 'vs/workbench/contrib/terminal/common/terminal'; +import { URI } from 'vs/base/common/uri'; + +export const GitCommandLineRegex = /git/; +export const GitPushCommandLineRegex = /git\s+push/; +export const GitTwoDashesRegex = /error: did you mean `--(.+)` \(with two dashes\)\?/; +export const AnyCommandLineRegex = /.+/; +export const GitSimilarOutputRegex = /(?:(most similar (command|commands) (is|are)))((\n\s*[^\s]+)+)/m; +export const FreePortOutputRegex = /address already in use (0\.0\.0\.0|127\.0\.0\.1|localhost|::):(?\d{4,5})|Unable to bind [^ ]*:(\d{4,5})|can't listen on port (\d{4,5})|listen EADDRINUSE [^ ]*:(\d{4,5})/; +export const GitPushOutputRegex = /git push --set-upstream origin ([^\s]+)/; +// The previous line starts with "Create a pull request for \'([^\s]+)\' on GitHub by visiting:\s*" +// it's safe to assume it's a github pull request if the URL includes `/pull/` +export const GitCreatePrOutputRegex = /remote:\s*(https:\/\/github\.com\/.+\/.+\/pull\/new\/.+)/; + +export function gitSimilarCommand(): ITerminalQuickFixOptions { + return { + commandLineMatcher: GitCommandLineRegex, + outputMatcher: { + lineMatcher: GitSimilarOutputRegex, + anchor: 'bottom', + offset: 0, + length: 10 + }, + exitStatus: false, + getQuickFixes: (matchResult: TerminalQuickFixMatchResult, command: ITerminalCommand) => { + if (!matchResult?.outputMatch) { + return; + } + const actions: TerminalQuickFixAction[] = []; + const results = matchResult.outputMatch[0].split('\n').map(r => r.trim()); + for (let i = 1; i < results.length; i++) { + const fixedCommand = results[i]; + if (fixedCommand) { + actions.push({ + type: 'command', + command: command.command.replace(/git\s+[^\s]+/, `git ${fixedCommand}`), + addNewLine: true + }); + } + } + return actions; + } + }; +} +export function gitTwoDashes(): ITerminalQuickFixOptions { + return { + commandLineMatcher: GitCommandLineRegex, + outputMatcher: { + lineMatcher: GitTwoDashesRegex, + anchor: 'bottom', + offset: 0, + length: 2 + }, + exitStatus: false, + getQuickFixes: (matchResult: TerminalQuickFixMatchResult, command: ITerminalCommand) => { + const problemArg = matchResult?.outputMatch?.[1]; + if (!problemArg) { + return; + } + return { + type: 'command', + command: command.command.replace(` -${problemArg}`, ` --${problemArg}`), + addNewLine: true + }; + } + }; +} +export function freePort(terminalInstance?: Partial): ITerminalQuickFixOptions { + return { + commandLineMatcher: AnyCommandLineRegex, + outputMatcher: { + lineMatcher: FreePortOutputRegex, + anchor: 'bottom', + offset: 0, + length: 30 + }, + exitStatus: false, + getQuickFixes: (matchResult: TerminalQuickFixMatchResult, command: ITerminalCommand) => { + const port = matchResult?.outputMatch?.groups?.portNumber; + if (!port) { + return; + } + const label = localize("terminal.freePort", "Free port {0}", port); + return { + class: undefined, + tooltip: label, + id: 'terminal.freePort', + label, + enabled: true, + run: async () => terminalInstance?.freePortKillProcess?.(port) + }; + } + }; +} +export function gitPushSetUpstream(): ITerminalQuickFixOptions { + return { + commandLineMatcher: GitPushCommandLineRegex, + outputMatcher: { + lineMatcher: GitPushOutputRegex, + anchor: 'bottom', + offset: 0, + length: 5 + }, + exitStatus: false, + getQuickFixes: (matchResult: TerminalQuickFixMatchResult, command: ITerminalCommand) => { + const branch = matchResult?.outputMatch?.[1]; + if (!branch) { + return; + } + return { + type: 'command', + command: `git push --set-upstream origin ${branch}`, + addNewLine: true + }; + } + }; +} + +export function gitCreatePr(): ITerminalQuickFixOptions { + return { + commandLineMatcher: GitPushCommandLineRegex, + outputMatcher: { + lineMatcher: GitCreatePrOutputRegex, + anchor: 'bottom', + offset: 0, + length: 5 + }, + exitStatus: true, + getQuickFixes: (matchResult: TerminalQuickFixMatchResult, command?: ITerminalCommand) => { + if (!command) { + return; + } + const link = matchResult?.outputMatch?.[1]; + if (!link) { + return; + } + return { + type: 'opener', + uri: URI.parse(link) + }; + } + }; +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalRunRecentQuickPick.ts b/src/vs/workbench/contrib/terminal/browser/terminalRunRecentQuickPick.ts index 207ea0f8ba3..e56f75730b1 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalRunRecentQuickPick.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalRunRecentQuickPick.ts @@ -26,7 +26,6 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { showWithPinnedItems } from 'vs/platform/quickinput/browser/quickPickPin'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { timeout } from 'vs/base/common/async'; export async function showRunRecentQuickPick( accessor: ServicesAccessor, @@ -266,7 +265,7 @@ export async function showRunRecentQuickPick( text = result.rawLabel; } quickPick.hide(); - runCommand(instance, text, !quickPick.keyMods.alt); + instance.runCommand(text, !quickPick.keyMods.alt); if (quickPick.keyMods.alt) { instance.focus(); } @@ -284,20 +283,6 @@ export async function showRunRecentQuickPick( }); } -async function runCommand(instance: ITerminalInstance, commandLine: string, addNewLine: boolean): Promise { - // Determine whether to send ETX (ctrl+c) before running the command. This should always - // happen unless command detection can reliably say that a command is being entered and - // there is no content in the prompt - if (instance.capabilities.get(TerminalCapability.CommandDetection)?.hasInput !== false) { - await instance.sendText('\x03', false); - // Wait a little before running the command to avoid the sequences being echoed while the ^C - // is being evaluated - await timeout(100); - } - // Use bracketed paste mode only when not running the command - await instance.sendText(commandLine, addNewLine, !addNewLine); -} - class TerminalOutputProvider implements ITextModelContentProvider { static scheme = 'TERMINAL_OUTPUT'; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index d4a0e680568..8d3c016c4ae 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -973,7 +973,7 @@ export class TerminalService implements ITerminalService { const splitActiveTerminal = typeof options?.location === 'object' && 'splitActiveTerminal' in options.location ? options.location.splitActiveTerminal : typeof options?.location === 'object' ? 'parentTerminal' in options.location : false; - this._resolveCwd(shellLaunchConfig, splitActiveTerminal, options); + await this._resolveCwd(shellLaunchConfig, splitActiveTerminal, options); // Launch the contributed profile if (contributedProfile) { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts index 2b1b063de2d..82d9b1747e2 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts @@ -261,7 +261,8 @@ class TerminalTabsRenderer implements IListRenderer { e.stopImmediatePropagation(); @@ -456,16 +453,14 @@ class TerminalTabsRenderer implements IListRenderer { if (!targetInstance) { this._terminalGroupService.moveGroupToEnd(sourceInstances[0]); + this._terminalService.setActiveInstance(sourceInstances[0]); return; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTooltip.ts b/src/vs/workbench/contrib/terminal/browser/terminalTooltip.ts index 4b7b97e5d06..8c1434a7981 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTooltip.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTooltip.ts @@ -22,7 +22,9 @@ export function getShellIntegrationTooltip(instance: ITerminalInstance, markdown if (instance.shellLaunchConfig.ignoreShellIntegration) { shellIntegrationString += `${markdown ? '\n\n---\n\n' : '\n\n'} ${localize('launchFailed.exitCodeOnlyShellIntegration', "The terminal process failed to launch. Disabling shell integration with terminal.integrated.shellIntegration.enabled might help.")}`; } else { - shellIntegrationString += `${markdown ? '\n\n---\n\n' : '\n\n'} ${localize('shellIntegration.activationFailed', "Shell integration failed to activate")}`; + if (instance.usedShellIntegrationInjection) { + shellIntegrationString += `${markdown ? '\n\n---\n\n' : '\n\n'} ${localize('shellIntegration.activationFailed', "Shell integration failed to activate")}`; + } } } return shellIntegrationString; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 966f327339c..89e3251e380 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -301,11 +301,15 @@ export class TerminalViewPane extends ViewPane { registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { const panelBackgroundColor = theme.getColor(TERMINAL_BACKGROUND_COLOR) || theme.getColor(PANEL_BACKGROUND); - collector.addRule(`.monaco-workbench .part.panel .pane-body.integrated-terminal .terminal-outer-container { background-color: ${panelBackgroundColor ? panelBackgroundColor.toString() : ''}; }`); + if (panelBackgroundColor) { + collector.addRule(`.monaco-workbench .part.panel .pane-body.integrated-terminal .terminal-outer-container { background-color: ${panelBackgroundColor.toString()}; }`); + } const sidebarBackgroundColor = theme.getColor(TERMINAL_BACKGROUND_COLOR) || theme.getColor(SIDE_BAR_BACKGROUND); - collector.addRule(`.monaco-workbench .part.sidebar .pane-body.integrated-terminal .terminal-outer-container { background-color: ${sidebarBackgroundColor ? sidebarBackgroundColor.toString() : ''}; }`); - collector.addRule(`.monaco-workbench .part.auxiliarybar .pane-body.integrated-terminal .terminal-outer-container { background-color: ${sidebarBackgroundColor ? sidebarBackgroundColor.toString() : ''}; }`); + if (sidebarBackgroundColor) { + collector.addRule(`.monaco-workbench .part.sidebar .pane-body.integrated-terminal .terminal-outer-container { background-color: ${sidebarBackgroundColor.toString()}; }`); + collector.addRule(`.monaco-workbench .part.auxiliarybar .pane-body.integrated-terminal .terminal-outer-container { background-color: ${sidebarBackgroundColor.toString()}; }`); + } const borderColor = theme.getColor(TERMINAL_BORDER_COLOR); if (borderColor) { diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/contextualActionAddon.ts b/src/vs/workbench/contrib/terminal/browser/xterm/contextualActionAddon.ts deleted file mode 100644 index f5c38ee5ff1..00000000000 --- a/src/vs/workbench/contrib/terminal/browser/xterm/contextualActionAddon.ts +++ /dev/null @@ -1,167 +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 { Emitter, Event } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { ITerminalCapabilityStore, ITerminalCommand, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; -// Importing types is safe in any layer -// eslint-disable-next-line local/code-import-patterns -import type { ITerminalAddon } from 'xterm-headless'; -import * as dom from 'vs/base/browser/dom'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { ICommandAction, ITerminalContextualActionOptions } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { DecorationSelector, updateLayout } from 'vs/workbench/contrib/terminal/browser/xterm/decorationStyles'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { Terminal } from 'xterm'; -import { IAction } from 'vs/base/common/actions'; - -export interface IContextualAction { - /** - * Shows the quick fix menu - */ - showQuickFixMenu(): void; - - /** - * Registers a listener on onCommandFinished scoped to a particular command or regular - * expression and provides a callback to be executed for commands that match. - */ - registerCommandFinishedListener(options: ITerminalContextualActionOptions): void; -} - -export interface IContextualActionAdddon extends IContextualAction { - onDidRequestRerunCommand: Event<{ command: string; addNewLine?: boolean }>; -} - -export class ContextualActionAddon extends Disposable implements ITerminalAddon, IContextualActionAdddon { - private readonly _onDidRequestRerunCommand = new Emitter<{ command: string; addNewLine?: boolean }>(); - readonly onDidRequestRerunCommand = this._onDidRequestRerunCommand.event; - - private _terminal: Terminal | undefined; - - private _currentQuickFixElement: HTMLElement | undefined; - - private _decorationMarkerIds = new Set(); - - private _commandListeners: Map = new Map(); - - private _matchActions: ICommandAction[] | undefined; - - constructor(private readonly _capabilities: ITerminalCapabilityStore, - @IContextMenuService private readonly _contextMenuService: IContextMenuService, - @IConfigurationService private readonly _configurationService: IConfigurationService) { - super(); - const commandDetectionCapability = this._capabilities.get(TerminalCapability.CommandDetection); - if (commandDetectionCapability) { - this._registerCommandFinishedHandler(); - } else { - this._capabilities.onDidAddCapability(c => { - if (c === TerminalCapability.CommandDetection) { - this._registerCommandFinishedHandler(); - } - }); - } - } - activate(terminal: Terminal): void { - this._terminal = terminal; - } - - showQuickFixMenu(): void { - this._currentQuickFixElement?.click(); - } - - registerCommandFinishedListener(options: ITerminalContextualActionOptions): void { - const matcherKey = options.commandLineMatcher.toString(); - const currentOptions = this._commandListeners.get(matcherKey) || []; - currentOptions.push(options); - this._commandListeners.set(matcherKey, currentOptions); - } - - private _registerCommandFinishedHandler(): void { - const terminal = this._terminal; - const commandDetection = this._capabilities.get(TerminalCapability.CommandDetection); - if (!terminal || !commandDetection) { - return; - } - this._register(commandDetection.onCommandFinished(async command => { - this._matchActions = getMatchActions(command, this._commandListeners, this._onDidRequestRerunCommand); - })); - // The buffer is not ready by the time command finish - // is called. Add the decoration on command start using the actions, if any, - // from the last command - this._register(commandDetection.onCommandStarted(() => { - if (this._matchActions) { - this._registerContextualDecoration(); - this._matchActions = undefined; - } - })); - } - - private _registerContextualDecoration(): void { - if (!this._terminal) { - return; - } - const marker = this._terminal.registerMarker(); - if (!marker) { - return; - } - const actions = this._matchActions; - const decoration = this._terminal.registerDecoration({ marker, layer: 'top' }); - decoration?.onRender((e: HTMLElement) => { - if (!this._decorationMarkerIds.has(decoration.marker.id)) { - this._currentQuickFixElement = e; - e.classList.add(DecorationSelector.QuickFix, DecorationSelector.Codicon, DecorationSelector.CommandDecoration, DecorationSelector.XtermDecoration); - e.style.color = '#ffcc00'; - updateLayout(this._configurationService, e); - if (actions) { - this._decorationMarkerIds.add(decoration.marker.id); - dom.addDisposableListener(e, dom.EventType.CLICK, () => { - this._contextMenuService.showContextMenu({ getAnchor: () => e, getActions: () => actions }); - this._contextMenuService.onDidHideContextMenu(() => decoration.dispose()); - }); - } - } - }); - } -} - -export function getMatchActions(command: ITerminalCommand, actionOptions: Map, onDidRequestRerunCommand?: Emitter<{ command: string; addNewLine?: boolean }>): IAction[] | undefined { - const matchActions: IAction[] = []; - const newCommand = command.command; - for (const options of actionOptions.values()) { - for (const actionOption of options) { - if (actionOption.exitStatus !== undefined && actionOption.exitStatus !== (command.exitCode === 0)) { - continue; - } - const commandLineMatch = newCommand.match(actionOption.commandLineMatcher); - if (!commandLineMatch) { - continue; - } - const outputMatcher = actionOption.outputMatcher; - let outputMatch; - if (outputMatcher) { - outputMatch = command.getOutputMatch(outputMatcher); - } - const actions = actionOption.getActions({ commandLineMatch, outputMatch }, command); - if (!actions) { - return matchActions.length === 0 ? undefined : matchActions; - } - for (const a of actions) { - matchActions.push({ - id: a.id, - label: a.label, - class: a.class, - enabled: a.enabled, - run: async () => { - await a.run(); - if (a.commandToRunInTerminal) { - onDidRequestRerunCommand?.fire({ command: a.commandToRunInTerminal, addNewLine: a.addNewLine }); - } - }, - tooltip: a.tooltip - }); - } - } - } - return matchActions.length === 0 ? undefined : matchActions; -} diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts b/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts index e520738108a..3cf8a961eab 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts @@ -3,44 +3,40 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; -import { ITerminalCommand } from 'vs/workbench/contrib/terminal/common/terminal'; -import { IDecoration, ITerminalAddon, Terminal } from 'xterm'; import * as dom from 'vs/base/browser/dom'; -import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { CommandInvalidationReason, ICommandDetectionCapability, IMarkProperties, ITerminalCapabilityStore, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; -import { IColorTheme, ICssStyleCollector, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; import { IAction, Separator } from 'vs/base/common/actions'; -import { Emitter } from 'vs/base/common/event'; -import { MarkdownString } from 'vs/base/common/htmlContent'; -import { localize } from 'vs/nls'; -import { Delayer } from 'vs/base/common/async'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { fromNow } from 'vs/base/common/date'; -import { toolbarHoverBackground } from 'vs/platform/theme/common/colorRegistry'; -import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; -import { TERMINAL_COMMAND_DECORATION_DEFAULT_BACKGROUND_COLOR, TERMINAL_COMMAND_DECORATION_ERROR_BACKGROUND_COLOR, TERMINAL_COMMAND_DECORATION_SUCCESS_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { Color } from 'vs/base/common/color'; +import { Emitter } from 'vs/base/common/event'; +import { Disposable, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { localize } from 'vs/nls'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; -import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { terminalDecorationError, terminalDecorationIncomplete, terminalDecorationMark, terminalDecorationSuccess } from 'vs/workbench/contrib/terminal/browser/terminalIcons'; +import { CommandInvalidationReason, ICommandDetectionCapability, IMarkProperties, ITerminalCapabilityStore, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; +import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; +import { toolbarHoverBackground } from 'vs/platform/theme/common/colorRegistry'; +import { IColorTheme, ICssStyleCollector, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { TaskSettingId } from 'vs/workbench/contrib/tasks/common/tasks'; -import { DecorationSelector, updateLayout } from 'vs/workbench/contrib/terminal/browser/xterm/decorationStyles'; +import { terminalDecorationError, terminalDecorationIncomplete, terminalDecorationMark, terminalDecorationSuccess } from 'vs/workbench/contrib/terminal/browser/terminalIcons'; +import { DecorationSelector, TerminalDecorationHoverManager, updateLayout } from 'vs/workbench/contrib/terminal/browser/xterm/decorationStyles'; +import { ITerminalCommand } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TERMINAL_COMMAND_DECORATION_DEFAULT_BACKGROUND_COLOR, TERMINAL_COMMAND_DECORATION_ERROR_BACKGROUND_COLOR, TERMINAL_COMMAND_DECORATION_SUCCESS_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { IDecoration, ITerminalAddon, Terminal } from 'xterm'; interface IDisposableDecoration { decoration: IDecoration; disposables: IDisposable[]; exitCode?: number; markProperties?: IMarkProperties } export class DecorationAddon extends Disposable implements ITerminalAddon { protected _terminal: Terminal | undefined; - private _hoverDelayer: Delayer; private _capabilityDisposables: Map = new Map(); - private _contextMenuVisible: boolean = false; private _decorations: Map = new Map(); private _placeholderDecoration: IDecoration | undefined; private _showGutterDecorations?: boolean; private _showOverviewRulerDecorations?: boolean; + private _terminalDecorationHoverService: TerminalDecorationHoverManager; private readonly _onDidRequestRunCommand = this._register(new Emitter<{ command: ITerminalCommand; copyAsHtml?: boolean }>()); readonly onDidRequestRunCommand = this._onDidRequestRunCommand.event; @@ -49,19 +45,15 @@ export class DecorationAddon extends Disposable implements ITerminalAddon { private readonly _capabilities: ITerminalCapabilityStore, @IClipboardService private readonly _clipboardService: IClipboardService, @IContextMenuService private readonly _contextMenuService: IContextMenuService, - @IHoverService private readonly _hoverService: IHoverService, @IConfigurationService private readonly _configurationService: IConfigurationService, @IThemeService private readonly _themeService: IThemeService, @IOpenerService private readonly _openerService: IOpenerService, @IQuickInputService private readonly _quickInputService: IQuickInputService, - @ILifecycleService lifecycleService: ILifecycleService + @ILifecycleService lifecycleService: ILifecycleService, + @IInstantiationService instantiationService: IInstantiationService ) { super(); this._register(toDisposable(() => this._dispose())); - this._register(this._contextMenuService.onDidShowContextMenu(() => this._contextMenuVisible = true)); - this._register(this._contextMenuService.onDidHideContextMenu(() => this._contextMenuVisible = false)); - this._hoverDelayer = this._register(new Delayer(this._configurationService.getValue('workbench.hover.delay'))); - this._register(this._configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(TerminalSettingId.FontSize) || e.affectsConfiguration(TerminalSettingId.LineHeight)) { this.refreshLayouts(); @@ -79,6 +71,7 @@ export class DecorationAddon extends Disposable implements ITerminalAddon { this._register(this._capabilities.onDidAddCapability(c => this._createCapabilityDisposables(c))); this._register(this._capabilities.onDidRemoveCapability(c => this._removeCapabilityDisposables(c))); this._register(lifecycleService.onWillShutdown(() => this._disposeAllDecorations())); + this._terminalDecorationHoverService = instantiationService.createInstance(TerminalDecorationHoverManager); } private _removeCapabilityDisposables(c: TerminalCapability): void { @@ -184,6 +177,7 @@ export class DecorationAddon extends Disposable implements ITerminalAddon { } private _dispose(): void { + this._terminalDecorationHoverService.dispose(); for (const disposable of this._capabilityDisposables.values()) { dispose(disposable); } @@ -309,9 +303,9 @@ export class DecorationAddon extends Disposable implements ITerminalAddon { if (command?.exitCode === undefined && !command?.markProperties) { return []; } else if (command?.markProperties || markProperties) { - return [...this._createHover(element, command || markProperties?.hoverMessage)]; + return [this._terminalDecorationHoverService.createHover(element, command || markProperties, markProperties?.hoverMessage)]; } - return [this._createContextMenu(element, command), ...this._createHover(element, command)]; + return [this._createContextMenu(element, command), this._terminalDecorationHoverService.createHover(element, command)]; } private _updateClasses(element?: HTMLElement, exitCode?: number, markProperties?: IMarkProperties): void { @@ -348,49 +342,12 @@ export class DecorationAddon extends Disposable implements ITerminalAddon { // When the xterm Decoration gets disposed of, its element gets removed from the dom // along with its listeners return dom.addDisposableListener(element, dom.EventType.CLICK, async () => { - this._hideHover(); + this._terminalDecorationHoverService.hideHover(); const actions = await this._getCommandActions(command); this._contextMenuService.showContextMenu({ getAnchor: () => element, getActions: () => actions }); }); } - private _createHover(element: HTMLElement, command: ITerminalCommand, markProperties?: IMarkProperties): IDisposable[] { - return [ - dom.addDisposableListener(element, dom.EventType.MOUSE_ENTER, () => { - if (this._contextMenuVisible) { - return; - } - this._hoverDelayer.trigger(() => { - let hoverContent = `${localize('terminalPromptContextMenu', "Show Command Actions")}`; - hoverContent += '\n\n---\n\n'; - if (command.markProperties || markProperties) { - if (command.markProperties?.hoverMessage || markProperties?.hoverMessage) { - hoverContent = command.markProperties?.hoverMessage || markProperties?.hoverMessage || ''; - } else { - return; - } - } else if (command.exitCode) { - if (command.exitCode === -1) { - hoverContent += localize('terminalPromptCommandFailed', 'Command executed {0} and failed', fromNow(command.timestamp, true)); - } else { - hoverContent += localize('terminalPromptCommandFailedWithExitCode', 'Command executed {0} and failed (Exit Code {1})', fromNow(command.timestamp, true), command.exitCode); - } - } else { - hoverContent += localize('terminalPromptCommandSuccess', 'Command executed {0}', fromNow(command.timestamp, true)); - } - this._hoverService.showHover({ content: new MarkdownString(hoverContent), target: element }); - }); - }), - dom.addDisposableListener(element, dom.EventType.MOUSE_LEAVE, () => this._hideHover()), - dom.addDisposableListener(element, dom.EventType.MOUSE_OUT, () => this._hideHover()) - ]; - } - - private _hideHover() { - this._hoverDelayer.cancel(); - this._hoverService.hideHover(); - } - private async _getCommandActions(command: ITerminalCommand): Promise { const actions: IAction[] = []; if (command.command !== '') { @@ -496,6 +453,7 @@ export class DecorationAddon extends Disposable implements ITerminalAddon { } await this._configurationService.updateValue(TerminalSettingId.ShellIntegrationDecorationsEnabled, newValue); }); + quickPick.ok = false; quickPick.show(); } } diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/decorationStyles.ts b/src/vs/workbench/contrib/terminal/browser/xterm/decorationStyles.ts index 5f3a59ec230..87b4d53b5db 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/decorationStyles.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/decorationStyles.ts @@ -3,8 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as dom from 'vs/base/browser/dom'; +import { Delayer } from 'vs/base/common/async'; +import { fromNow } from 'vs/base/common/date'; +import { MarkdownString } from 'vs/base/common/htmlContent'; +import { combinedDisposable, Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; +import { ITerminalCommand } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; const enum DecorationStyles { DefaultDimension = 16, @@ -20,7 +29,66 @@ export const enum DecorationSelector { Codicon = 'codicon', XtermDecoration = 'xterm-decoration', OverviewRuler = '.xterm-decoration-overview-ruler', - QuickFix = 'codicon-light-bulb' + QuickFix = 'quick-fix', + LightBulb = 'codicon-light-bulb' +} + +export class TerminalDecorationHoverManager extends Disposable { + private _hoverDelayer: Delayer; + private _contextMenuVisible: boolean = false; + + constructor(@IHoverService private readonly _hoverService: IHoverService, + @IConfigurationService configurationService: IConfigurationService, + @IContextMenuService contextMenuService: IContextMenuService) { + super(); + this._register(contextMenuService.onDidShowContextMenu(() => this._contextMenuVisible = true)); + this._register(contextMenuService.onDidHideContextMenu(() => this._contextMenuVisible = false)); + this._hoverDelayer = this._register(new Delayer(configurationService.getValue('workbench.hover.delay'))); + } + + public hideHover() { + this._hoverDelayer.cancel(); + this._hoverService.hideHover(); + } + + createHover(element: HTMLElement, command: ITerminalCommand | undefined, hoverMessage?: string): IDisposable { + return combinedDisposable( + dom.addDisposableListener(element, dom.EventType.MOUSE_ENTER, () => { + if (this._contextMenuVisible) { + return; + } + this._hoverDelayer.trigger(() => { + let hoverContent = `${localize('terminalPromptContextMenu', "Show Command Actions")}`; + hoverContent += '\n\n---\n\n'; + if (!command) { + if (hoverMessage) { + hoverContent = hoverMessage; + } else { + return; + } + } else if (command.markProperties || hoverMessage) { + if (command.markProperties?.hoverMessage || hoverMessage) { + hoverContent = command.markProperties?.hoverMessage || hoverMessage || ''; + } else { + return; + } + } else if (command.exitCode) { + if (command.exitCode === -1) { + hoverContent += localize('terminalPromptCommandFailed', 'Command executed {0} and failed', fromNow(command.timestamp, true)); + } else { + hoverContent += localize('terminalPromptCommandFailedWithExitCode', 'Command executed {0} and failed (Exit Code {1})', fromNow(command.timestamp, true), command.exitCode); + } + } else { + hoverContent += localize('terminalPromptCommandSuccess', 'Command executed {0}', fromNow(command.timestamp, true)); + } + this._hoverService.showHover({ content: new MarkdownString(hoverContent), target: element }); + }); + }), + dom.addDisposableListener(element, dom.EventType.MOUSE_LEAVE, () => this.hideHover()), + dom.addDisposableListener(element, dom.EventType.MOUSE_OUT, () => this.hideHover()) + ); + } + } export function updateLayout(configurationService: IConfigurationService, element?: HTMLElement): void { @@ -39,4 +107,3 @@ export function updateLayout(configurationService: IConfigurationService, elemen element.style.marginLeft = `${scalar * DecorationStyles.MarginLeft}px`; } } - diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/quickFixAddon.ts b/src/vs/workbench/contrib/terminal/browser/xterm/quickFixAddon.ts new file mode 100644 index 00000000000..44ce6c7858f --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/xterm/quickFixAddon.ts @@ -0,0 +1,275 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { ITerminalCapabilityStore, ITerminalCommand, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; +import * as dom from 'vs/base/browser/dom'; +import { IAction } from 'vs/base/common/actions'; +import { asArray } from 'vs/base/common/arrays'; +import { Color } from 'vs/base/common/color'; +import { localize } from 'vs/nls'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IColorTheme, ICssStyleCollector, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { PANEL_BACKGROUND } from 'vs/workbench/common/theme'; +import { AudioCue, IAudioCueService } from 'vs/workbench/contrib/audioCues/browser/audioCueService'; +import { ITerminalQuickFixOptions } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { DecorationSelector, TerminalDecorationHoverManager, updateLayout } from 'vs/workbench/contrib/terminal/browser/xterm/decorationStyles'; +import { TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TERMINAL_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IDecoration, Terminal } from 'xterm'; +// Importing types is safe in any layer +// eslint-disable-next-line local/code-import-patterns +import type { ITerminalAddon } from 'xterm-headless'; + + +const quickFixSelectors = [DecorationSelector.QuickFix, DecorationSelector.LightBulb, DecorationSelector.Codicon, DecorationSelector.CommandDecoration, DecorationSelector.XtermDecoration]; +export interface ITerminalQuickFix { + showMenu(): void; + /** + * Registers a listener on onCommandFinished scoped to a particular command or regular + * expression and provides a callback to be executed for commands that match. + */ + registerCommandFinishedListener(options: ITerminalQuickFixOptions): void; +} + +export interface ITerminalQuickFixAddon extends ITerminalQuickFix { + onDidRequestRerunCommand: Event<{ command: string; addNewLine?: boolean }>; +} + +export class TerminalQuickFixAddon extends Disposable implements ITerminalAddon, ITerminalQuickFixAddon { + private readonly _onDidRequestRerunCommand = new Emitter<{ command: string; addNewLine?: boolean }>(); + readonly onDidRequestRerunCommand = this._onDidRequestRerunCommand.event; + + private _terminal: Terminal | undefined; + + private _commandListeners: Map = new Map(); + + private _quickFixes: IAction[] | undefined; + + private _decoration: IDecoration | undefined; + + private readonly _terminalDecorationHoverService: TerminalDecorationHoverManager; + + constructor(private readonly _capabilities: ITerminalCapabilityStore, + @IContextMenuService private readonly _contextMenuService: IContextMenuService, + @IConfigurationService private readonly _configurationService: IConfigurationService, + @IInstantiationService instantiationService: IInstantiationService, + @IAudioCueService private readonly _audioCueService: IAudioCueService, + @IKeybindingService private readonly _keybindingService: IKeybindingService, + @IOpenerService private readonly _openerService: IOpenerService + ) { + super(); + const commandDetectionCapability = this._capabilities.get(TerminalCapability.CommandDetection); + if (commandDetectionCapability) { + this._registerCommandHandlers(); + } else { + this._capabilities.onDidAddCapability(c => { + if (c === TerminalCapability.CommandDetection) { + this._registerCommandHandlers(); + } + }); + } + this._terminalDecorationHoverService = instantiationService.createInstance(TerminalDecorationHoverManager); + } + + activate(terminal: Terminal): void { + this._terminal = terminal; + } + + showMenu(): void { + this._decoration?.element?.click(); + } + + registerCommandFinishedListener(options: ITerminalQuickFixOptions): void { + const matcherKey = options.commandLineMatcher.toString(); + const currentOptions = this._commandListeners.get(matcherKey) || []; + currentOptions.push(options); + this._commandListeners.set(matcherKey, currentOptions); + } + + private _registerCommandHandlers(): void { + const terminal = this._terminal; + const commandDetection = this._capabilities.get(TerminalCapability.CommandDetection); + if (!terminal || !commandDetection) { + return; + } + this._register(commandDetection.onCommandFinished(command => this._resolveQuickFixes(command))); + // The buffer is not ready by the time command finish + // is called. Add the decoration on command start if there are corresponding quick fixes + this._register(commandDetection.onCommandStarted(() => { + this._registerQuickFixDecoration(); + this._quickFixes = undefined; + })); + } + + /** + * Resolves quick fixes, if any, based on the + * @param command & its output + */ + private _resolveQuickFixes(command: ITerminalCommand): void { + if (command.command !== '') { + this._disposeQuickFix(); + } + const result = getQuickFixesForCommand(command, this._commandListeners, this._openerService, this._onDidRequestRerunCommand); + if (!result) { + return; + } + const { fixes, onDidRunQuickFix } = result; + this._quickFixes = fixes; + onDidRunQuickFix(() => this._disposeQuickFix()); + } + + private _disposeQuickFix(): void { + this._decoration?.dispose(); + this._decoration = undefined; + this._quickFixes = undefined; + } + + /** + * Registers a decoration with the quick fixes + */ + private _registerQuickFixDecoration(): void { + if (!this._terminal) { + return; + } + if (!this._quickFixes) { + return; + } + const marker = this._terminal.registerMarker(); + if (!marker) { + return; + } + const decoration = this._terminal.registerDecoration({ marker, layer: 'top' }); + if (!decoration) { + return; + } + this._decoration = decoration; + const kb = this._keybindingService.lookupKeybinding(TerminalCommandId.QuickFix); + const hoverLabel = kb ? localize('terminalQuickFixWithKb', "Show Quick Fixes ({0})", kb.getLabel()) : ''; + const fixes = this._quickFixes; + if (!fixes) { + decoration.dispose(); + return; + } + decoration?.onRender((e: HTMLElement) => { + if (e.classList.contains(DecorationSelector.QuickFix)) { + return; + } + e.classList.add(...quickFixSelectors); + updateLayout(this._configurationService, e); + this._audioCueService.playAudioCue(AudioCue.terminalQuickFix); + this._register(dom.addDisposableListener(e, dom.EventType.CLICK, () => { + this._contextMenuService.showContextMenu({ getAnchor: () => e, getActions: () => fixes, autoSelectFirstItem: true }); + })); + this._register(this._terminalDecorationHoverService.createHover(e, undefined, hoverLabel)); + }); + } +} + +export function getQuickFixesForCommand( + command: ITerminalCommand, + quickFixOptions: Map, + openerService: IOpenerService, + onDidRequestRerunCommand?: Emitter<{ command: string; addNewLine?: boolean }> +): { fixes: IAction[]; onDidRunQuickFix: Event } | undefined { + const onDidRunQuickFixEmitter = new Emitter(); + const onDidRunQuickFix = onDidRunQuickFixEmitter.event; + const fixes: IAction[] = []; + const newCommand = command.command; + for (const options of quickFixOptions.values()) { + for (const option of options) { + if (option.exitStatus !== undefined && option.exitStatus !== (command.exitCode === 0)) { + continue; + } + const commandLineMatch = newCommand.match(option.commandLineMatcher); + if (!commandLineMatch) { + continue; + } + const outputMatcher = option.outputMatcher; + let outputMatch; + if (outputMatcher) { + outputMatch = command.getOutputMatch(outputMatcher); + } + const quickFixes = option.getQuickFixes({ commandLineMatch, outputMatch }, command); + if (quickFixes) { + for (const quickFix of asArray(quickFixes)) { + let action: IAction | undefined; + if ('type' in quickFix) { + switch (quickFix.type) { + case 'command': { + const label = localize('quickFix.command', 'Run: {0}', quickFix.command); + action = { + id: `quickFix.command`, + label, + class: undefined, + enabled: true, + run: () => { + onDidRequestRerunCommand?.fire({ + command: quickFix.command, + addNewLine: quickFix.addNewLine + }); + }, + tooltip: label, + command: quickFix.command + } as IAction; + break; + } + case 'opener': { + const label = localize('quickFix.opener', 'Open: {0}', quickFix.uri.toString()); + action = { + id: `quickFix.opener`, + label, + class: undefined, + enabled: true, + run: () => { + openerService.open(quickFix.uri); + // since no command gets run here, need to + // clear the decoration and quick fix + onDidRunQuickFixEmitter.fire(); + }, + tooltip: label, + uri: quickFix.uri + } as IAction; + break; + } + } + } else { + action = { + id: quickFix.id, + label: quickFix.label, + class: quickFix.class, + enabled: quickFix.enabled, + run: () => quickFix.run(), + tooltip: quickFix.tooltip + }; + } + if (action) { + fixes.push(action); + } + } + } + } + } + return fixes.length > 0 ? { fixes, onDidRunQuickFix } : undefined; +} + + + +let foregroundColor: string | Color | undefined; +let backgroundColor: string | Color | undefined; +registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { + foregroundColor = theme.getColor('editorLightBulb.foreground'); + backgroundColor = theme.getColor(TERMINAL_BACKGROUND_COLOR) || theme.getColor(PANEL_BACKGROUND); + if (foregroundColor) { + collector.addRule(`.${DecorationSelector.CommandDecoration}.${DecorationSelector.QuickFix} { color: ${foregroundColor.toString()} !important; } `); + } + if (backgroundColor) { + collector.addRule(`.${DecorationSelector.CommandDecoration}.${DecorationSelector.QuickFix} { background-color: ${backgroundColor.toString()}; } `); + } +}); diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index 892e5411f86..553df339e49 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -66,6 +66,40 @@ function getFullBufferLineAsString(lineIndex: number, buffer: IBuffer): { lineDa return { lineData, lineIndex }; } + +// DEBUG: This helper can be used to draw image data to the console, it's commented out as we don't +// want to ship it, but this is very useful for investigating texture atlas issues. +// (console as any).image = (source: ImageData | HTMLCanvasElement, scale: number = 1) => { +// function getBox(width: number, height: number) { +// return { +// string: '+', +// style: 'font-size: 1px; padding: ' + Math.floor(height/2) + 'px ' + Math.floor(width/2) + 'px; line-height: ' + height + 'px;' +// }; +// } +// if (source instanceof HTMLCanvasElement) { +// source = source.getContext('2d')?.getImageData(0, 0, source.width, source.height)!; +// } +// const canvas = document.createElement('canvas'); +// canvas.width = source.width; +// canvas.height = source.height; +// const ctx = canvas.getContext('2d')!; +// ctx.putImageData(source, 0, 0); + +// const sw = source.width * scale; +// const sh = source.height * scale; +// const dim = getBox(sw, sh); +// console.log( +// `Image: ${source.width} x ${source.height}\n%c${dim.string}`, +// `${dim.style}background: url(${canvas.toDataURL()}); background-size: ${sw}px ${sh}px; background-repeat: no-repeat; color: transparent;` +// ); +// console.groupCollapsed('Zoomed'); +// console.log( +// `%c${dim.string}`, +// `${getBox(sw * 10, sh * 10).style}background: url(${canvas.toDataURL()}); background-size: ${sw * 10}px ${sh * 10}px; background-repeat: no-repeat; color: transparent; image-rendering: pixelated;-ms-interpolation-mode: nearest-neighbor;` +// ); +// console.groupEnd(); +// }; + /** * Wraps the xterm object with additional functionality. Interaction with the backing process is out * of the scope of this class. @@ -121,6 +155,14 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, II } get target(): TerminalLocation | undefined { return this._target; } + get textureAtlas(): Promise | undefined { + const canvas = this._webglAddon?.textureAtlas || this._canvasAddon?.textureAtlas; + if (!canvas) { + return undefined; + } + return createImageBitmap(canvas); + } + /** * @param xtermCtor The xterm.js constructor, this is passed in so it can be fetched lazily * outside of this class such that {@link raw} is not nullable. @@ -239,12 +281,13 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, II if (!this._container) { this.raw.open(container); } - this._container = container; + // TODO: Move before open to the DOM renderer doesn't initialize if (this._shouldLoadWebgl()) { this._enableWebglRenderer(); } else if (this._shouldLoadCanvas()) { this._enableCanvasRenderer(); } + this._container = container; // Screen must be created at this point as xterm.open is called return this._container.querySelector('.xterm-screen')!; } @@ -289,7 +332,6 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, II } forceRedraw() { - this._webglAddon?.clearTextureAtlas(); this.raw.clearTextureAtlas(); } diff --git a/src/vs/workbench/contrib/terminal/common/environmentVariable.contribution.ts b/src/vs/workbench/contrib/terminal/common/environmentVariable.contribution.ts index 7fd4e951754..3897ed68693 100644 --- a/src/vs/workbench/contrib/terminal/common/environmentVariable.contribution.ts +++ b/src/vs/workbench/contrib/terminal/common/environmentVariable.contribution.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { EnvironmentVariableService } from 'vs/workbench/contrib/terminal/common/environmentVariableService'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IEnvironmentVariableService } from 'vs/workbench/contrib/terminal/common/environmentVariable'; -registerSingleton(IEnvironmentVariableService, EnvironmentVariableService, true); +registerSingleton(IEnvironmentVariableService, EnvironmentVariableService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts b/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts index 23d2d4552b1..9efd1de958c 100644 --- a/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts +++ b/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts @@ -237,8 +237,8 @@ export class RemoteTerminalChannelClient implements IPtyHostController { sendCommandResult(reqId: number, isError: boolean, payload: any): Promise { return this._channel.call('$sendCommandResult', [reqId, isError, payload]); } - freePortKillProcess(id: number, port: string): Promise<{ port: string; processId: string }> { - return this._channel.call('$freePortKillProcess', [id, port]); + freePortKillProcess(port: string): Promise<{ port: string; processId: string }> { + return this._channel.call('$freePortKillProcess', [port]); } installAutoReply(match: string, reply: string): Promise { return this._channel.call('$installAutoReply', [match, reply]); diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index e886f17e418..2f6901c8d82 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -13,7 +13,7 @@ import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/e import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { URI } from 'vs/base/common/uri'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IMarkProperties, ISerializedCommandDetectionCapability, ITerminalCapabilityStore, IXtermMarker } from 'vs/platform/terminal/common/capabilities/capabilities'; +import { IMarkProperties, ISerializedCommandDetectionCapability, ITerminalCapabilityStore, ITerminalOutputMatcher, IXtermMarker } from 'vs/platform/terminal/common/capabilities/capabilities'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IProcessDetails } from 'vs/platform/terminal/common/terminalProcess'; @@ -96,13 +96,6 @@ export interface IShellLaunchConfigResolveOptions { allowAutomationShell?: boolean; } -export interface ITerminalOutputMatcher { - lineMatcher: string | RegExp; - anchor?: 'top' | 'bottom'; - offset?: number; - length?: number; -} - export interface ITerminalBackend { readonly remoteAuthority: string | undefined; @@ -580,6 +573,7 @@ export const enum TerminalCommandId { SetDimensions = 'workbench.action.terminal.setDimensions', ClearCommandHistory = 'workbench.action.terminal.clearCommandHistory', WriteDataToTerminal = 'workbench.action.terminal.writeDataToTerminal', + ShowTextureAtlas = 'workbench.action.terminal.showTextureAtlas', } export const DEFAULT_COMMANDS_TO_SKIP_SHELL: string[] = [ @@ -615,6 +609,7 @@ export const DEFAULT_COMMANDS_TO_SKIP_SHELL: string[] = [ TerminalCommandId.New, TerminalCommandId.Paste, TerminalCommandId.PasteSelection, + TerminalCommandId.QuickFix, TerminalCommandId.ResizePaneDown, TerminalCommandId.ResizePaneLeft, TerminalCommandId.ResizePaneRight, diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index 790e1777020..ed2caa4f5c2 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -186,7 +186,8 @@ const terminalConfiguration: IConfigurationNode = { [TerminalSettingId.MinimumContrastRatio]: { markdownDescription: localize('terminal.integrated.minimumContrastRatio', "When set, the foreground color of each cell will change to try meet the contrast ratio specified. Note that this will not apply to `powerline` characters per #146406. Example values:\n\n- 1: Do nothing and use the standard theme colors.\n- 4.5: [WCAG AA compliance (minimum)](https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html) (default).\n- 7: [WCAG AAA compliance (enhanced)](https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast7.html).\n- 21: White on black or black on white."), type: 'number', - default: 4.5 + default: 4.5, + tags: ['accessibility'] }, [TerminalSettingId.FastScrollSensitivity]: { markdownDescription: localize('terminal.integrated.fastScrollSensitivity', "Scrolling speed multiplier when pressing `Alt`."), diff --git a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts index 80041aeb869..290023e91b6 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts @@ -14,7 +14,8 @@ import { IConfigurationResolverService } from 'vs/workbench/services/configurati import { sanitizeProcessEnvironment } from 'vs/base/common/processes'; import { ILogService } from 'vs/platform/log/common/log'; import { IShellLaunchConfig, ITerminalEnvironment, TerminalSettingId, TerminalSettingPrefix } from 'vs/platform/terminal/common/terminal'; -import { IProcessEnvironment, isWindows, locale, OperatingSystem, OS, platform, Platform } from 'vs/base/common/platform'; +import { IProcessEnvironment, isWindows, locale, platform, Platform } from 'vs/base/common/platform'; +import { sanitizeCwd } from 'vs/platform/terminal/common/terminalEnvironment'; export function mergeEnvironments(parent: IProcessEnvironment, other: ITerminalEnvironment | undefined): void { if (!other) { @@ -190,7 +191,7 @@ export async function getCwd( if (shell.cwd) { const unresolved = (typeof shell.cwd === 'object') ? shell.cwd.fsPath : shell.cwd; const resolved = await _resolveCwd(unresolved, variableResolver); - return _sanitizeCwd(resolved || unresolved); + return sanitizeCwd(resolved || unresolved); } let cwd: string | undefined; @@ -213,7 +214,7 @@ export async function getCwd( cwd = root ? root.fsPath : userHome || ''; } - return _sanitizeCwd(cwd); + return sanitizeCwd(cwd); } async function _resolveCwd(cwd: string, variableResolver: VariableResolver | undefined, logService?: ILogService): Promise { @@ -228,14 +229,6 @@ async function _resolveCwd(cwd: string, variableResolver: VariableResolver | und return cwd; } -function _sanitizeCwd(cwd: string): string { - // Make the drive letter uppercase on Windows (see #9448) - if (OS === OperatingSystem.Windows && cwd && cwd[1] === ':') { - return cwd[0].toUpperCase() + cwd.substr(1); - } - return cwd; -} - export type TerminalShellSetting = ( TerminalSettingId.AutomationShellWindows | TerminalSettingId.AutomationShellMacOs diff --git a/src/vs/workbench/contrib/terminal/common/terminalExtensionPoints.contribution.ts b/src/vs/workbench/contrib/terminal/common/terminalExtensionPoints.contribution.ts index d3d2e293eb3..a705e6fc45f 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalExtensionPoints.contribution.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalExtensionPoints.contribution.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ITerminalContributionService, TerminalContributionService } from 'vs/workbench/contrib/terminal/common/terminalExtensionPoints'; -registerSingleton(ITerminalContributionService, TerminalContributionService, true); +registerSingleton(ITerminalContributionService, TerminalContributionService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/localPty.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/localPty.ts index dea8aba884e..c8379fe51c8 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/localPty.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/localPty.ts @@ -80,7 +80,7 @@ export class LocalPty extends Disposable implements ITerminalChildProcess { if (!this._localPtyService.freePortKillProcess) { throw new Error('freePortKillProcess does not exist on the local pty service'); } - return this._localPtyService.freePortKillProcess(this.id, port); + return this._localPtyService.freePortKillProcess(port); } async getInitialCwd(): Promise { return this._properties.initialCwd; diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/terminal.contribution.ts index 2e768f25878..b47d5943150 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/terminal.contribution.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { registerSharedProcessRemoteService } from 'vs/platform/ipc/electron-sandbox/services'; import { Registry } from 'vs/platform/registry/common/platform'; import { TerminalIpcChannels } from 'vs/platform/terminal/common/terminal'; @@ -17,9 +17,9 @@ import { LocalTerminalBackendContribution } from 'vs/workbench/contrib/terminal/ // Register services registerSharedProcessRemoteService(ILocalPtyService, TerminalIpcChannels.LocalPty); -registerSingleton(ITerminalProfileResolverService, ElectronTerminalProfileResolverService, true); +registerSingleton(ITerminalProfileResolverService, ElectronTerminalProfileResolverService, InstantiationType.Delayed); // Register workbench contributions const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(LocalTerminalBackendContribution, 'LocalTerminalBackendContribution', LifecyclePhase.Restored); -workbenchRegistry.registerWorkbenchContribution(TerminalNativeContribution, 'TerminalNativeContribution', LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(LocalTerminalBackendContribution, LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(TerminalNativeContribution, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/terminalNativeContribution.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/terminalNativeContribution.ts index b22ebddac85..b1c88750736 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/terminalNativeContribution.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/terminalNativeContribution.ts @@ -43,7 +43,9 @@ export class TerminalNativeContribution extends Disposable implements IWorkbench } private _onOsResume(): void { - this._terminalService.instances.forEach(instance => instance.xterm?.forceRedraw()); + for (const instance of this._terminalService.instances) { + instance.xterm?.forceRedraw(); + } } private async _onOpenFileRequest(request: INativeOpenFileRequest): Promise { diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/terminalRemote.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/terminalRemote.ts index e52260f1843..6d2c0b18700 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/terminalRemote.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/terminalRemote.ts @@ -3,59 +3,52 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { TERMINAL_ACTION_CATEGORY, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; -import { Action } from 'vs/base/common/actions'; -import { ITerminalGroupService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { Schemas } from 'vs/base/common/network'; +import { URI } from 'vs/base/common/uri'; +import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; +import { localize } from 'vs/nls'; +import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { URI } from 'vs/base/common/uri'; +import { ITerminalGroupService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; -import { Schemas } from 'vs/base/common/network'; export function registerRemoteContributions() { - const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); - actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(CreateNewLocalTerminalAction), 'Terminal: Create New Integrated Terminal (Local)', TERMINAL_ACTION_CATEGORY); -} - -export class CreateNewLocalTerminalAction extends Action { - static readonly ID = TerminalCommandId.NewLocal; - static readonly LABEL = nls.localize('workbench.action.terminal.newLocal', "Create New Integrated Terminal (Local)"); - - constructor( - id: string, label: string, - @ITerminalService private readonly _terminalService: ITerminalService, - @ITerminalGroupService private readonly _terminalGroupService: ITerminalGroupService, - @INativeEnvironmentService private readonly _nativeEnvironmentService: INativeEnvironmentService, - @IRemoteAuthorityResolverService private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService, - @IHistoryService private readonly _historyService: IHistoryService - ) { - super(id, label); - } - - override async run(): Promise { - let cwd: URI | undefined; - try { - const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.vscodeRemote); - if (activeWorkspaceRootUri) { - const canonicalUri = await this._remoteAuthorityResolverService.getCanonicalURI(activeWorkspaceRootUri); - if (canonicalUri.scheme === Schemas.file) { - cwd = canonicalUri; + registerAction2(class extends Action2 { + constructor() { + super({ + id: TerminalCommandId.NewLocal, + title: { value: localize('workbench.action.terminal.newLocal', "Create New Integrated Terminal (Local)"), original: 'Create New Integrated Terminal (Local)' }, + f1: true + }); + } + async run(accessor: ServicesAccessor) { + const historyService = accessor.get(IHistoryService); + const remoteAuthorityResolverService = accessor.get(IRemoteAuthorityResolverService); + const nativeEnvironmentService = accessor.get(INativeEnvironmentService); + const terminalService = accessor.get(ITerminalService); + const terminalGroupService = accessor.get(ITerminalGroupService); + let cwd: URI | undefined; + try { + const activeWorkspaceRootUri = historyService.getLastActiveWorkspaceRoot(Schemas.vscodeRemote); + if (activeWorkspaceRootUri) { + const canonicalUri = await remoteAuthorityResolverService.getCanonicalURI(activeWorkspaceRootUri); + if (canonicalUri.scheme === Schemas.file) { + cwd = canonicalUri; + } } + } catch { } + if (!cwd) { + cwd = nativeEnvironmentService.userHome; + } + const instance = await terminalService.createTerminal({ cwd }); + if (!instance) { + return Promise.resolve(undefined); } - } catch { } - if (!cwd) { - cwd = this._nativeEnvironmentService.userHome; - } - const instance = await this._terminalService.createTerminal({ cwd }); - if (!instance) { - return Promise.resolve(undefined); - } - this._terminalService.setActiveInstance(instance); - return this._terminalGroupService.showPanel(true); - } + terminalService.setActiveInstance(instance); + return terminalGroupService.showPanel(true); + } + }); } diff --git a/src/vs/workbench/contrib/terminal/test/browser/contextualActionAddon.test.ts b/src/vs/workbench/contrib/terminal/test/browser/contextualActionAddon.test.ts deleted file mode 100644 index d317f678d19..00000000000 --- a/src/vs/workbench/contrib/terminal/test/browser/contextualActionAddon.test.ts +++ /dev/null @@ -1,237 +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 { strictEqual } from 'assert'; -import { OpenerService } from 'vs/editor/browser/services/openerService'; -import { ContextMenuService } from 'vs/platform/contextview/browser/contextMenuService'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { ILogService, NullLogService } from 'vs/platform/log/common/log'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { ITerminalCommand, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; -import { CommandDetectionCapability } from 'vs/platform/terminal/common/capabilities/commandDetectionCapability'; -import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/terminalCapabilityStore'; -import { ICommandAction, ITerminalInstance, ITerminalOutputMatcher } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { freePort, FreePortOutputRegex, gitCreatePr, GitCreatePrOutputRegex, GitPushOutputRegex, gitPushSetUpstream, gitSimilarCommand, GitSimilarOutputRegex } from 'vs/workbench/contrib/terminal/browser/terminalBaseContextualActions'; -import { ContextualActionAddon, getMatchActions } from 'vs/workbench/contrib/terminal/browser/xterm/contextualActionAddon'; -import { Terminal } from 'xterm'; - -suite('ContextualActionAddon', () => { - let contextualActionAddon: ContextualActionAddon; - let terminalInstance: Pick; - let commandDetection: CommandDetectionCapability; - let openerService: OpenerService; - setup(() => { - const instantiationService = new TestInstantiationService(); - const xterm = new Terminal({ - allowProposedApi: true, - cols: 80, - rows: 30 - }); - const capabilities = new TerminalCapabilityStore(); - instantiationService.stub(ILogService, new NullLogService()); - commandDetection = instantiationService.createInstance(CommandDetectionCapability, xterm); - capabilities.add(TerminalCapability.CommandDetection, commandDetection); - instantiationService.stub(IContextMenuService, instantiationService.createInstance(ContextMenuService)); - openerService = instantiationService.createInstance(OpenerService); - instantiationService.stub(IOpenerService, openerService); - terminalInstance = { - async freePortKillProcess(port: string): Promise { } - } as Pick; - contextualActionAddon = instantiationService.createInstance(ContextualActionAddon, capabilities); - xterm.loadAddon(contextualActionAddon); - }); - suite('registerCommandFinishedListener & getMatchActions', () => { - suite('gitSimilarCommand', async () => { - const expectedMap = new Map(); - const command = `git sttatus`; - const output = `git: 'sttatus' is not a git command. See 'git --help'. - - The most similar command is - status`; - const exitCode = 1; - const actions = [ - { - id: 'terminal.gitSimilarCommand', - label: 'Run git status', - run: true, - tooltip: 'Run git status', - enabled: true - } - ]; - setup(() => { - const command = gitSimilarCommand(); - expectedMap.set(command.commandLineMatcher.toString(), [command]); - contextualActionAddon.registerCommandFinishedListener(command); - }); - suite('returns undefined when', () => { - test('output does not match', () => { - strictEqual(getMatchActions(createCommand(command, `invalid output`, GitSimilarOutputRegex, exitCode), expectedMap), undefined); - }); - test('command does not match', () => { - strictEqual(getMatchActions(createCommand(`gt sttatus`, output, GitSimilarOutputRegex, exitCode), expectedMap), undefined); - }); - }); - suite('returns undefined when', () => { - test('expected unix exit code', () => { - assertMatchOptions(getMatchActions(createCommand(command, output, GitSimilarOutputRegex, exitCode), expectedMap), actions); - }); - test('matching exit status', () => { - assertMatchOptions(getMatchActions(createCommand(command, output, GitSimilarOutputRegex, 2), expectedMap), actions); - }); - }); - }); - suite('freePort', () => { - const expected = new Map(); - const portCommand = `yarn start dev`; - const output = `yarn run v1.22.17 - warning ../../package.json: No license field - Error: listen EADDRINUSE: address already in use 0.0.0.0:3000 - at Server.setupListenHandle [as _listen2] (node:net:1315:16) - at listenInCluster (node:net:1363:12) - at doListen (node:net:1501:7) - at processTicksAndRejections (node:internal/process/task_queues:84:21) - Emitted 'error' event on WebSocketServer instance at: - at Server.emit (node:events:394:28) - at emitErrorNT (node:net:1342:8) - at processTicksAndRejections (node:internal/process/task_queues:83:21) { - } - error Command failed with exit code 1. - info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.`; - const actionOptions = [{ - id: 'terminal.freePort', - label: 'Free port 3000', - run: true, - tooltip: 'Free port 3000', - enabled: true - }]; - setup(() => { - const command = freePort(terminalInstance); - expected.set(command.commandLineMatcher.toString(), [command]); - contextualActionAddon.registerCommandFinishedListener(command); - }); - suite('returns undefined when', () => { - test('output does not match', () => { - strictEqual(getMatchActions(createCommand(portCommand, `invalid output`, FreePortOutputRegex), expected), undefined); - }); - }); - test.skip('returns actions', () => { - assertMatchOptions(getMatchActions(createCommand(portCommand, output, FreePortOutputRegex), expected), actionOptions); - }); - }); - suite('gitPushSetUpstream', () => { - const expectedMap = new Map(); - const command = `git push`; - const output = `fatal: The current branch test22 has no upstream branch. - To push the current branch and set the remote as upstream, use - - git push --set-upstream origin test22 `; - const exitCode = 128; - const actions = [ - { - id: 'terminal.gitPush', - label: 'Git push test22', - run: true, - tooltip: 'Git push test22', - enabled: true - } - ]; - setup(() => { - const command = gitPushSetUpstream(); - expectedMap.set(command.commandLineMatcher.toString(), [command]); - contextualActionAddon.registerCommandFinishedListener(command); - }); - suite('returns undefined when', () => { - test('output does not match', () => { - strictEqual(getMatchActions(createCommand(command, `invalid output`, GitPushOutputRegex, exitCode), expectedMap), undefined); - }); - test('command does not match', () => { - strictEqual(getMatchActions(createCommand(`git status`, output, GitPushOutputRegex, exitCode), expectedMap), undefined); - }); - }); - suite('returns undefined when', () => { - test('expected unix exit code', () => { - assertMatchOptions(getMatchActions(createCommand(command, output, GitPushOutputRegex, exitCode), expectedMap), actions); - }); - test('matching exit status', () => { - assertMatchOptions(getMatchActions(createCommand(command, output, GitPushOutputRegex, 2), expectedMap), actions); - }); - }); - }); - suite('gitCreatePr', () => { - const expectedMap = new Map(); - const command = `git push`; - const output = `Total 0 (delta 0), reused 0 (delta 0), pack-reused 0 - remote: - remote: Create a pull request for 'test22' on GitHub by visiting: - remote: https://github.com/meganrogge/xterm.js/pull/new/test22 - remote: - To https://github.com/meganrogge/xterm.js - * [new branch] test22 -> test22 - Branch 'test22' set up to track remote branch 'test22' from 'origin'. `; - const exitCode = 0; - const actions = [ - { - id: 'terminal.gitCreatePr', - label: 'Create PR', - run: true, - tooltip: 'Create PR', - enabled: true - } - ]; - setup(() => { - const command = gitCreatePr(openerService); - expectedMap.set(command.commandLineMatcher.toString(), [command]); - contextualActionAddon.registerCommandFinishedListener(command); - }); - suite('returns undefined when', () => { - test('output does not match', () => { - strictEqual(getMatchActions(createCommand(command, `invalid output`, GitCreatePrOutputRegex, exitCode), expectedMap), undefined); - }); - test('command does not match', () => { - strictEqual(getMatchActions(createCommand(`git status`, output, GitCreatePrOutputRegex, exitCode), expectedMap), undefined); - }); - test('failure exit status', () => { - strictEqual(getMatchActions(createCommand(command, output, GitCreatePrOutputRegex, 2), expectedMap), undefined); - }); - }); - suite('returns actions when', () => { - test('expected unix exit code', () => { - assertMatchOptions(getMatchActions(createCommand(command, output, GitCreatePrOutputRegex, exitCode), expectedMap), actions); - }); - }); - }); - }); -}); - -function createCommand(command: string, output: string, outputMatcher?: RegExp | string, exitCode?: number): ITerminalCommand { - return { - command, - exitCode, - getOutput: () => { return output; }, - getOutputMatch: (matcher: ITerminalOutputMatcher) => { - if (outputMatcher) { - return output.match(outputMatcher) ?? undefined; - } - return undefined; - }, - timestamp: Date.now(), - hasOutput: () => !!output - }; -} - -function assertMatchOptions(actual: ICommandAction[] | undefined, expected: { id: string; label: string; run: boolean; tooltip: string; enabled: boolean }[]): void { - strictEqual(actual?.length, expected.length); - let index = 0; - for (const i of actual) { - const j = expected[index]; - strictEqual(i.id, j.id, `ID`); - strictEqual(i.enabled, j.enabled, `enabled`); - strictEqual(i.label, j.label, `label`); - strictEqual(!!i.run, j.run, `run`); - strictEqual(i.tooltip, j.tooltip, `tooltip`); - index++; - } -} diff --git a/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkOpeners.test.ts b/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkOpeners.test.ts index 4aaa1f4fedf..528ca7290a2 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkOpeners.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkOpeners.test.ts @@ -17,7 +17,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { CommandDetectionCapability } from 'vs/platform/terminal/common/capabilities/commandDetectionCapability'; import { TerminalBuiltinLinkType } from 'vs/workbench/contrib/terminal/browser/links/links'; import { TerminalLocalFileLinkOpener, TerminalLocalFolderInWorkspaceLinkOpener, TerminalSearchLinkOpener } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkOpeners'; -import { TerminalCapability, ITerminalCommand, IXtermMarker } from 'vs/platform/terminal/common/capabilities/capabilities'; +import { TerminalCapability, ITerminalCommand, IXtermMarker, ITerminalOutputMatcher } from 'vs/platform/terminal/common/capabilities/capabilities'; import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/terminalCapabilityStore'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -25,7 +25,6 @@ import { TestContextService } from 'vs/workbench/test/common/workbenchTestServic import { Terminal } from 'xterm'; import { IFileQuery, ISearchComplete, ISearchService } from 'vs/workbench/services/search/common/search'; import { SearchService } from 'vs/workbench/services/search/common/searchService'; -import { ITerminalOutputMatcher } from 'vs/workbench/contrib/terminal/common/terminal'; export interface ITerminalLinkActivationResult { source: 'editor' | 'search'; diff --git a/src/vs/workbench/contrib/terminal/test/browser/quickFixAddon.test.ts b/src/vs/workbench/contrib/terminal/test/browser/quickFixAddon.test.ts new file mode 100644 index 00000000000..0f0ae6bdf57 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/test/browser/quickFixAddon.test.ts @@ -0,0 +1,359 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { strictEqual } from 'assert'; +import { IAction } from 'vs/base/common/actions'; +import { isWindows } from 'vs/base/common/platform'; +import { OpenerService } from 'vs/editor/browser/services/openerService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { ContextMenuService } from 'vs/platform/contextview/browser/contextMenuService'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { ILogService, NullLogService } from 'vs/platform/log/common/log'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { ITerminalCommand, ITerminalOutputMatcher, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; +import { CommandDetectionCapability } from 'vs/platform/terminal/common/capabilities/commandDetectionCapability'; +import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/terminalCapabilityStore'; +import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { freePort, FreePortOutputRegex, gitCreatePr, GitCreatePrOutputRegex, GitPushOutputRegex, gitPushSetUpstream, gitSimilarCommand, GitSimilarOutputRegex, gitTwoDashes, GitTwoDashesRegex } from 'vs/workbench/contrib/terminal/browser/terminalQuickFixBuiltinActions'; +import { TerminalQuickFixAddon, getQuickFixesForCommand } from 'vs/workbench/contrib/terminal/browser/xterm/quickFixAddon'; +import { URI } from 'vs/base/common/uri'; +import { Terminal } from 'xterm'; + +suite('QuickFixAddon', () => { + let quickFixAddon: TerminalQuickFixAddon; + let terminalInstance: Pick; + let commandDetection: CommandDetectionCapability; + let openerService: OpenerService; + setup(() => { + const instantiationService = new TestInstantiationService(); + const xterm = new Terminal({ + allowProposedApi: true, + cols: 80, + rows: 30 + }); + instantiationService.stub(IConfigurationService, new TestConfigurationService()); + const capabilities = new TerminalCapabilityStore(); + instantiationService.stub(ILogService, new NullLogService()); + commandDetection = instantiationService.createInstance(CommandDetectionCapability, xterm); + capabilities.add(TerminalCapability.CommandDetection, commandDetection); + instantiationService.stub(IContextMenuService, instantiationService.createInstance(ContextMenuService)); + openerService = instantiationService.createInstance(OpenerService); + instantiationService.stub(IOpenerService, openerService); + terminalInstance = { + async freePortKillProcess(port: string): Promise { } + } as Pick; + quickFixAddon = instantiationService.createInstance(TerminalQuickFixAddon, capabilities); + xterm.loadAddon(quickFixAddon); + }); + suite('registerCommandFinishedListener & getMatchActions', () => { + suite('gitSimilarCommand', async () => { + const expectedMap = new Map(); + const command = `git sttatus`; + let output = `git: 'sttatus' is not a git command. See 'git --help'. + + The most similar command is + status`; + const exitCode = 1; + const actions = [{ + id: `quickFix.command`, + enabled: true, + label: 'Run: git status', + tooltip: 'Run: git status', + command: 'git status' + }]; + setup(() => { + const command = gitSimilarCommand(); + expectedMap.set(command.commandLineMatcher.toString(), [command]); + quickFixAddon.registerCommandFinishedListener(command); + }); + suite('returns undefined when', () => { + test('output does not match', () => { + strictEqual(getQuickFixesForCommand(createCommand(command, `invalid output`, GitSimilarOutputRegex, exitCode), expectedMap, openerService), undefined); + }); + test('command does not match', () => { + strictEqual(getQuickFixesForCommand(createCommand(`gt sttatus`, output, GitSimilarOutputRegex, exitCode), expectedMap, openerService), undefined); + }); + }); + suite('returns undefined when', () => { + test('expected unix exit code', () => { + assertMatchOptions(getQuickFixesForCommand(createCommand(command, output, GitSimilarOutputRegex, exitCode), expectedMap, openerService)?.fixes, actions); + }); + test('matching exit status', () => { + assertMatchOptions(getQuickFixesForCommand(createCommand(command, output, GitSimilarOutputRegex, 2), expectedMap, openerService)?.fixes, actions); + }); + }); + suite('returns match', () => { + test('returns match', () => { + assertMatchOptions(getQuickFixesForCommand(createCommand(command, output, GitSimilarOutputRegex), expectedMap, openerService)?.fixes, actions); + }); + + test('returns multiple match', () => { + output = `git: 'pu' is not a git command. See 'git --help'. + + The most similar commands are + pull + push`; + const actions = [{ + id: `quickFix.command`, + enabled: true, + label: 'Run: git pull', + tooltip: 'Run: git pull', + command: 'git pull' + }, { + id: `quickFix.command`, + enabled: true, + label: 'Run: git push', + tooltip: 'Run: git push', + command: 'git push' + }]; + assertMatchOptions(getQuickFixesForCommand(createCommand('git pu', output, GitSimilarOutputRegex), expectedMap, openerService)?.fixes, actions); + }); + test('passes any arguments through', () => { + output = `git: 'checkoutt' is not a git command. See 'git --help'. + + The most similar commands are + checkout`; + assertMatchOptions(getQuickFixesForCommand(createCommand('git checkoutt .', output, GitSimilarOutputRegex), expectedMap, openerService)?.fixes, [{ + id: `quickFix.command`, + enabled: true, + label: 'Run: git checkout .', + tooltip: 'Run: git checkout .', + command: 'git checkout .' + }]); + }); + }); + }); + suite('gitTwoDashes', async () => { + const expectedMap = new Map(); + const command = `git add . -all`; + const output = 'error: did you mean `--all` (with two dashes)?'; + const exitCode = 1; + const actions = [{ + id: `quickFix.command`, + enabled: true, + label: 'Run: git add . --all', + tooltip: 'Run: git add . --all', + command: 'git add . --all' + }]; + setup(() => { + const command = gitTwoDashes(); + expectedMap.set(command.commandLineMatcher.toString(), [command]); + quickFixAddon.registerCommandFinishedListener(command); + }); + suite('returns undefined when', () => { + test('output does not match', () => { + strictEqual(getQuickFixesForCommand(createCommand(command, `invalid output`, GitTwoDashesRegex, exitCode), expectedMap, openerService), undefined); + }); + test('command does not match', () => { + strictEqual(getQuickFixesForCommand(createCommand(`gt sttatus`, output, GitTwoDashesRegex, exitCode), expectedMap, openerService), undefined); + }); + }); + suite('returns undefined when', () => { + test('expected unix exit code', () => { + assertMatchOptions(getQuickFixesForCommand(createCommand(command, output, GitTwoDashesRegex, exitCode), expectedMap, openerService)?.fixes, actions); + }); + test('matching exit status', () => { + assertMatchOptions(getQuickFixesForCommand(createCommand(command, output, GitTwoDashesRegex, 2), expectedMap, openerService)?.fixes, actions); + }); + }); + }); + if (!isWindows) { + suite('freePort', () => { + const expectedMap = new Map(); + const portCommand = `yarn start dev`; + const output = `yarn run v1.22.17 + warning ../../package.json: No license field + Error: listen EADDRINUSE: address already in use 0.0.0.0:3000 + at Server.setupListenHandle [as _listen2] (node:net:1315:16) + at listenInCluster (node:net:1363:12) + at doListen (node:net:1501:7) + at processTicksAndRejections (node:internal/process/task_queues:84:21) + Emitted 'error' event on WebSocketServer instance at: + at Server.emit (node:events:394:28) + at emitErrorNT (node:net:1342:8) + at processTicksAndRejections (node:internal/process/task_queues:83:21) { + } + error Command failed with exit code 1. + info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.`; + const actionOptions = [{ + id: 'terminal.freePort', + label: 'Free port 3000', + run: true, + tooltip: 'Free port 3000', + enabled: true + }]; + setup(() => { + const command = freePort(terminalInstance); + expectedMap.set(command.commandLineMatcher.toString(), [command]); + quickFixAddon.registerCommandFinishedListener(command); + }); + suite('returns undefined when', () => { + test('output does not match', () => { + strictEqual(getQuickFixesForCommand(createCommand(portCommand, `invalid output`, FreePortOutputRegex), expectedMap, openerService)?.fixes, undefined); + }); + }); + test('returns actions', () => { + assertMatchOptions(getQuickFixesForCommand(createCommand(portCommand, output, FreePortOutputRegex), expectedMap, openerService)?.fixes, actionOptions); + }); + }); + } + + suite('gitPushSetUpstream', () => { + const expectedMap = new Map(); + const command = `git push`; + const output = `fatal: The current branch test22 has no upstream branch. + To push the current branch and set the remote as upstream, use + + git push --set-upstream origin test22`; + const exitCode = 128; + const actions = [{ + id: `quickFix.command`, + enabled: true, + label: 'Run: git push --set-upstream origin test22', + tooltip: 'Run: git push --set-upstream origin test22', + command: 'git push --set-upstream origin test22' + }]; + setup(() => { + const command = gitPushSetUpstream(); + expectedMap.set(command.commandLineMatcher.toString(), [command]); + quickFixAddon.registerCommandFinishedListener(command); + }); + suite('returns undefined when', () => { + test('output does not match', () => { + strictEqual(getQuickFixesForCommand(createCommand(command, `invalid output`, GitPushOutputRegex, exitCode), expectedMap, openerService), undefined); + }); + test('command does not match', () => { + strictEqual(getQuickFixesForCommand(createCommand(`git status`, output, GitPushOutputRegex, exitCode), expectedMap, openerService), undefined); + }); + }); + suite('returns actions when', () => { + test('expected unix exit code', () => { + assertMatchOptions(getQuickFixesForCommand(createCommand(command, output, GitPushOutputRegex, exitCode), expectedMap, openerService)?.fixes, actions); + }); + test('matching exit status', () => { + assertMatchOptions(getQuickFixesForCommand(createCommand(command, output, GitPushOutputRegex, 2), expectedMap, openerService)?.fixes, actions); + }); + }); + }); + suite('gitCreatePr', () => { + const expectedMap = new Map(); + const command = `git push`; + const output = `Total 0 (delta 0), reused 0 (delta 0), pack-reused 0 + remote: + remote: Create a pull request for 'test22' on GitHub by visiting: + remote: https://github.com/meganrogge/xterm.js/pull/new/test22 + remote: + To https://github.com/meganrogge/xterm.js + * [new branch] test22 -> test22 + Branch 'test22' set up to track remote branch 'test22' from 'origin'. `; + const exitCode = 0; + const actions = [{ + id: `quickFix.opener`, + enabled: true, + label: 'Open: https://github.com/meganrogge/xterm.js/pull/new/test22', + tooltip: 'Open: https://github.com/meganrogge/xterm.js/pull/new/test22', + uri: URI.parse('https://github.com/meganrogge/xterm.js/pull/new/test22') + }]; + setup(() => { + const command = gitCreatePr(); + expectedMap.set(command.commandLineMatcher.toString(), [command]); + quickFixAddon.registerCommandFinishedListener(command); + }); + suite('returns undefined when', () => { + test('output does not match', () => { + strictEqual(getQuickFixesForCommand(createCommand(command, `invalid output`, GitCreatePrOutputRegex, exitCode), expectedMap, openerService), undefined); + }); + test('command does not match', () => { + strictEqual(getQuickFixesForCommand(createCommand(`git status`, output, GitCreatePrOutputRegex, exitCode), expectedMap, openerService), undefined); + }); + test('failure exit status', () => { + strictEqual(getQuickFixesForCommand(createCommand(command, output, GitCreatePrOutputRegex, 2), expectedMap, openerService), undefined); + }); + }); + suite('returns actions when', () => { + test('expected unix exit code', () => { + assertMatchOptions(getQuickFixesForCommand(createCommand(command, output, GitCreatePrOutputRegex, exitCode), expectedMap, openerService)?.fixes, actions); + }); + }); + }); + }); + suite('gitPush - multiple providers', () => { + const expectedMap = new Map(); + const command = `git push`; + const output = `fatal: The current branch test22 has no upstream branch. + To push the current branch and set the remote as upstream, use + + git push --set-upstream origin test22`; + const exitCode = 128; + const actions = [{ + id: `quickFix.command`, + enabled: true, + label: 'Run: git push --set-upstream origin test22', + tooltip: 'Run: git push --set-upstream origin test22', + command: 'git push --set-upstream origin test22' + }]; + setup(() => { + const pushCommand = gitPushSetUpstream(); + const prCommand = gitCreatePr(); + quickFixAddon.registerCommandFinishedListener(pushCommand); + quickFixAddon.registerCommandFinishedListener(prCommand); + expectedMap.set(pushCommand.commandLineMatcher.toString(), [pushCommand, prCommand]); + }); + suite('returns undefined when', () => { + test('output does not match', () => { + strictEqual(getQuickFixesForCommand(createCommand(command, `invalid output`, GitPushOutputRegex, exitCode), expectedMap, openerService), undefined); + }); + test('command does not match', () => { + strictEqual(getQuickFixesForCommand(createCommand(`git status`, output, GitPushOutputRegex, exitCode), expectedMap, openerService), undefined); + }); + }); + suite('returns actions when', () => { + test('expected unix exit code', () => { + assertMatchOptions(getQuickFixesForCommand(createCommand(command, output, GitPushOutputRegex, exitCode), expectedMap, openerService)?.fixes, actions); + }); + test('matching exit status', () => { + assertMatchOptions(getQuickFixesForCommand(createCommand(command, output, GitPushOutputRegex, 2), expectedMap, openerService)?.fixes, actions); + }); + }); + }); +}); + +function createCommand(command: string, output: string, outputMatcher?: RegExp | string, exitCode?: number): ITerminalCommand { + return { + command, + exitCode, + getOutput: () => { return output; }, + getOutputMatch: (matcher: ITerminalOutputMatcher) => { + if (outputMatcher) { + return output.match(outputMatcher) ?? undefined; + } + return undefined; + }, + timestamp: Date.now(), + hasOutput: () => !!output + }; +} + +type TestAction = Pick & { command?: string; uri?: URI }; +function assertMatchOptions(actual: TestAction[] | undefined, expected: TestAction[]): void { + strictEqual(actual?.length, expected.length); + let index = 0; + for (const i of actual) { + const j = expected[index]; + strictEqual(i.id, j.id, `ID`); + strictEqual(i.enabled, j.enabled, `enabled`); + strictEqual(i.label, j.label, `label`); + strictEqual(i.tooltip, j.tooltip, `tooltip`); + if (j.command) { + strictEqual(i.command, j.command); + } + if (j.uri) { + strictEqual(i.uri!.toString(), j.uri.toString()); + } + index++; + } +} diff --git a/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts b/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts index f30df87be77..905b968d753 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts @@ -45,7 +45,7 @@ suite('ShellIntegrationAddon', () => { xterm = new Terminal({ allowProposedApi: true, cols: 80, rows: 30 }); const instantiationService = new TestInstantiationService(); instantiationService.stub(ILogService, NullLogService); - shellIntegrationAddon = instantiationService.createInstance(TestShellIntegrationAddon); + shellIntegrationAddon = instantiationService.createInstance(TestShellIntegrationAddon, true, undefined); xterm.loadAddon(shellIntegrationAddon); capabilities = shellIntegrationAddon.capabilities; }); diff --git a/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts b/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts index d0929417060..b20d910337b 100644 --- a/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts +++ b/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts @@ -18,6 +18,7 @@ import { InternalTestItem, TestDiffOpType, TestItemExpandState, TestResultState, import { TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResult'; import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; import { ITestService } from 'vs/workbench/contrib/testing/common/testService'; +import { TestId } from 'vs/workbench/contrib/testing/common/testId'; const computedStateAccessor: IComputedStateAndDurationAccessor = { getOwnState: i => i instanceof TestItemTreeElement ? i.ownState : TestResultState.Unset, @@ -212,7 +213,8 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes } protected createItem(item: InternalTestItem): ByLocationTestItemElement { - const parent = item.parent ? this.items.get(item.parent)! : null; + const parentId = TestId.parentId(item.item.extId); + const parent = parentId ? this.items.get(parentId)! : null; return new ByLocationTestItemElement(item, parent, n => this.changes.addedOrRemoved(n)); } diff --git a/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByName.ts b/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByName.ts index cd34647d6d0..eeedf8de3d4 100644 --- a/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByName.ts +++ b/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByName.ts @@ -12,6 +12,7 @@ import { NodeRenderDirective } from 'vs/workbench/contrib/testing/browser/explor import { InternalTestItem } from 'vs/workbench/contrib/testing/common/testTypes'; import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; import { ITestService } from 'vs/workbench/contrib/testing/common/testService'; +import { TestId } from 'vs/workbench/contrib/testing/common/testId'; /** * Type of test element in the list. @@ -97,7 +98,8 @@ export class HierarchicalByNameProjection extends HierarchicalByLocationProjecti * @override */ protected override createItem(item: InternalTestItem): ByLocationTestItemElement { - const actualParent = item.parent ? this.items.get(item.parent) as ByNameTestItemElement : undefined; + const parentId = TestId.parentId(item.item.extId); + const actualParent = parentId ? this.items.get(parentId.toString()) as ByNameTestItemElement : undefined; if (!actualParent) { return new ByNameTestItemElement(item, null, r => this.changes.addedOrRemoved(r)); } diff --git a/src/vs/workbench/contrib/testing/browser/media/testing.css b/src/vs/workbench/contrib/testing/browser/media/testing.css index 504ecb13ac7..2b6cbe6dbb2 100644 --- a/src/vs/workbench/contrib/testing/browser/media/testing.css +++ b/src/vs/workbench/contrib/testing/browser/media/testing.css @@ -43,8 +43,8 @@ .test-explorer .monaco-list-row:hover .monaco-action-bar, .test-output-peek-tree .monaco-list-row:hover .monaco-action-bar, -.test-explorer .monaco-list-row:focus .monaco-action-bar, -.test-output-peek-tree .monaco-list-row:focus .monaco-action-bar { +.test-explorer .monaco-list-row.focused .monaco-action-bar, +.test-output-peek-tree .monaco-list-row.focused .monaco-action-bar { display: initial; } @@ -54,7 +54,7 @@ } .test-explorer .monaco-list-row:hover .codicon-testing-hidden, -.test-explorer .monaco-list-row:focus .codicon-testing-hidden { +.test-explorer .monaco-list-row.focused .codicon-testing-hidden { display: none; } diff --git a/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts b/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts index 53579678c41..6e1f48fa599 100644 --- a/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts +++ b/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts @@ -21,7 +21,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { ViewAction } from 'vs/workbench/browser/parts/views/viewPane'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { FocusedViewContext } from 'vs/workbench/common/contextkeys'; import { ViewContainerLocation } from 'vs/workbench/common/views'; import { IExtensionsViewPaneContainer, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; @@ -40,8 +40,10 @@ import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResu import { expandAndGetTestById, IMainThreadTestCollection, ITestService, testsInFile } from 'vs/workbench/contrib/testing/common/testService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; +import { getTestingConfiguration, TestingConfigKeys } from 'vs/workbench/contrib/testing/common/configuration'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -const category = CATEGORIES.Test; +const category = Categories.Test; const enum ActionOrder { // Navigation: @@ -679,9 +681,15 @@ abstract class ExecuteTestAtCursor extends Action2 { * @override */ public async run(accessor: ServicesAccessor) { - const control = accessor.get(IEditorService).activeTextEditorControl; - const position = control?.getPosition(); - const model = control?.getModel(); + const editorService = accessor.get(IEditorService); + const activeEditorPane = editorService.activeEditorPane; + const activeControl = editorService.activeTextEditorControl; + if (!activeEditorPane || !activeControl) { + return; + } + + const position = activeControl?.getPosition(); + const model = activeControl?.getModel(); if (!position || !model || !('uri' in model)) { return; } @@ -689,6 +697,8 @@ abstract class ExecuteTestAtCursor extends Action2 { const testService = accessor.get(ITestService); const profileService = accessor.get(ITestProfileService); const uriIdentityService = accessor.get(IUriIdentityService); + const progressService = accessor.get(IProgressService); + const configurationService = accessor.get(IConfigurationService); let bestNodes: InternalTestItem[] = []; let bestRange: Range | undefined; @@ -696,6 +706,12 @@ abstract class ExecuteTestAtCursor extends Action2 { let bestNodesBefore: InternalTestItem[] = []; let bestRangeBefore: Range | undefined; + const saveBeforeTest = getTestingConfiguration(configurationService, TestingConfigKeys.SaveBeforeTest); + if (saveBeforeTest) { + await editorService.save({ editor: activeEditorPane.input, groupId: activeEditorPane.group.id }); + await testService.syncTests(); + } + // testsInFile will descend in the test tree. We assume that as we go // deeper, ranges get more specific. We'll want to run all tests whose // range is equal to the most specific range we find (see #133519) @@ -703,8 +719,8 @@ abstract class ExecuteTestAtCursor extends Action2 { // If we don't find any test whose range contains the position, we pick // the closest one before the position. Again, if we find several tests // whose range is equal to the closest one, we run them all. - await showDiscoveringWhile(accessor.get(IProgressService), (async () => { - for await (const test of testsInFile(testService.collection, uriIdentityService, model.uri)) { + await showDiscoveringWhile(progressService, (async () => { + for await (const test of testsInFile(testService, uriIdentityService, model.uri)) { if (!test.item.range || !(profileService.capabilitiesForTest(test) & this.group)) { continue; } diff --git a/src/vs/workbench/contrib/testing/browser/testing.contribution.ts b/src/vs/workbench/contrib/testing/browser/testing.contribution.ts index 087f7555cff..828affe2faf 100644 --- a/src/vs/workbench/contrib/testing/browser/testing.contribution.ts +++ b/src/vs/workbench/contrib/testing/browser/testing.contribution.ts @@ -11,7 +11,7 @@ import { Extensions as ConfigurationExtensions, IConfigurationRegistry } from 'v import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IFileService } from 'vs/platform/files/common/files'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IProgressService } from 'vs/platform/progress/common/progress'; @@ -44,15 +44,15 @@ import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle import { allTestActions, discoverAndRunTests } from './testExplorerActions'; import './testingConfigurationUi'; -registerSingleton(ITestService, TestService, true); -registerSingleton(ITestResultStorage, TestResultStorage, true); -registerSingleton(ITestProfileService, TestProfileService, true); -registerSingleton(ITestResultService, TestResultService, true); -registerSingleton(ITestExplorerFilterState, TestExplorerFilterState, true); -registerSingleton(ITestingOutputTerminalService, TestingOutputTerminalService, true); -registerSingleton(ITestingPeekOpener, TestingPeekOpener, true); -registerSingleton(ITestingProgressUiService, TestingProgressUiService, true); -registerSingleton(ITestingDecorationsService, TestingDecorationService, true); +registerSingleton(ITestService, TestService, InstantiationType.Delayed); +registerSingleton(ITestResultStorage, TestResultStorage, InstantiationType.Delayed); +registerSingleton(ITestProfileService, TestProfileService, InstantiationType.Delayed); +registerSingleton(ITestResultService, TestResultService, InstantiationType.Delayed); +registerSingleton(ITestExplorerFilterState, TestExplorerFilterState, InstantiationType.Delayed); +registerSingleton(ITestingOutputTerminalService, TestingOutputTerminalService, InstantiationType.Delayed); +registerSingleton(ITestingPeekOpener, TestingPeekOpener, InstantiationType.Delayed); +registerSingleton(ITestingProgressUiService, TestingProgressUiService, InstantiationType.Delayed); +registerSingleton(ITestingDecorationsService, TestingDecorationService, InstantiationType.Delayed); const viewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: Testing.ViewletId, @@ -102,9 +102,9 @@ registerAction2(GoToNextMessageAction); registerAction2(CloseTestPeek); registerAction2(ToggleTestingPeekHistory); -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TestingContentProvider, 'TestingContentProvider', LifecyclePhase.Restored); -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TestingPeekOpener, 'TestingPeekOpener', LifecyclePhase.Eventually); -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TestingProgressTrigger, 'TestingProgressTrigger', LifecyclePhase.Eventually); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TestingContentProvider, LifecyclePhase.Restored); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TestingPeekOpener, LifecyclePhase.Eventually); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TestingProgressTrigger, LifecyclePhase.Eventually); registerEditorContribution(Testing.OutputPeekContributionId, TestingOutputPeekController); registerEditorContribution(Testing.DecorationsContributionId, TestingDecorations); diff --git a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts index 7287bcccafa..9d11b2304fa 100644 --- a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts +++ b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts @@ -10,6 +10,8 @@ import { equals } from 'vs/base/common/arrays'; import { RunOnceScheduler } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent'; +import { stripIcons } from 'vs/base/common/iconLabels'; +import { Iterable } from 'vs/base/common/iterator'; import { Disposable, DisposableStore, IReference, MutableDisposable } from 'vs/base/common/lifecycle'; import { ResourceMap } from 'vs/base/common/map'; import { Constants } from 'vs/base/common/uint'; @@ -39,7 +41,7 @@ import { testingRunAllIcon, testingRunIcon, testingStatesToIcons } from 'vs/work import { testMessageSeverityColors } from 'vs/workbench/contrib/testing/browser/theme'; import { DefaultGutterClickAction, getTestingConfiguration, TestingConfigKeys } from 'vs/workbench/contrib/testing/common/configuration'; import { labelForTestInState, Testing } from 'vs/workbench/contrib/testing/common/constants'; -import { IncrementalTestCollectionItem, InternalTestItem, IRichLocation, ITestMessage, ITestRunProfile, TestDiffOpType, TestMessageType, TestResultItem, TestResultState, TestRunProfileBitset } from 'vs/workbench/contrib/testing/common/testTypes'; +import { TestId } from 'vs/workbench/contrib/testing/common/testId'; import { ITestDecoration as IPublicTestDecoration, ITestingDecorationsService, TestDecorations } from 'vs/workbench/contrib/testing/common/testingDecorations'; import { ITestingPeekOpener } from 'vs/workbench/contrib/testing/common/testingPeekOpener'; import { isFailedState, maxPriority } from 'vs/workbench/contrib/testing/common/testingStates'; @@ -48,7 +50,7 @@ import { ITestProfileService } from 'vs/workbench/contrib/testing/common/testPro import { LiveTestResult } from 'vs/workbench/contrib/testing/common/testResult'; import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; import { getContextForTestItem, ITestService, testsInFile } from 'vs/workbench/contrib/testing/common/testService'; -import { stripIcons } from 'vs/base/common/iconLabels'; +import { IncrementalTestCollectionItem, InternalTestItem, IRichLocation, ITestMessage, ITestRunProfile, TestDiffOpType, TestMessageType, TestResultItem, TestResultState, TestRunProfileBitset } from 'vs/workbench/contrib/testing/common/testTypes'; const MAX_INLINE_MESSAGE_LENGTH = 128; @@ -79,7 +81,8 @@ export class TestingDecorationService extends Disposable implements ITestingDeco rangeUpdateVersionId?: number; /** Counter for the results rendered in the document */ generation: number; - value: TestDecorations; + value: readonly ITestDecoration[]; + map: ReadonlyMap; }>(); /** @@ -150,15 +153,15 @@ export class TestingDecorationService extends Disposable implements ITestingDeco } /** @inheritdoc */ - public syncDecorations(resource: URI): TestDecorations { + public syncDecorations(resource: URI): ReadonlyMap { const model = this.modelService.getModel(resource); if (!model) { - return new TestDecorations(); + return new Map(); } const cached = this.decorationCache.get(resource); if (cached && cached.generation === this.generation && (cached.rangeUpdateVersionId === undefined || cached.rangeUpdateVersionId !== model.getVersionId())) { - return cached.value; + return cached.map; } return this.applyDecorations(model); @@ -171,7 +174,7 @@ export class TestingDecorationService extends Disposable implements ITestingDeco return undefined; } - const decoration = this.syncDecorations(resource).value.find(v => v instanceof RunTestDecoration && v.isForTest(testId)); + const decoration = Iterable.find(this.syncDecorations(resource).values(), v => v instanceof RunTestDecoration && v.isForTest(testId)); if (!decoration) { return undefined; } @@ -192,10 +195,10 @@ export class TestingDecorationService extends Disposable implements ITestingDeco const uriStr = model.uri.toString(); const cached = this.decorationCache.get(model.uri); const testRangesUpdated = cached?.rangeUpdateVersionId === model.getVersionId(); - const lastDecorations = cached?.value ?? new TestDecorations(); - const newDecorations = new TestDecorations(); + const lastDecorations = cached?.value ?? []; - model.changeDecorations(accessor => { + const map = model.changeDecorations(accessor => { + const newDecorations: ITestDecoration[] = []; const runDecorations = new TestDecorations<{ line: number; id: ''; test: IncrementalTestCollectionItem; resultItem: TestResultItem | undefined }>(); for (const test of this.testService.collection.all) { if (!test.item.range || test.item.uri?.toString() !== uriStr) { @@ -209,7 +212,7 @@ export class TestingDecorationService extends Disposable implements ITestingDeco for (const [line, tests] of runDecorations.lines()) { const multi = tests.length > 1; - let existing = lastDecorations.value.find(d => d instanceof RunTestDecoration && d.exactlyContainsTests(tests)) as RunTestDecoration | undefined; + let existing = lastDecorations.find(d => d instanceof RunTestDecoration && d.exactlyContainsTests(tests)) as RunTestDecoration | undefined; // see comment in the constructor for what's going on here if (existing && testRangesUpdated && model.getDecorationRange(existing.id)?.startLineNumber !== line) { @@ -233,14 +236,14 @@ export class TestingDecorationService extends Disposable implements ITestingDeco for (const task of lastResult.tasks) { for (const m of task.otherMessages) { if (!this.invalidatedMessages.has(m) && m.location?.uri.toString() === uriStr) { - const decoration = lastDecorations.findOnLine(m.location.range.startLineNumber, l => l instanceof TestMessageDecoration && l.testMessage === m) + const decoration = lastDecorations.find(l => l instanceof TestMessageDecoration && l.testMessage === m) || this.instantiationService.createInstance(TestMessageDecoration, m, undefined, model); newDecorations.push(decoration); } } } - const messageLines = new Set(); + const messageLines = new Map(); for (const test of lastResult.tests) { for (let taskId = 0; taskId < test.tasks.length; taskId++) { const state = test.tasks[taskId]; @@ -253,14 +256,17 @@ export class TestingDecorationService extends Disposable implements ITestingDeco // Only add one message per line number. Overlapping messages // don't appear well, and the peek will show all of them (#134129) const line = m.location.range.startLineNumber; + let index: number; if (messageLines.has(line)) { - continue; + index = messageLines.get(line)!; + } else { + index = newDecorations.length; + messageLines.set(line, index); } - messageLines.add(line); - const previous = lastDecorations.findOnLine(line, l => l instanceof TestMessageDecoration && l.testMessage === m); + const previous = lastDecorations.find(l => l instanceof TestMessageDecoration && l.testMessage === m); if (previous) { - newDecorations.push(previous); + newDecorations[index] = previous; continue; } @@ -279,7 +285,7 @@ export class TestingDecorationService extends Disposable implements ITestingDeco } const saveFromRemoval = new Set(); - for (const decoration of newDecorations.value) { + for (const decoration of newDecorations) { if (decoration.id === '') { decoration.id = accessor.addDecoration(decoration.editorDecoration.range, decoration.editorDecoration.options); } else { @@ -287,20 +293,24 @@ export class TestingDecorationService extends Disposable implements ITestingDeco } } - for (const decoration of lastDecorations.value) { + for (const decoration of lastDecorations) { if (!saveFromRemoval.has(decoration.id)) { accessor.removeDecoration(decoration.id); } } + const map = new Map(newDecorations.map(d => [d.id, d])); this.decorationCache.set(model.uri, { generation: this.generation, rangeUpdateVersionId: cached?.rangeUpdateVersionId, value: newDecorations, + map, }); + + return map; }); - return newDecorations; + return map || new Map(); } } @@ -338,8 +348,8 @@ export class TestingDecorations extends Disposable implements IEditorContributio if (e.target.position && this.currentUri) { const modelDecorations = editor.getModel()?.getDecorationsInRange(Range.fromPositions(e.target.position)) ?? []; for (const { id } of modelDecorations) { - const cache = decorations.syncDecorations(this.currentUri) as TestDecorations; - if (cache.get(id)?.click(e)) { + const cache = decorations.syncDecorations(this.currentUri); + if ((cache.get(id) as ITestDecoration | undefined)?.click(e)) { e.event.stopPropagation(); return; } @@ -404,7 +414,7 @@ export class TestingDecorations extends Disposable implements IEditorContributio this.decorations.syncDecorations(uri); (async () => { - for await (const _test of testsInFile(this.testService.collection, this.uriIdentityService, uri)) { + for await (const _test of testsInFile(this.testService, this.uriIdentityService, uri, false)) { // consume the iterator so that all tests in the file get expanded. Or // at least until the URI changes. If new items are requested, changes // will be trigged in the `onDidProcessDiff` callback. @@ -840,7 +850,7 @@ class MultiRunTestDecoration extends RunTestDecoration implements ITestDecoratio const testItems = this.tests.map(testItem => ({ currentLabel: testItem.test.item.label, testItem, - parent: testItem.test.parent, + parent: TestId.fromString(testItem.test.item.extId).parentId, })); const getLabelConflicts = (tests: typeof testItems) => { @@ -856,9 +866,9 @@ class MultiRunTestDecoration extends RunTestDecoration implements ITestDecoratio while ((conflicts = getLabelConflicts(testItems)).length && hasParent) { for (const conflict of conflicts) { if (conflict.parent) { - const parent = this.testService.collection.getNodeById(conflict.parent); + const parent = this.testService.collection.getNodeById(conflict.parent.toString()); conflict.currentLabel = parent?.item.label + ' > ' + conflict.currentLabel; - conflict.parent = parent?.parent ? parent.parent : null; + conflict.parent = conflict.parent.parentId; } else { hasParent = false; } diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts index 23ced5391da..f2de0dbdc97 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts @@ -10,20 +10,19 @@ import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; import { Action, IAction, IActionRunner, Separator } from 'vs/base/common/actions'; import { Delayer } from 'vs/base/common/async'; +import { Emitter } from 'vs/base/common/event'; import { Iterable } from 'vs/base/common/iterator'; import { localize } from 'vs/nls'; -import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { attachSuggestEnabledInputBoxStyler, ContextScopedSuggestEnabledInputWithHistory, SuggestEnabledInputWithHistory, SuggestResultsProvider } from 'vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput'; import { testingFilterIcon } from 'vs/workbench/contrib/testing/browser/icons'; -import { TestCommandId } from 'vs/workbench/contrib/testing/common/constants'; import { StoredValue } from 'vs/workbench/contrib/testing/common/storedValue'; -import { denamespaceTestTag } from 'vs/workbench/contrib/testing/common/testTypes'; import { ITestExplorerFilterState, TestFilterTerm } from 'vs/workbench/contrib/testing/common/testExplorerFilterState'; import { ITestService } from 'vs/workbench/contrib/testing/common/testService'; +import { denamespaceTestTag } from 'vs/workbench/contrib/testing/common/testTypes'; const testFilterDescriptions: { [K in TestFilterTerm]: string } = { [TestFilterTerm.Failed]: localize('testing.filters.showOnlyFailed', "Show Only Failed Tests"), @@ -35,6 +34,8 @@ const testFilterDescriptions: { [K in TestFilterTerm]: string } = { export class TestingExplorerFilter extends BaseActionViewItem { private input!: SuggestEnabledInputWithHistory; private wrapper!: HTMLDivElement; + private readonly focusEmitter = this._register(new Emitter()); + public readonly onDidFocus = this.focusEmitter.event; private readonly history: StoredValue<{ values: string[]; lastValue: string } | string[]> = this.instantiationService.createInstance(StoredValue, { key: 'testing.filterHistory2', scope: StorageScope.WORKSPACE, @@ -111,6 +112,10 @@ export class TestingExplorerFilter extends BaseActionViewItem { input.focus(); })); + this._register(input.onDidFocus(() => { + this.focusEmitter.fire(); + })); + this._register(input.onInputDidChange(() => updateDelayer.trigger(() => { input.addToHistory(); this.state.setText(input.getValue()); @@ -242,13 +247,3 @@ class FiltersDropdownMenuActionViewItem extends DropdownMenuActionViewItem { this.element!.classList.toggle('checked', this._action.checked); } } - -registerAction2(class extends Action2 { - constructor() { - super({ - id: TestCommandId.FilterAction, - title: { value: localize('filter', "Filter"), original: 'Filter' }, - }); - } - async run(): Promise { } -}); diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts index 60a5566ddb0..6f3b60c0027 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts @@ -46,6 +46,7 @@ import { IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platfor import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { ViewPane } from 'vs/workbench/browser/parts/views/viewPane'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; +import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { HierarchicalByLocationProjection } from 'vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation'; import { ByNameTestItemElement, HierarchicalByNameProjection } from 'vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByName'; @@ -69,14 +70,21 @@ import { IMainThreadTestCollection, ITestService, testCollectionIsEmpty } from ' import { InternalTestItem, ITestRunProfile, TestItemExpandState, TestResultState, TestRunProfileBitset } from 'vs/workbench/contrib/testing/common/testTypes'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +const enum LastFocusState { + Input, + Tree, +} + export class TestingExplorerView extends ViewPane { public viewModel!: TestingExplorerViewModel; private filterActionBar = this._register(new MutableDisposable()); private container!: HTMLElement; private treeHeader!: HTMLElement; private discoveryProgress = this._register(new MutableDisposable()); - private filter?: TestingExplorerFilter; + private readonly filter = this._register(new MutableDisposable()); + private readonly filterFocusListener = this._register(new MutableDisposable()); private readonly dimensions = { width: 0, height: 0 }; + private lastFocusState = LastFocusState.Input; constructor( options: IViewletViewOptions, @@ -110,13 +118,19 @@ export class TestingExplorerView extends ViewPane { this._register(testProfileService.onDidChange(() => this.updateActions())); } - /** - * @override - */ public override shouldShowWelcome() { return this.viewModel?.welcomeExperience === WelcomeExperience.ForWorkspace ?? true; } + public override focus() { + super.focus(); + if (this.lastFocusState === LastFocusState.Tree) { + this.viewModel.tree.domFocus(); + } else { + this.filter.value?.focus(); + } + } + public getSelectedOrVisibleItems(profile?: ITestRunProfile) { const projection = this.viewModel.projection.value; if (!projection) { @@ -226,6 +240,7 @@ export class TestingExplorerView extends ViewPane { const listContainer = dom.append(this.container, dom.$('.test-explorer-tree')); this.viewModel = this.instantiationService.createInstance(TestingExplorerViewModel, listContainer, this.onDidChangeBodyVisibility); + this._register(this.viewModel.tree.onDidFocus(() => this.lastFocusState = LastFocusState.Tree)); this._register(this.viewModel.onChangeWelcomeVisibility(() => this._onDidChangeViewWelcomeState.fire())); this._register(this.viewModel); this._onDidChangeViewWelcomeState.fire(); @@ -235,7 +250,9 @@ export class TestingExplorerView extends ViewPane { public override getActionViewItem(action: IAction): IActionViewItem | undefined { switch (action.id) { case TestCommandId.FilterAction: - return this.filter = this.instantiationService.createInstance(TestingExplorerFilter, action); + this.filter.value = this.instantiationService.createInstance(TestingExplorerFilter, action); + this.filterFocusListener.value = this.filter.value.onDidFocus(() => this.lastFocusState = LastFocusState.Input); + return this.filter.value; case TestCommandId.RunSelectedAction: return this.getRunGroupDropdown(TestRunProfileBitset.Run, action); case TestCommandId.DebugSelectedAction: @@ -321,7 +338,7 @@ export class TestingExplorerView extends ViewPane { * @override */ public override saveState() { - this.filter?.saveState(); + this.filter.value?.saveState(); super.saveState(); } @@ -377,7 +394,7 @@ export class TestingExplorerView extends ViewPane { this.dimensions.width = width; this.container.style.height = `${height}px`; this.viewModel.layout(height - this.treeHeader.clientHeight, width); - this.filter?.layout(width); + this.filter.value?.layout(width); } } @@ -586,7 +603,12 @@ export class TestingExplorerViewModel extends Disposable { })); const onEditorChange = () => { - this.filter.filterToDocumentUri(editorService.activeEditor?.resource); + if (editorService.activeEditor instanceof DiffEditorInput) { + this.filter.filterToDocumentUri(editorService.activeEditor.primary.resource); + } else { + this.filter.filterToDocumentUri(editorService.activeEditor?.resource); + } + if (this.filterState.isFilteringFor(TestFilterTerm.CurrentDoc)) { this.tree.refilter(); } @@ -783,6 +805,8 @@ export class TestingExplorerViewModel extends Disposable { this.reevaluateWelcomeState(); this.projection.value?.applyTo(this.tree); + this.tree.refilter(); + if (this.hasPendingReveal) { this.revealById(this.filterState.reveal.value); } diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index bed3955d83c..fb9b48fd009 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -55,7 +55,7 @@ import { WorkbenchCompressibleObjectTree } from 'vs/platform/list/browser/listSe import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { textLinkActiveForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; import { IColorTheme, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { EditorModel } from 'vs/workbench/common/editor/editorModel'; import { flatTestItemDelimiter } from 'vs/workbench/contrib/testing/browser/explorerProjections/display'; import { getTestItemContextOverlay } from 'vs/workbench/contrib/testing/browser/explorerProjections/testItemContextOverlay'; @@ -1166,6 +1166,7 @@ class TestMessageElement implements ITreeElement { public readonly uri: URI; public readonly location?: IRichLocation; public readonly description?: string; + public readonly marker?: number; constructor( public readonly result: ITestResult, @@ -1173,9 +1174,10 @@ class TestMessageElement implements ITreeElement { public readonly taskIndex: number, public readonly messageIndex: number, ) { - const { message, location } = test.tasks[taskIndex].messages[messageIndex]; + const m = test.tasks[taskIndex].messages[messageIndex]; - this.location = location; + this.location = m.location; + this.marker = m.type === TestMessageType.Output ? m.marker : undefined; this.uri = this.context = buildTestUri({ type: TestUriType.ResultMessage, messageIndex, @@ -1186,7 +1188,7 @@ class TestMessageElement implements ITreeElement { this.id = this.uri.toString(); - const asPlaintext = renderStringAsPlaintext(message); + const asPlaintext = renderStringAsPlaintext(m.message); const lines = count(asPlaintext.trimRight(), '\n'); this.label = firstLine(asPlaintext); if (lines > 0) { @@ -1597,6 +1599,18 @@ class TreeActionsProvider { } } + if (element instanceof TestMessageElement) { + if (element.marker !== undefined) { + primary.push(new Action( + 'testing.outputPeek.showMessageInTerminal', + localize('testing.showMessageInTerminal', "Show Output in Terminal"), + Codicon.terminal.classNames, + undefined, + () => this.testTerminalService.open(element.result, element.marker), + )); + } + } + const result = { primary, secondary }; createAndFillInActionBarActions(menu, { shouldForwardArgs: true, @@ -1672,7 +1686,7 @@ export class GoToNextMessageAction extends EditorAction2 { f1: true, title: { value: localize('testing.goToNextMessage', "Go to Next Test Failure"), original: 'Go to Next Test Failure' }, icon: Codicon.arrowDown, - category: CATEGORIES.Test, + category: Categories.Test, keybinding: { primary: KeyMod.Alt | KeyCode.F8, weight: KeybindingWeight.EditorContrib + 1, @@ -1702,7 +1716,7 @@ export class GoToPreviousMessageAction extends EditorAction2 { f1: true, title: { value: localize('testing.goToPreviousMessage', "Go to Previous Test Failure"), original: 'Go to Previous Test Failure' }, icon: Codicon.arrowUp, - category: CATEGORIES.Test, + category: Categories.Test, keybinding: { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.F8, weight: KeybindingWeight.EditorContrib + 1, @@ -1732,7 +1746,7 @@ export class OpenMessageInEditorAction extends EditorAction2 { f1: false, title: { value: localize('testing.openMessageInEditor', "Open in Editor"), original: 'Open in Editor' }, icon: Codicon.linkExternal, - category: CATEGORIES.Test, + category: Categories.Test, menu: [{ id: MenuId.TestPeekTitle }], }); } @@ -1750,7 +1764,7 @@ export class ToggleTestingPeekHistory extends EditorAction2 { f1: true, title: { value: localize('testing.toggleTestingPeekHistory', "Toggle Test History in Peek"), original: 'Toggle Test History in Peek' }, icon: Codicon.history, - category: CATEGORIES.Test, + category: Categories.Test, menu: [{ id: MenuId.TestPeekTitle, group: 'navigation', diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputTerminalService.ts b/src/vs/workbench/contrib/testing/browser/testingOutputTerminalService.ts index 70f9d91907a..1786fe37813 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputTerminalService.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputTerminalService.ts @@ -17,15 +17,17 @@ import { TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal' import { testingViewIcon } from 'vs/workbench/contrib/testing/browser/icons'; import { ITestResult } from 'vs/workbench/contrib/testing/common/testResult'; import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; +import { getMarkId } from 'vs/workbench/contrib/testing/common/testTypes'; export interface ITestingOutputTerminalService { _serviceBrand: undefined; /** - * Opens a terminal for the given test's output. + * Opens a terminal for the given test's output. Optionally, scrolls to and + * selects the given marker in the test results. */ - open(result: ITestResult): Promise; + open(result: ITestResult, marker?: number): Promise; } const friendlyDate = (date: number) => { @@ -78,7 +80,7 @@ export class TestingOutputTerminalService implements ITestingOutputTerminalServi /** * @inheritdoc */ - public async open(result: ITestResult | undefined): Promise { + public async open(result: ITestResult | undefined, marker?: number): Promise { const testOutputPtys = this.terminalService.instances .map(t => { const output = this.outputTerminals.get(t); @@ -95,6 +97,8 @@ export class TestingOutputTerminalService implements ITestingOutputTerminalServi } else { this.terminalGroupService.showPanel(); } + + this.revealMarker(existing[0], marker); return; } @@ -114,10 +118,10 @@ export class TestingOutputTerminalService implements ITestingOutputTerminalServi customPtyImplementation: () => output, name: getTitle(result), }, - }), output, result); + }), output, result, marker); } - private async showResultsInTerminal(terminal: ITerminalInstance, output: TestOutputProcess, result: ITestResult | undefined) { + private async showResultsInTerminal(terminal: ITerminalInstance, output: TestOutputProcess, result: ITestResult | undefined, thenSelectMarker?: number) { this.outputTerminals.set(terminal, output); output.resetFor(result?.id, getTitle(result)); this.terminalService.setActiveInstance(terminal); @@ -151,9 +155,16 @@ export class TestingOutputTerminalService implements ITestingOutputTerminalServi const text = localize('runFinished', 'Test run finished at {0}', completedAt.toLocaleString()); output.pushData(`\r\n\r\n\x1b[1m> ${text} <\x1b[0m\r\n\r\n`); output.ended = true; + this.revealMarker(terminal, thenSelectMarker); }, }); } + + private revealMarker(terminal: ITerminalInstance, marker?: number) { + if (marker !== undefined) { + terminal.scrollToMark(getMarkId(marker, true), getMarkId(marker, false), true); + } + } } class TestOutputProcess extends Disposable implements ITerminalChildProcess { diff --git a/src/vs/workbench/contrib/testing/common/mainThreadTestCollection.ts b/src/vs/workbench/contrib/testing/common/mainThreadTestCollection.ts index f8b3c45493a..4ac746110c3 100644 --- a/src/vs/workbench/contrib/testing/common/mainThreadTestCollection.ts +++ b/src/vs/workbench/contrib/testing/common/mainThreadTestCollection.ts @@ -89,11 +89,11 @@ export class MainThreadTestCollection extends AbstractIncrementalTestCollection< for (const child of queue.pop()!) { const item = this.items.get(child)!; ops.push({ - op: TestDiffOpType.Add, item: { + op: TestDiffOpType.Add, + item: { controllerId: item.controllerId, expand: item.expand, item: item.item, - parent: item.parent, } }); queue.push(item.children); diff --git a/src/vs/workbench/contrib/testing/common/testId.ts b/src/vs/workbench/contrib/testing/common/testId.ts index a5b5323aff9..58b9af85589 100644 --- a/src/vs/workbench/contrib/testing/common/testId.ts +++ b/src/vs/workbench/contrib/testing/common/testId.ts @@ -23,7 +23,7 @@ export const enum TestPosition { IsParent, } -type TestItemLike = { id: string; parent?: TestItemLike }; +type TestItemLike = { id: string; parent?: TestItemLike; _isRoot?: boolean }; /** * The test ID is a stringifiable client that @@ -35,7 +35,7 @@ export class TestId { * Creates a test ID from an ext host test item. */ public static fromExtHostTestItem(item: TestItemLike, rootId: string, parent = item.parent) { - if (item.id === rootId) { + if (item._isRoot) { return new TestId([rootId]); } @@ -56,7 +56,7 @@ export class TestId { } /** - * Cheaply ets whether the ID refers to the root . + * Cheaply gets whether the ID refers to the root . */ public static root(idString: string) { const idx = idString.indexOf(TestIdPathParts.Delimiter); @@ -84,6 +84,14 @@ export class TestId { return base.toString() + TestIdPathParts.Delimiter + b; } + /** + * Cheaply gets the parent ID of a test identified with the string. + */ + public static parentId(idString: string) { + const idx = idString.lastIndexOf(TestIdPathParts.Delimiter); + return idx === -1 ? undefined : idString.slice(0, idx); + } + /** * Compares the position of the two ID strings. */ @@ -115,8 +123,8 @@ export class TestId { /** * Gets the ID of the parent test. */ - public get parentId(): TestId { - return this.viewEnd > 1 ? new TestId(this.path, this.viewEnd - 1) : this; + public get parentId(): TestId | undefined { + return this.viewEnd > 1 ? new TestId(this.path, this.viewEnd - 1) : undefined; } /** @@ -150,6 +158,16 @@ export class TestId { } } + /** + * Returns an iterable that yields IDs of the current item up to the root + * item. + */ + public *idsToRoot() { + for (let i = this.viewEnd; i > 0; i--) { + yield new TestId(this.path, i); + } + } + /** * Compares the other test ID with this one. */ diff --git a/src/vs/workbench/contrib/testing/common/testItemCollection.ts b/src/vs/workbench/contrib/testing/common/testItemCollection.ts index 34ef310d046..e255f1d068d 100644 --- a/src/vs/workbench/contrib/testing/common/testItemCollection.ts +++ b/src/vs/workbench/contrib/testing/common/testItemCollection.ts @@ -16,7 +16,6 @@ import { URI } from 'vs/base/common/uri'; */ interface CollectionItem { readonly fullId: TestId; - readonly parent: TestId | null; actual: T; expand: TestItemExpandState; /** @@ -351,7 +350,6 @@ export class TestItemCollection extends Disposable { internal = { fullId, actual, - parent: parent ? fullId.parentId : null, expandLevels: parent?.expandLevels /* intentionally undefined or 0 */ ? parent.expandLevels - 1 : undefined, expand: TestItemExpandState.NotExpandable, // updated by `connectItemAndChildren` }; @@ -362,7 +360,6 @@ export class TestItemCollection extends Disposable { this.pushDiff({ op: TestDiffOpType.Add, item: { - parent: internal.parent && internal.parent.toString(), controllerId: this.options.controllerId, expand: internal.expand, item: this.options.toITestItem(actual), diff --git a/src/vs/workbench/contrib/testing/common/testResult.ts b/src/vs/workbench/contrib/testing/common/testResult.ts index eb0114497e2..b84725e61d0 100644 --- a/src/vs/workbench/contrib/testing/common/testResult.ts +++ b/src/vs/workbench/contrib/testing/common/testResult.ts @@ -7,14 +7,14 @@ import { newWriteableBufferStream, VSBuffer, VSBufferReadableStream, VSBufferWri import { Emitter } from 'vs/base/common/event'; import { Lazy } from 'vs/base/common/lazy'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { IComputedStateAccessor, refreshComputedState } from 'vs/workbench/contrib/testing/common/getComputedState'; import { IObservableValue, MutableObservableValue, staticObservableValue } from 'vs/workbench/contrib/testing/common/observableValue'; -import { IRichLocation, ISerializedTestResults, ITestItem, ITestMessage, ITestOutputMessage, ITestRunTask, ITestTaskState, ResolvedTestRunRequest, TestItemExpandState, TestMessageType, TestResultItem, TestResultState } from 'vs/workbench/contrib/testing/common/testTypes'; +import { getMarkId, IRichLocation, ISerializedTestResults, ITestItem, ITestMessage, ITestOutputMessage, ITestRunTask, ITestTaskState, ResolvedTestRunRequest, TestItemExpandState, TestMessageType, TestResultItem, TestResultState } from 'vs/workbench/contrib/testing/common/testTypes'; import { TestCoverage } from 'vs/workbench/contrib/testing/common/testCoverage'; import { maxPriority, statesInOrder, terminalStatePriorities } from 'vs/workbench/contrib/testing/common/testingStates'; import { removeAnsiEscapeCodes } from 'vs/base/common/strings'; +import { TestId } from 'vs/workbench/contrib/testing/common/testId'; export interface ITestRunTaskResults extends ITestRunTask { /** @@ -88,10 +88,8 @@ export interface ITestResult { } export const resultItemParents = function* (results: ITestResult, item: TestResultItem) { - let i: TestResultItem | undefined = item; - while (i) { - yield i; - i = i.parent ? results.getStateById(i.parent) : undefined; + for (const id of TestId.fromString(item.item.extId).idsToRoot()) { + yield results.getStateById(id.toString())!; } }; @@ -130,6 +128,7 @@ export const maxCountPriority = (counts: Readonly) => { return TestResultState.Unset; }; +const getMarkCode = (marker: number, start: boolean) => `\x1b]633;SetMark;Id=${getMarkId(marker, start)};Hidden\x07`; /** * Deals with output of a {@link LiveTestResult}. By default we pass-through @@ -162,11 +161,19 @@ export class LiveOutputController { /** * Appends data to the output. */ - public append(data: VSBuffer): Promise | void { + public append(data: VSBuffer, marker?: number): Promise | void { if (this.closed) { return this.closed; } + if (marker !== undefined) { + data = VSBuffer.concat([ + VSBuffer.fromString(getMarkCode(marker, true)), + data, + VSBuffer.fromString(getMarkCode(marker, false)), + ]); + } + this.previouslyWritten?.push(data); this.dataEmitter.fire(data); this._offset += data.byteLength; @@ -257,7 +264,6 @@ interface TestResultItemWithChildren extends TestResultItem { } const itemToNode = (controllerId: string, item: ITestItem, parent: string | null): TestResultItemWithChildren => ({ - parent, controllerId, expand: TestItemExpandState.NotExpandable, item: { ...item }, @@ -285,6 +291,7 @@ export class LiveTestResult implements ITestResult { private readonly completeEmitter = new Emitter(); private readonly changeEmitter = new Emitter(); private readonly testById = new Map(); + private testMarkerCounter = 0; private _completedAt?: number; public readonly onChange = this.changeEmitter.event; @@ -319,14 +326,11 @@ export class LiveTestResult implements ITestResult { getParents: i => { const { testById: testByExtId } = this; return (function* () { - for (let parentId = i.parent; parentId;) { - const parent = testByExtId.get(parentId); - if (!parent) { - break; + const parentId = TestId.fromString(i.item.extId).parentId; + if (parentId) { + for (const id of parentId.idsToRoot()) { + yield testByExtId.get(id.toString())!; } - - yield parent; - parentId = parent.parent; } })(); }, @@ -352,15 +356,24 @@ export class LiveTestResult implements ITestResult { */ public appendOutput(output: VSBuffer, taskId: string, location?: IRichLocation, testId?: string): void { const preview = output.byteLength > 100 ? output.slice(0, 100).toString() + '…' : output.toString(); + let marker: number | undefined; + + // currently, the UI only exposes jump-to-message from tests or locations, + // so no need to mark outputs that don't come from either of those. + if (testId || location) { + marker = this.testMarkerCounter++; + } + const message: ITestOutputMessage = { location, message: removeAnsiEscapeCodes(preview), offset: this.output.offset, length: output.byteLength, + marker: marker, type: TestMessageType.Output, }; - this.output.append(output); + this.output.append(output, marker); const index = this.mustGetTaskIndex(taskId); if (testId) { @@ -646,11 +659,9 @@ export class HydratedTestResult implements ITestResult { this.request = serialized.request; for (const item of serialized.items) { - const cast: TestResultItem = { ...item } as any; - cast.item.uri = URI.revive(cast.item.uri); - cast.retired = true; - this.counts[item.ownComputedState]++; - this.testById.set(item.item.extId, cast); + const de = TestResultItem.deserialize(item); + this.counts[de.ownComputedState]++; + this.testById.set(item.item.extId, de); } } diff --git a/src/vs/workbench/contrib/testing/common/testService.ts b/src/vs/workbench/contrib/testing/common/testService.ts index f4a19c91d99..9da1e073307 100644 --- a/src/vs/workbench/contrib/testing/common/testService.ts +++ b/src/vs/workbench/contrib/testing/common/testService.ts @@ -23,6 +23,7 @@ export interface IMainThreadTestController { readonly id: string; readonly label: IObservableValue; readonly canRefresh: IObservableValue; + syncTests(token: CancellationToken): Promise; refreshTests(token: CancellationToken): Promise; configureRunProfile(profileId: number): void; expandTest(id: string, levels: number): Promise; @@ -77,10 +78,8 @@ export interface IMainThreadTestCollection extends AbstractIncrementalTestCollec * Iterates through the item and its parents to the root. */ export const getCollectionItemParents = function* (collection: IMainThreadTestCollection, item: InternalTestItem) { - let i: InternalTestItem | undefined = item; - while (i) { - yield i; - i = i.parent ? collection.getNodeById(i.parent) : undefined; + for (const id of TestId.fromString(item.item.extId).idsToRoot()) { + yield collection.getNodeById(id.toString())!; } }; @@ -158,12 +157,30 @@ export const getAllTestsInHierarchy = async (collection: IMainThreadTestCollecti ]).finally(() => l?.dispose()); }; +/** + * Waits for the test to no longer be in the "busy" state. + */ +export const waitForTestToBeIdle = (testService: ITestService, test: IncrementalTestCollectionItem) => { + if (!test.item.busy) { + return; + } + + return new Promise(resolve => { + const l = testService.onDidProcessDiff(() => { + if (testService.collection.getNodeById(test.item.extId)?.item.busy !== true) { + resolve(); // removed, or no longer busy + l.dispose(); + } + }); + }); +}; + /** * Iterator that expands to and iterates through tests in the file. Iterates * in strictly descending order. */ -export const testsInFile = async function* (collection: IMainThreadTestCollection, ident: IUriIdentityService, uri: URI): AsyncIterable { - for (const test of collection.all) { +export const testsInFile = async function* (testService: ITestService, ident: IUriIdentityService, uri: URI, waitForIdle = true): AsyncIterable { + for (const test of testService.collection.all) { if (!test.item.uri) { continue; } @@ -172,8 +189,13 @@ export const testsInFile = async function* (collection: IMainThreadTestCollectio yield test; } - if (ident.extUri.isEqualOrParent(uri, test.item.uri) && test.expand === TestItemExpandState.Expandable) { - await collection.expand(test.item.extId, 1); + if (ident.extUri.isEqualOrParent(uri, test.item.uri)) { + if (test.expand === TestItemExpandState.Expandable) { + await testService.collection.expand(test.item.extId, 1); + } + if (waitForIdle) { + await waitForTestToBeIdle(testService, test); + } } } }; @@ -264,6 +286,12 @@ export interface ITestService { */ runResolvedTests(req: ResolvedTestRunRequest, token?: CancellationToken): Promise; + /** + * Ensures the test diff from the remote ext host is flushed and waits for + * any "busy" tests to become idle before resolving. + */ + syncTests(): Promise; + /** * Cancels an ongoing test run by its ID, or all runs if no ID is given. */ diff --git a/src/vs/workbench/contrib/testing/common/testServiceImpl.ts b/src/vs/workbench/contrib/testing/common/testServiceImpl.ts index 3b107fabf85..0467fa176c7 100644 --- a/src/vs/workbench/contrib/testing/common/testServiceImpl.ts +++ b/src/vs/workbench/contrib/testing/common/testServiceImpl.ts @@ -238,6 +238,18 @@ export class TestService extends Disposable implements ITestService { return this.testControllers.get(id); } + /** + * @inheritdoc + */ + public async syncTests(): Promise { + const cts = new CancellationTokenSource(); + try { + await Promise.all([...this.testControllers.values()].map(c => c.syncTests(cts.token))); + } finally { + cts.dispose(true); + } + } + /** * @inheritdoc */ @@ -255,7 +267,7 @@ export class TestService extends Disposable implements ITestService { } finally { this.testRefreshCancellations.delete(cts); this.isRefreshingTests.set(this.testRefreshCancellations.size > 0); - cts.dispose(); + cts.dispose(true); } } @@ -305,7 +317,7 @@ export class TestService extends Disposable implements ITestService { if (req.isUiTriggered === false) { return; } - const saveBeforeTest: boolean = getTestingConfiguration(this.configurationService, TestingConfigKeys.SaveBeforeTest); + const saveBeforeTest = getTestingConfiguration(this.configurationService, TestingConfigKeys.SaveBeforeTest); if (saveBeforeTest) { await editorService.saveAll(); } diff --git a/src/vs/workbench/contrib/testing/common/testTypes.ts b/src/vs/workbench/contrib/testing/common/testTypes.ts index bf2ac6d4ba6..b93713245cd 100644 --- a/src/vs/workbench/contrib/testing/common/testTypes.ts +++ b/src/vs/workbench/contrib/testing/common/testTypes.ts @@ -8,6 +8,7 @@ import { MarshalledId } from 'vs/base/common/marshallingIds'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IPosition } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; +import { TestId } from 'vs/workbench/contrib/testing/common/testId'; export const enum TestResultState { Unset = 0, @@ -163,9 +164,16 @@ export interface ITestOutputMessage { type: TestMessageType.Output; offset: number; length: number; + marker?: number; location: IRichLocation | undefined; } +/** + * Gets the TTY marker ID for either starting or ending + * an ITestOutputMessage.marker of the given ID. + */ +export const getMarkId = (marker: number, start: boolean) => `${start ? 's' : 'e'}${marker}`; + export namespace ITestOutputMessage { export interface Serialized { message: string; @@ -326,38 +334,34 @@ export const enum TestItemExpandState { } /** - * TestItem-like shape, butm with an ID and children as strings. + * TestItem-like shape, but with an ID and children as strings. */ export interface InternalTestItem { /** Controller ID from whence this test came */ controllerId: string; /** Expandability state */ expand: TestItemExpandState; - /** Parent ID, if any */ - parent: string | null; /** Raw test item properties */ item: ITestItem; } export namespace InternalTestItem { export interface Serialized { - controllerId: string; expand: TestItemExpandState; - parent: string | null; item: ITestItem.Serialized; } export const serialize = (item: InternalTestItem): Serialized => ({ - controllerId: item.controllerId, expand: item.expand, - parent: item.parent, item: ITestItem.serialize(item.item) }); export const deserialize = (serialized: Serialized): InternalTestItem => ({ - controllerId: serialized.controllerId, + // the `controllerId` is derived from the test.item.extId. It's redundant + // in the non-serialized InternalTestItem too, but there just because it's + // checked against in many hot paths. + controllerId: TestId.root(serialized.item.extId), expand: serialized.expand, - parent: serialized.parent, item: ITestItem.deserialize(serialized.item) }); } @@ -462,6 +466,14 @@ export namespace TestResultItem { tasks: original.tasks.map(ITestTaskState.serialize), retired: original.retired, }); + + export const deserialize = (serialized: Serialized): TestResultItem => ({ + ...InternalTestItem.deserialize(serialized), + ownComputedState: serialized.ownComputedState, + computedState: serialized.computedState, + tasks: serialized.tasks.map(ITestTaskState.deserialize), + retired: true, + }); } export interface ISerializedTestResults { @@ -677,13 +689,14 @@ export abstract class AbstractIncrementalTestCollection; /** * Gets the range where a test ID is displayed, in the given URI. @@ -59,49 +59,12 @@ export interface ITestDecoration { export class TestDecorations { public value: T[] = []; - - private _idMap?: Map; - - /** - * Looks up a decoration by ID. - */ - public get(decorationId: string) { - if (this._idMap) { - return this._idMap.get(decorationId); - } else if (this.value.length > 16) { - this._idMap = new Map(); - for (const value of this.value) { this._idMap.set(value.id, value); } - return this._idMap.get(decorationId); - } else { - return this.value.find(v => v.id === decorationId); - } - } - /** * Adds a new value to the decorations. */ public push(value: T) { const searchIndex = binarySearch(this.value, value, (a, b) => a.line - b.line); this.value.splice(searchIndex < 0 ? ~searchIndex : searchIndex, 0, value); - this._idMap = undefined; - } - - /** - * Finds the value that exists on the given line, if any. - */ - public findOnLine(line: number, predicate: (value: T) => boolean): T | undefined { - const lineStart = binarySearch<{ line: number }>(this.value, { line }, (a, b) => a.line - b.line); - if (lineStart < 0) { - return undefined; - } - - for (let i = lineStart; i < this.value.length && this.value[i].line === line; i++) { - if (predicate(this.value[i])) { - return this.value[i]; - } - } - - return undefined; } /** diff --git a/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByLocation.test.ts b/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByLocation.test.ts index af584d316c4..48c5506112e 100644 --- a/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByLocation.test.ts +++ b/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByLocation.test.ts @@ -55,10 +55,10 @@ suite('Workbench - Testing Explorer Hierarchal by Location Projection', () => { harness.flush(); harness.pushDiff({ op: TestDiffOpType.Add, - item: { controllerId: 'ctrl2', parent: null, expand: TestItemExpandState.Expanded, item: new TestTestItem('ctrl2', 'c', 'c').toTestItem() }, + item: { controllerId: 'ctrl2', expand: TestItemExpandState.Expanded, item: new TestTestItem(new TestId(['ctrlId2']), 'c').toTestItem() }, }, { op: TestDiffOpType.Add, - item: { controllerId: 'ctrl2', parent: new TestId(['ctrl2', 'c']).toString(), expand: TestItemExpandState.NotExpandable, item: new TestTestItem('ctrl2', 'c-a', 'ca').toTestItem() }, + item: { controllerId: 'ctrl2', expand: TestItemExpandState.NotExpandable, item: new TestTestItem(new TestId(['ctrlId2', 'id-c']), 'ca').toTestItem() }, }); assert.deepStrictEqual(harness.flush(), [ @@ -76,7 +76,7 @@ suite('Workbench - Testing Explorer Hierarchal by Location Projection', () => { { e: 'b' } ]); - harness.c.root.children.get('id-a')!.children.add(new TestTestItem('ctrlId', 'ac', 'ac')); + harness.c.root.children.get('id-a')!.children.add(new TestTestItem(new TestId(['ctrlId', 'id-a', 'id-ac']), 'ac')); assert.deepStrictEqual(harness.flush(), [ { e: 'a', children: [{ e: 'aa' }, { e: 'ab' }, { e: 'ac' }] }, @@ -117,7 +117,6 @@ suite('Workbench - Testing Explorer Hierarchal by Location Projection', () => { tags: [], uri: undefined, }, - parent: 'id-root', tasks: [], ownComputedState: state, computedState: state, diff --git a/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByName.test.ts b/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByName.test.ts index ec4424b8edd..05f27076a0a 100644 --- a/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByName.test.ts +++ b/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByName.test.ts @@ -8,10 +8,10 @@ import { AbstractTreeViewState } from 'vs/base/browser/ui/tree/abstractTree'; import { Emitter } from 'vs/base/common/event'; import { HierarchicalByNameProjection } from 'vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByName'; import { TestDiffOpType, TestItemExpandState } from 'vs/workbench/contrib/testing/common/testTypes'; -import { TestId } from 'vs/workbench/contrib/testing/common/testId'; import { TestResultItemChange } from 'vs/workbench/contrib/testing/common/testResult'; import { TestTreeTestHarness } from 'vs/workbench/contrib/testing/test/browser/testObjectTree'; import { TestTestItem } from 'vs/workbench/contrib/testing/test/common/testStubs'; +import { TestId } from 'vs/workbench/contrib/testing/common/testId'; suite('Workbench - Testing Explorer Hierarchal by Name Projection', () => { let harness: TestTreeTestHarness; @@ -44,10 +44,10 @@ suite('Workbench - Testing Explorer Hierarchal by Name Projection', () => { harness.flush(); harness.pushDiff({ op: TestDiffOpType.Add, - item: { controllerId: 'ctrl2', parent: null, expand: TestItemExpandState.Expanded, item: new TestTestItem('ctrl2', 'c', 'root2').toTestItem() }, + item: { controllerId: 'ctrl2', expand: TestItemExpandState.Expanded, item: new TestTestItem(new TestId(['ctrl2']), 'root2').toTestItem() }, }, { op: TestDiffOpType.Add, - item: { controllerId: 'ctrl2', parent: new TestId(['ctrl2', 'c']).toString(), expand: TestItemExpandState.NotExpandable, item: new TestTestItem('ctrl2', 'c-a', 'c', undefined).toTestItem() }, + item: { controllerId: 'ctrl2', expand: TestItemExpandState.NotExpandable, item: new TestTestItem(new TestId(['ctrl2', 'id-c']), 'c', undefined).toTestItem() }, }); assert.deepStrictEqual(harness.flush(), [ @@ -59,7 +59,7 @@ suite('Workbench - Testing Explorer Hierarchal by Name Projection', () => { test('updates nodes if they add children', async () => { harness.flush(); - harness.c.root.children.get('id-a')!.children.add(new TestTestItem('ctrl2', 'ac', 'ac')); + harness.c.root.children.get('id-a')!.children.add(new TestTestItem(new TestId(['ctrlId', 'id-a', 'id-ac']), 'ac')); assert.deepStrictEqual(harness.flush(), [ { e: 'aa' }, @@ -81,7 +81,7 @@ suite('Workbench - Testing Explorer Hierarchal by Name Projection', () => { test('swaps when node is no longer leaf', async () => { harness.flush(); - harness.c.root.children.get('id-b')!.children.add(new TestTestItem('ctrl2', 'ba', 'ba')); + harness.c.root.children.get('id-b')!.children.add(new TestTestItem(new TestId(['ctrlId', 'id-b', 'id-ba']), 'ba')); assert.deepStrictEqual(harness.flush(), [ { e: 'aa' }, diff --git a/src/vs/workbench/contrib/testing/test/common/testStubs.ts b/src/vs/workbench/contrib/testing/test/common/testStubs.ts index f05f2a55214..a8770a29e15 100644 --- a/src/vs/workbench/contrib/testing/test/common/testStubs.ts +++ b/src/vs/workbench/contrib/testing/test/common/testStubs.ts @@ -35,18 +35,21 @@ export class TestTestItem implements ITestItemLike { return this.api.parent; } - public api: ITestItemApi = { controllerId: this.controllerId }; + public get id() { + return this._extId.localId; + } + + public api: ITestItemApi = { controllerId: this._extId.controllerId }; public children = createTestItemChildren(this.api, i => i.api, TestTestItem); constructor( - public readonly controllerId: string, - public readonly id: string, + private readonly _extId: TestId, label: string, uri?: URI, ) { this.props = { - extId: '', + extId: _extId.toString(), busy: false, description: null, error: null, @@ -69,20 +72,23 @@ export class TestTestItem implements ITestItemLike { public toTestItem(): ITestItem { const props = { ...this.props }; - props.extId = TestId.fromExtHostTestItem(this, this.controllerId).toString(); + props.extId = this._extId.toString(); return props; } } export class TestTestCollection extends TestItemCollection { constructor(controllerId = 'ctrlId') { + const root = new TestTestItem(new TestId([controllerId]), 'root'); + (root as any)._isRoot = true; + super({ controllerId, getApiFor: t => t.api, toITestItem: t => t.toTestItem(), getChildren: t => t.children, getDocumentVersion: () => undefined, - root: new TestTestItem(controllerId, controllerId, 'root'), + root, }); } @@ -111,14 +117,14 @@ export const testStubs = { const collection = new TestTestCollection(); collection.resolveHandler = item => { if (item === undefined) { - const a = new TestTestItem('ctrlId', idPrefix + 'a', 'a', URI.file('/')); + const a = new TestTestItem(new TestId(['ctrlId', 'id-a']), 'a', URI.file('/')); a.canResolveChildren = true; - const b = new TestTestItem('ctrlId', idPrefix + 'b', 'b', URI.file('/')); + const b = new TestTestItem(new TestId(['ctrlId', 'id-b']), 'b', URI.file('/')); collection.root.children.add(a); collection.root.children.add(b); } else if (item.id === idPrefix + 'a') { - item.children.add(new TestTestItem('ctrlId', idPrefix + 'aa', 'aa', URI.file('/'))); - item.children.add(new TestTestItem('ctrlId', idPrefix + 'ab', 'ab', URI.file('/'))); + item.children.add(new TestTestItem(new TestId(['ctrlId', 'id-a', 'id-aa']), 'aa', URI.file('/'))); + item.children.add(new TestTestItem(new TestId(['ctrlId', 'id-a', 'id-ab']), 'ab', URI.file('/'))); } }; diff --git a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts index adb89a15354..0cf8cd7b52f 100644 --- a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts +++ b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts @@ -8,7 +8,7 @@ import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { MenuRegistry, MenuId, Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { equalsIgnoreCase } from 'vs/base/common/strings'; import { Registry } from 'vs/platform/registry/common/platform'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { IWorkbenchThemeService, IWorkbenchTheme, ThemeSettingTarget, IWorkbenchColorTheme, IWorkbenchFileIconTheme, IWorkbenchProductIconTheme, ThemeSettings } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { VIEWLET_ID, IExtensionsViewPaneContainer } from 'vs/workbench/contrib/extensions/common/extensions'; import { IExtensionGalleryService, IExtensionManagementService, IGalleryExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; @@ -30,7 +30,7 @@ import { Codicon } from 'vs/base/common/codicons'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { Emitter } from 'vs/base/common/event'; -import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; +import { IExtensionResourceLoaderService } from 'vs/platform/extensionResourceLoader/common/extensionResourceLoader'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; @@ -358,7 +358,7 @@ registerAction2(class extends Action2 { super({ id: SelectColorThemeCommandId, title: { value: localize('selectTheme.label', "Color Theme"), original: 'Color Theme' }, - category: CATEGORIES.Preferences, + category: Categories.Preferences, f1: true, keybinding: { weight: KeybindingWeight.WorkbenchContrib, @@ -400,7 +400,7 @@ registerAction2(class extends Action2 { super({ id: SelectFileIconThemeCommandId, title: { value: localize('selectIconTheme.label', "File Icon Theme"), original: 'File Icon Theme' }, - category: CATEGORIES.Preferences, + category: Categories.Preferences, f1: true }); } @@ -435,7 +435,7 @@ registerAction2(class extends Action2 { super({ id: SelectProductIconThemeCommandId, title: { value: localize('selectProductIconTheme.label', "Product Icon Theme"), original: 'Product Icon Theme' }, - category: CATEGORIES.Preferences, + category: Categories.Preferences, f1: true }); } @@ -546,7 +546,7 @@ registerAction2(class extends Action2 { super({ id: 'workbench.action.generateColorTheme', title: { value: localize('generateColorTheme.label', "Generate Color Theme From Current Settings"), original: 'Generate Color Theme From Current Settings' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true }); } @@ -600,7 +600,7 @@ registerAction2(class extends Action2 { super({ id: toggleLightDarkThemesCommandId, title: { value: localize('toggleLightDarkThemes.label', "Toggle between Light/Dark Themes"), original: 'Toggle between Light/Dark Themes' }, - category: CATEGORIES.Preferences, + category: Categories.Preferences, f1: true, }); } diff --git a/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts b/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts index 8aae19bc264..f04d0d41651 100644 --- a/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts +++ b/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts @@ -5,7 +5,7 @@ import { localize } from 'vs/nls'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Registry } from 'vs/platform/registry/common/platform'; import { IViewsRegistry, IViewDescriptor, Extensions as ViewExtensions } from 'vs/workbench/common/views'; import { VIEW_CONTAINER } from 'vs/workbench/contrib/files/browser/explorerViewlet'; @@ -99,4 +99,4 @@ MenuRegistry.appendMenuItem(MenuId.TimelineTitle, { icon: timelineFilter }); -registerSingleton(ITimelineService, TimelineService, true); +registerSingleton(ITimelineService, TimelineService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts b/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts index 9517be6e2e0..b31141ebf75 100644 --- a/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts +++ b/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts @@ -17,7 +17,6 @@ import { generateTokensCSSForColorMap } from 'vs/editor/common/languages/support import { ILanguageService } from 'vs/editor/common/languages/language'; import * as nls from 'vs/nls'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -63,10 +62,7 @@ export class ReleaseNotesManager { }); } - public async show( - accessor: ServicesAccessor, - version: string - ): Promise { + public async show(version: string): Promise { const releaseNoteText = await this.loadReleaseNotes(version); this._lastText = releaseNoteText; const html = await this.renderBody(releaseNoteText); @@ -78,7 +74,7 @@ export class ReleaseNotesManager { this._currentReleaseNotes.webview.html = html; this._webviewWorkbenchService.revealWebview(this._currentReleaseNotes, activeEditorPane ? activeEditorPane.group : this._editorGroupService.activeGroup, false); } else { - this._currentReleaseNotes = this._webviewWorkbenchService.createWebview( + this._currentReleaseNotes = this._webviewWorkbenchService.openWebview( { id: generateUuid(), options: { diff --git a/src/vs/workbench/contrib/update/browser/update.contribution.ts b/src/vs/workbench/contrib/update/browser/update.contribution.ts index f77d43ba166..68185b8ea63 100644 --- a/src/vs/workbench/contrib/update/browser/update.contribution.ts +++ b/src/vs/workbench/contrib/update/browser/update.contribution.ts @@ -7,31 +7,90 @@ import 'vs/platform/update/common/update.config.contribution'; import { localize } from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { IWorkbenchActionRegistry, Extensions as ActionExtensions, CATEGORIES } from 'vs/workbench/common/actions'; -import { SyncActionDescriptor, MenuRegistry, MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; -import { ShowCurrentReleaseNotesAction, ProductContribution, UpdateContribution, CheckForVSCodeUpdateAction, CONTEXT_UPDATE_STATE, SwitchProductQualityContribution } from 'vs/workbench/contrib/update/browser/update'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; +import { MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; +import { ProductContribution, UpdateContribution, CONTEXT_UPDATE_STATE, SwitchProductQualityContribution, RELEASE_NOTES_URL, showReleaseNotes } from 'vs/workbench/contrib/update/browser/update'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import product from 'vs/platform/product/common/product'; import { IUpdateService, StateType } from 'vs/platform/update/common/update'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { isWindows } from 'vs/base/common/platform'; import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; +import { ShowCurrentReleaseNotesActionId } from 'vs/workbench/contrib/update/common/update'; +import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { URI } from 'vs/base/common/uri'; const workbench = Registry.as(WorkbenchExtensions.Workbench); -workbench.registerWorkbenchContribution(ProductContribution, 'ProductContribution', LifecyclePhase.Restored); -workbench.registerWorkbenchContribution(UpdateContribution, 'UpdateContribution', LifecyclePhase.Restored); -workbench.registerWorkbenchContribution(SwitchProductQualityContribution, 'SwitchProductQualityContribution', LifecyclePhase.Restored); +workbench.registerWorkbenchContribution(ProductContribution, LifecyclePhase.Restored); +workbench.registerWorkbenchContribution(UpdateContribution, LifecyclePhase.Restored); +workbench.registerWorkbenchContribution(SwitchProductQualityContribution, LifecyclePhase.Restored); -const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); +// Release notes -// Editor -actionRegistry - .registerWorkbenchAction(SyncActionDescriptor.from(ShowCurrentReleaseNotesAction), `${product.nameShort}: Show Release Notes`, product.nameShort); +export class ShowCurrentReleaseNotesAction extends Action2 { -actionRegistry - .registerWorkbenchAction(SyncActionDescriptor.from(CheckForVSCodeUpdateAction), `${product.nameShort}: Check for Update`, product.nameShort, CONTEXT_UPDATE_STATE.isEqualTo(StateType.Idle)); + constructor() { + super({ + id: ShowCurrentReleaseNotesActionId, + title: { + value: localize('showReleaseNotes', "Show Release Notes"), + mnemonicTitle: localize('mshowReleaseNotes', "Show &&Release Notes"), + original: 'Show Release Notes' + }, + category: { value: product.nameShort, original: product.nameShort }, + f1: true, + precondition: RELEASE_NOTES_URL, + menu: [{ + id: MenuId.MenubarHelpMenu, + group: '1_welcome', + order: 5, + when: RELEASE_NOTES_URL, + }] + }); + } + + async run(accessor: ServicesAccessor): Promise { + const instantiationService = accessor.get(IInstantiationService); + const productService = accessor.get(IProductService); + const openerService = accessor.get(IOpenerService); + + try { + await showReleaseNotes(instantiationService, productService.version); + } catch (err) { + if (productService.releaseNotesUrl) { + await openerService.open(URI.parse(productService.releaseNotesUrl)); + } else { + throw new Error(localize('update.noReleaseNotesOnline', "This version of {0} does not have release notes online", productService.nameLong)); + } + } + } +} + +registerAction2(ShowCurrentReleaseNotesAction); + +// Update + +export class CheckForUpdateAction extends Action2 { + + constructor() { + super({ + id: 'update.checkForUpdate', + title: { value: localize('checkForUpdates', "Check for Updates..."), original: 'Check for Updates...' }, + category: { value: product.nameShort, original: product.nameShort }, + f1: true, + precondition: CONTEXT_UPDATE_STATE.isEqualTo(StateType.Idle), + }); + } + + async run(accessor: ServicesAccessor): Promise { + const updateService = accessor.get(IUpdateService); + return updateService.checkForUpdates(true); + } +} class DownloadUpdateAction extends Action2 { constructor() { @@ -81,29 +140,50 @@ class RestartToUpdateAction extends Action2 { } } +class DownloadAction extends Action2 { + + static readonly ID = 'workbench.action.download'; + static readonly AVAILABLE = !!product.downloadUrl; + + constructor() { + super({ + id: DownloadAction.ID, + title: { + value: localize('openDownloadPage', "Download {0}", product.nameLong), + original: `Download ${product.downloadUrl}` + }, + precondition: IsWebContext, // Only show when running in a web browser + f1: true, + menu: [{ + id: MenuId.StatusBarWindowIndicatorMenu, + when: IsWebContext + }] + }); + } + + run(accessor: ServicesAccessor): void { + const productService = accessor.get(IProductService); + const openerService = accessor.get(IOpenerService); + + if (productService.downloadUrl) { + openerService.open(URI.parse(productService.downloadUrl)); + } + } +} + +registerAction2(DownloadAction); +registerAction2(CheckForUpdateAction); registerAction2(DownloadUpdateAction); registerAction2(InstallUpdateAction); registerAction2(RestartToUpdateAction); -// Menu -if (ShowCurrentReleaseNotesAction.AVAILABE) { - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { - group: '1_welcome', - command: { - id: ShowCurrentReleaseNotesAction.ID, - title: localize({ key: 'miReleaseNotes', comment: ['&& denotes a mnemonic'] }, "&&Release Notes") - }, - order: 5 - }); -} - if (isWindows) { class DeveloperApplyUpdateAction extends Action2 { constructor() { super({ id: '_update.applyupdate', title: { value: localize('applyUpdate', "Apply Update..."), original: 'Apply Update...' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true, precondition: CONTEXT_UPDATE_STATE.isEqualTo(StateType.Idle) }); diff --git a/src/vs/workbench/contrib/update/browser/update.ts b/src/vs/workbench/contrib/update/browser/update.ts index 1ff40d85b54..83166c9f475 100644 --- a/src/vs/workbench/contrib/update/browser/update.ts +++ b/src/vs/workbench/contrib/update/browser/update.ts @@ -23,26 +23,25 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { RawContextKey, IContextKey, IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { MenuRegistry, MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { ShowCurrentReleaseNotesActionId, CheckForVSCodeUpdateActionId } from 'vs/workbench/contrib/update/common/update'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IProductService } from 'vs/platform/product/common/productService'; -import product from 'vs/platform/product/common/product'; import { IUserDataSyncEnablementService, IUserDataSyncService, IUserDataSyncStoreManagementService, SyncStatus, UserDataSyncStoreType } from 'vs/platform/userDataSync/common/userDataSync'; import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys'; import { Promises } from 'vs/base/common/async'; import { IUserDataSyncWorkbenchService } from 'vs/workbench/services/userDataSync/common/userDataSync'; import { Event } from 'vs/base/common/event'; -export const CONTEXT_UPDATE_STATE = new RawContextKey('updateState', StateType.Idle); +export const CONTEXT_UPDATE_STATE = new RawContextKey('updateState', StateType.Uninitialized); +export const RELEASE_NOTES_URL = new RawContextKey('releaseNotesUrl', ''); let releaseNotesManager: ReleaseNotesManager | undefined = undefined; -function showReleaseNotes(instantiationService: IInstantiationService, version: string) { +export function showReleaseNotes(instantiationService: IInstantiationService, version: string) { if (!releaseNotesManager) { releaseNotesManager = instantiationService.createInstance(ReleaseNotesManager); } - return instantiationService.invokeFunction(accessor => releaseNotesManager!.show(accessor, version)); + return releaseNotesManager.show(version); } export class OpenLatestReleaseNotesInBrowserAction extends Action { @@ -104,22 +103,6 @@ export class ShowReleaseNotesAction extends AbstractShowReleaseNotesAction { } } -export class ShowCurrentReleaseNotesAction extends AbstractShowReleaseNotesAction { - - static readonly ID = ShowCurrentReleaseNotesActionId; - static readonly LABEL = nls.localize('showReleaseNotes', "Show Release Notes"); - static readonly AVAILABE = !!product.releaseNotesUrl; - - constructor( - id = ShowCurrentReleaseNotesAction.ID, - label = ShowCurrentReleaseNotesAction.LABEL, - @IInstantiationService instantiationService: IInstantiationService, - @IProductService productService: IProductService - ) { - super(id, label, productService.version, instantiationService); - } -} - interface IVersion { major: number; minor: number; @@ -156,8 +139,14 @@ export class ProductContribution implements IWorkbenchContribution { @IOpenerService openerService: IOpenerService, @IConfigurationService configurationService: IConfigurationService, @IHostService hostService: IHostService, - @IProductService productService: IProductService + @IProductService productService: IProductService, + @IContextKeyService contextKeyService: IContextKeyService, ) { + if (productService.releaseNotesUrl) { + const releaseNotesUrlKey = RELEASE_NOTES_URL.bindTo(contextKeyService); + releaseNotesUrlKey.set(productService.releaseNotesUrl); + } + hostService.hadLastFocus().then(async hadLastFocus => { if (!hadLastFocus) { return; @@ -604,21 +593,3 @@ export class SwitchProductQualityContribution extends Disposable implements IWor } } } - -export class CheckForVSCodeUpdateAction extends Action { - - static readonly ID = CheckForVSCodeUpdateActionId; - static LABEL = nls.localize('checkForUpdates', "Check for Updates..."); - - constructor( - id: string, - label: string, - @IUpdateService private readonly updateService: IUpdateService, - ) { - super(id, label, undefined, true); - } - - override run(): Promise { - return this.updateService.checkForUpdates(true); - } -} diff --git a/src/vs/workbench/contrib/update/common/update.ts b/src/vs/workbench/contrib/update/common/update.ts index 5b9303f6eac..c224d76703a 100644 --- a/src/vs/workbench/contrib/update/common/update.ts +++ b/src/vs/workbench/contrib/update/common/update.ts @@ -4,4 +4,3 @@ *--------------------------------------------------------------------------------------------*/ export const ShowCurrentReleaseNotesActionId = 'update.showCurrentReleaseNotes'; -export const CheckForVSCodeUpdateActionId = 'update.checkForVSCodeUpdate'; \ No newline at end of file diff --git a/src/vs/workbench/contrib/url/browser/url.contribution.ts b/src/vs/workbench/contrib/url/browser/url.contribution.ts index 8f0486abd0b..994320e7863 100644 --- a/src/vs/workbench/contrib/url/browser/url.contribution.ts +++ b/src/vs/workbench/contrib/url/browser/url.contribution.ts @@ -17,7 +17,7 @@ import { manageTrustedDomainSettingsCommand } from 'vs/workbench/contrib/url/bro import { TrustedDomainsFileSystemProvider } from 'vs/workbench/contrib/url/browser/trustedDomainsFileSystemProvider'; import { OpenerValidatorContributions } from 'vs/workbench/contrib/url/browser/trustedDomainsValidator'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { ConfigurationScope, Extensions as ConfigurationExtensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; @@ -27,7 +27,7 @@ class OpenUrlAction extends Action2 { super({ id: 'workbench.action.url.openUrl', title: { value: localize('openUrl', "Open URL"), original: 'Open URL' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true }); } @@ -64,17 +64,14 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( OpenerValidatorContributions, - 'OpenerValidatorContributions', LifecyclePhase.Restored ); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( TrustedDomainsFileSystemProvider, - 'TrustedDomainsFileSystemProvider', LifecyclePhase.Ready ); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( ExternalUriResolverContribution, - 'ExternalUriResolverContribution', LifecyclePhase.Ready ); diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.contribution.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.contribution.ts index 665ae46d09c..b069dea82cd 100644 --- a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.contribution.ts +++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.contribution.ts @@ -10,4 +10,4 @@ import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle import './userDataProfileActions'; const workbenchRegistry = Registry.as(Extensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(UserDataProfilesWorkbenchContribution, 'UserDataProfilesWorkbenchContribution', LifecyclePhase.Ready); +workbenchRegistry.registerWorkbenchContribution(UserDataProfilesWorkbenchContribution, LifecyclePhase.Ready); diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts index 74775e7c433..9861a884a99 100644 --- a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts +++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts @@ -19,11 +19,15 @@ import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuratio import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { RenameProfileAction } from 'vs/workbench/contrib/userDataProfile/browser/userDataProfileActions'; import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { CURRENT_PROFILE_CONTEXT, HAS_PROFILES_CONTEXT, IS_CURRENT_PROFILE_TRANSIENT_CONTEXT, IUserDataProfileManagementService, IUserDataProfileService, ManageProfilesSubMenu, PROFILES_ENABLEMENT_CONTEXT, PROFILES_TTILE } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; +import { CURRENT_PROFILE_CONTEXT, HAS_PROFILES_CONTEXT, IS_CURRENT_PROFILE_TRANSIENT_CONTEXT, IUserDataProfileImportExportService, IUserDataProfileManagementService, IUserDataProfileService, ManageProfilesSubMenu, PROFILES_CATEGORY, PROFILES_ENABLEMENT_CONTEXT, PROFILES_TTILE, PROFILE_EXTENSION, PROFILE_FILTER } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { charCount } from 'vs/base/common/strings'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { joinPath } from 'vs/base/common/resources'; +import { Codicon } from 'vs/base/common/codicons'; export class UserDataProfilesWorkbenchContribution extends Disposable implements IWorkbenchContribution { @@ -44,6 +48,7 @@ export class UserDataProfilesWorkbenchContribution extends Disposable implements this.registerConfiguration(); this.currentProfileContext = CURRENT_PROFILE_CONTEXT.bindTo(contextKeyService); + PROFILES_ENABLEMENT_CONTEXT.bindTo(contextKeyService).set(this.userDataProfilesService.isEnabled()); this.isCurrentProfileTransientContext = IS_CURRENT_PROFILE_TRANSIENT_CONTEXT.bindTo(contextKeyService); this.currentProfileContext.set(this.userDataProfileService.currentProfile.id); @@ -153,17 +158,20 @@ export class UserDataProfilesWorkbenchContribution extends Disposable implements this.currentprofileActionsDisposable.value = new DisposableStore(); this.currentprofileActionsDisposable.value.add(this.registerUpdateCurrentProfileShortNameAction()); this.currentprofileActionsDisposable.value.add(this.registerRenameCurrentProfileAction()); + this.currentprofileActionsDisposable.value.add(this.registerExportCurrentProfileAction()); } private registerUpdateCurrentProfileShortNameAction(): IDisposable { const that = this; return registerAction2(class UpdateCurrentProfileShortName extends Action2 { constructor() { + const shortName = that.userDataProfileService.getShortName(that.userDataProfileService.currentProfile); + const themeIcon = ThemeIcon.fromString(shortName); super({ id: `workbench.profiles.actions.updateCurrentProfileShortName`, title: { - value: localize('change short name profile', "Change Short Name ({0})...", that.userDataProfileService.currentProfile.shortName), - original: `Change Short Name (${that.userDataProfileService.currentProfile.shortName})...` + value: localize('change short name profile', "Change Short Name ({0})...", themeIcon?.id ?? shortName), + original: `Change Short Name (${themeIcon?.id ?? shortName})...` }, menu: [ { @@ -181,10 +189,20 @@ export class UserDataProfilesWorkbenchContribution extends Disposable implements const profile = that.userDataProfileService.currentProfile; const shortName = await quickInputService.input({ - value: profile.shortName, + value: that.userDataProfileService.getShortName(profile), title: localize('change short name', "Change Short Name..."), validateInput: async (value: string) => { - if (profile.shortName !== value && !ThemeIcon.fromString(value) && charCount(value) > 2) { + if (profile.shortName === value) { + return undefined; + } + const themeIcon = ThemeIcon.fromString(value); + if (themeIcon) { + if (Codicon.getAll().some(c => c.id === themeIcon.id)) { + return undefined; + } + return localize('invalid codicon', "Invalid codicon. Please use a valid codicon id."); + } + if (charCount(value) > 2) { return localize('invalid short name', "Short name should be at most 2 characters long."); } return undefined; @@ -227,4 +245,64 @@ export class UserDataProfilesWorkbenchContribution extends Disposable implements }); } + private registerExportCurrentProfileAction(): IDisposable { + const that = this; + const disposables = new DisposableStore(); + const id = 'workbench.profiles.actions.exportProfile'; + disposables.add(registerAction2(class ExportProfileAction extends Action2 { + constructor() { + super({ + id, + title: { + value: localize('export profile', "Export ({0})...", that.userDataProfileService.currentProfile.name), + original: `Export (${that.userDataProfileService.currentProfile.name})...` + }, + category: PROFILES_CATEGORY, + menu: [ + { + id: ManageProfilesSubMenu, + group: '4_import_export_profiles', + when: PROFILES_ENABLEMENT_CONTEXT, + order: 1 + }, { + id: MenuId.CommandPalette + } + ] + }); + } + + async run(accessor: ServicesAccessor) { + const textFileService = accessor.get(ITextFileService); + const fileDialogService = accessor.get(IFileDialogService); + const userDataProfileImportExportService = accessor.get(IUserDataProfileImportExportService); + const notificationService = accessor.get(INotificationService); + + const profileLocation = await fileDialogService.showSaveDialog({ + title: localize('export profile dialog', "Save Profile"), + filters: PROFILE_FILTER, + defaultUri: joinPath(await fileDialogService.defaultFilePath(), `profile.${PROFILE_EXTENSION}`), + }); + + if (!profileLocation) { + return; + } + + const profile = await userDataProfileImportExportService.exportProfile({ skipComments: true }); + await textFileService.create([{ resource: profileLocation, value: JSON.stringify(profile), options: { overwrite: true } }]); + + notificationService.info(localize('export success', "{0}: Exported successfully.", PROFILES_CATEGORY.value)); + } + })); + disposables.add(MenuRegistry.appendMenuItem(MenuId.MenubarShare, { + command: { + id, + title: { + value: localize('export settings profile', "Export Settings Profile ({0})...", that.userDataProfileService.currentProfile.name), + original: `Export Settings Profile (${that.userDataProfileService.currentProfile.name})...` + } + }, + })); + return disposables; + } + } diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfileActions.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfileActions.ts index dbdb4fea927..a8bfcdadac1 100644 --- a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfileActions.ts +++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfileActions.ts @@ -5,19 +5,17 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { joinPath } from 'vs/base/common/resources'; import { localize } from 'vs/nls'; -import { Action2, IMenuService, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; +import { Action2, IMenuService, registerAction2 } from 'vs/platform/actions/common/actions'; import { IDialogService, IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IFileService } from 'vs/platform/files/common/files'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { QuickPickItem, IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { asJson, asText, IRequestService } from 'vs/platform/request/common/request'; -import { IUserDataProfileTemplate, isUserDataProfileTemplate, IUserDataProfileManagementService, IUserDataProfileImportExportService, PROFILES_CATEGORY, PROFILE_EXTENSION, PROFILE_FILTER, ManageProfilesSubMenu, IUserDataProfileService, PROFILES_ENABLEMENT_CONTEXT, HAS_PROFILES_CONTEXT, MANAGE_PROFILES_ACTION_ID } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IUserDataProfileTemplate, isUserDataProfileTemplate, IUserDataProfileManagementService, IUserDataProfileImportExportService, PROFILES_CATEGORY, ManageProfilesSubMenu, IUserDataProfileService, PROFILES_ENABLEMENT_CONTEXT, HAS_PROFILES_CONTEXT, MANAGE_PROFILES_ACTION_ID, PROFILE_FILTER } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { compare } from 'vs/base/common/strings'; @@ -136,36 +134,50 @@ registerAction2(class CreateProfileAction extends Action2 { const commandService = accessor.get(ICommandService); const pick = await quickInputService.pick( [{ + id: CreateEmptyProfileAction.ID, + label: CreateEmptyProfileAction.TITLE.value, + }, { + type: 'separator', + }, { id: CreateFromCurrentProfileAction.ID, label: CreateFromCurrentProfileAction.TITLE.value, }, { - id: CreateEmptyProfileAction.ID, - label: CreateEmptyProfileAction.TITLE.value, - }], { hideInput: true, canPickMany: false, title: localize('create settings profile', "{0}: Create...", PROFILES_CATEGORY) }); - if (pick) { + id: CreateFromTemplateAction.ID, + label: CreateFromTemplateAction.TITLE.value, + }, { + type: 'separator', + }, { + id: CreateTransientProfileAction.ID, + label: CreateTransientProfileAction.TITLE.value, + }], { hideInput: true, canPickMany: false, title: localize('create settings profile', "{0}: Create...", PROFILES_CATEGORY.value) }); + if (pick?.id) { return commandService.executeCommand(pick.id); } } }); -registerAction2(class CreateTransientProfileAction extends Action2 { +class CreateTransientProfileAction extends Action2 { + static readonly ID = 'workbench.profiles.actions.createTemporaryProfile'; + static readonly TITLE = { + value: localize('create temporary profile', "Create a Temporary Settings Profile"), + original: 'Create a Temporary Settings Profile' + }; constructor() { super({ - id: 'workbench.profiles.actions.createTemporaryProfile', - title: { - value: localize('create temporary profile', "Create a Temporary Settings Profile"), - original: 'Create a Temporary Settings Profile' - }, + id: CreateTransientProfileAction.ID, + title: CreateTransientProfileAction.TITLE, category: PROFILES_CATEGORY, f1: true, - precondition: PROFILES_ENABLEMENT_CONTEXT + precondition: PROFILES_ENABLEMENT_CONTEXT, }); } async run(accessor: ServicesAccessor) { return accessor.get(IUserDataProfileManagementService).createAndEnterTransientProfile(); } -}); +} + +registerAction2(CreateTransientProfileAction); export class RenameProfileAction extends Action2 { static readonly ID = 'workbench.profiles.actions.renameProfile'; @@ -330,7 +342,7 @@ registerAction2(class ManageSettingsProfileAction extends Action2 { label: `${action.label}${action.checked ? ` $(${Codicon.check.id})` : ''}`, }; }); - const pick = await quickInputService.pick(picks, { canPickMany: false, title: PROFILES_CATEGORY }); + const pick = await quickInputService.pick(picks, { canPickMany: false, title: PROFILES_CATEGORY.value }); if (pick?.id) { await commandService.executeCommand(pick.id); } @@ -372,70 +384,19 @@ registerAction2(class SwitchProfileAction extends Action2 { } }); -registerAction2(class ExportProfileAction extends Action2 { +class ImportProfileAction extends Action2 { + static readonly ID = 'workbench.profiles.actions.importProfile'; + static readonly TITLE = { + value: localize('import profile', "Import from ..."), + original: 'Import...' + }; constructor() { super({ - id: 'workbench.profiles.actions.exportProfile', - title: { - value: localize('export profile', "Export..."), - original: 'Export...' - }, + id: ImportProfileAction.ID, + title: ImportProfileAction.TITLE, category: PROFILES_CATEGORY, - menu: [ - { - id: ManageProfilesSubMenu, - group: '4_import_export_profiles', - when: PROFILES_ENABLEMENT_CONTEXT, - order: 1 - }, { - id: MenuId.CommandPalette - } - ] - }); - } - - async run(accessor: ServicesAccessor) { - const textFileService = accessor.get(ITextFileService); - const fileDialogService = accessor.get(IFileDialogService); - const userDataProfileImportExportService = accessor.get(IUserDataProfileImportExportService); - const notificationService = accessor.get(INotificationService); - - const profileLocation = await fileDialogService.showSaveDialog({ - title: localize('export profile dialog', "Save Profile"), - filters: PROFILE_FILTER, - defaultUri: joinPath(await fileDialogService.defaultFilePath(), `profile.${PROFILE_EXTENSION}`), - }); - - if (!profileLocation) { - return; - } - - const profile = await userDataProfileImportExportService.exportProfile({ skipComments: true }); - await textFileService.create([{ resource: profileLocation, value: JSON.stringify(profile), options: { overwrite: true } }]); - - notificationService.info(localize('export success', "{0}: Exported successfully.", PROFILES_CATEGORY)); - } -}); - -registerAction2(class ImportProfileAction extends Action2 { - constructor() { - super({ - id: 'workbench.profiles.actions.importProfile', - title: { - value: localize('import profile', "Import..."), - original: 'Import...' - }, - category: PROFILES_CATEGORY, - menu: [ - { - id: ManageProfilesSubMenu, - group: '4_import_export_profiles', - when: PROFILES_ENABLEMENT_CONTEXT, - order: 2 - }, { - id: MenuId.CommandPalette - } - ] + f1: true, + precondition: PROFILES_ENABLEMENT_CONTEXT?.negate(), }); } @@ -463,11 +424,11 @@ registerAction2(class ImportProfileAction extends Action2 { const disposables = new DisposableStore(); const quickPick = disposables.add(quickInputService.createQuickPick()); const updateQuickPickItems = (value?: string) => { - const selectFromFileItem: IQuickPickItem = { label: localize('select from file', "Import from profile file") }; - quickPick.items = value ? [{ label: localize('select from url', "Import from URL"), description: quickPick.value }, selectFromFileItem] : [selectFromFileItem]; + const selectFromFileItem: IQuickPickItem = { label: isSettingProfilesEnabled ? localize('select from file', "Select Settings Profile template file") : localize('import from file', "Import from profile file") }; + quickPick.items = value ? [{ label: isSettingProfilesEnabled ? localize('select from url', "Create from template URL") : localize('import from url', "Import from URL"), description: quickPick.value }, selectFromFileItem] : [selectFromFileItem]; }; - quickPick.title = localize('import profile quick pick title', "Import Settings from a Profile"); - quickPick.placeholder = localize('import profile placeholder', "Provide profile URL or select profile file to import"); + quickPick.title = isSettingProfilesEnabled ? localize('create from profile template quick pick title', "Create from Settings Profile Template") : localize('import profile quick pick title', "Import Settings from a Profile"); + quickPick.placeholder = isSettingProfilesEnabled ? localize('create from profile template placeholder', "Provide a template URL or Select a template file") : localize('import profile placeholder', "Provide profile URL or select profile file to import"); quickPick.ignoreFocusOut = true; disposables.add(quickPick.onDidChangeValue(updateQuickPickItems)); updateQuickPickItems(); @@ -520,7 +481,32 @@ registerAction2(class ImportProfileAction extends Action2 { } } -}); +} +registerAction2(ImportProfileAction); + +class CreateFromTemplateAction extends Action2 { + static readonly ID = 'workbench.profiles.actions.createFromTemplate'; + static readonly TITLE = { + value: localize('create from template profile', "Create from a Settings Profile Template..."), + original: 'Create from a Settings Profile Template...' + }; + constructor() { + super({ + id: CreateFromTemplateAction.ID, + title: CreateFromTemplateAction.TITLE, + category: PROFILES_CATEGORY, + f1: true, + precondition: PROFILES_ENABLEMENT_CONTEXT, + }); + } + + async run(accessor: ServicesAccessor) { + return accessor.get(ICommandService).executeCommand(ImportProfileAction.ID); + } + +} + +registerAction2(CreateFromTemplateAction); // Developer Actions @@ -532,7 +518,7 @@ registerAction2(class CleanupProfilesAction extends Action2 { value: localize('cleanup profile', "Cleanup Settings Profiles"), original: 'Cleanup Profiles' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true, precondition: PROFILES_ENABLEMENT_CONTEXT, }); @@ -551,7 +537,7 @@ registerAction2(class ResetWorkspacesAction extends Action2 { value: localize('reset workspaces', "Reset Workspace Settings Profiles Associations"), original: 'Reset Workspace Settings Profiles Associations' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true, precondition: PROFILES_ENABLEMENT_CONTEXT, }); diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts index e470b811bc0..64c68ee02e6 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts @@ -69,6 +69,6 @@ class UserDataSyncReportIssueContribution extends Disposable implements IWorkben } const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(UserDataSyncWorkbenchContribution, 'UserDataSyncWorkbenchContribution', LifecyclePhase.Restored); -workbenchRegistry.registerWorkbenchContribution(UserDataSyncTrigger, 'UserDataSyncTrigger', LifecyclePhase.Eventually); -workbenchRegistry.registerWorkbenchContribution(UserDataSyncReportIssueContribution, 'UserDataSyncReportIssueContribution', LifecyclePhase.Eventually); +workbenchRegistry.registerWorkbenchContribution(UserDataSyncWorkbenchContribution, LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(UserDataSyncTrigger, LifecyclePhase.Eventually); +workbenchRegistry.registerWorkbenchContribution(UserDataSyncReportIssueContribution, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 023bf17fe4c..6ac33754732 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -49,7 +49,7 @@ import { UserDataSyncDataViews } from 'vs/workbench/contrib/userDataSync/browser import { IUserDataSyncWorkbenchService, getSyncAreaLabel, AccountStatus, CONTEXT_SYNC_STATE, CONTEXT_SYNC_ENABLEMENT, CONTEXT_ACCOUNT_STATE, CONFIGURE_SYNC_COMMAND_ID, SHOW_SYNC_LOG_COMMAND_ID, SYNC_VIEW_CONTAINER_ID, SYNC_TITLE, SYNC_VIEW_ICON, CONTEXT_HAS_CONFLICTS } from 'vs/workbench/services/userDataSync/common/userDataSync'; import { Codicon } from 'vs/base/common/codicons'; import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { IUserDataInitializationService } from 'vs/workbench/services/userData/browser/userDataInit'; import { MarkdownString } from 'vs/base/common/htmlContent'; import { IHostService } from 'vs/workbench/services/host/browser/host'; @@ -609,7 +609,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo id: SyncResource.GlobalState, label: getSyncAreaLabel(SyncResource.GlobalState), }]; - if (!this.environmentService.isBuilt || (this.productService.enableSyncingProfiles && this.configurationService.getValue('settingsSync.enableSyncingProfiles'))) { + if (this.userDataProfilesService.isEnabled() && (!this.environmentService.isBuilt || this.productService.enableSyncingProfiles)) { result.push({ id: SyncResource.Profiles, label: getSyncAreaLabel(SyncResource.Profiles), @@ -1127,7 +1127,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo super({ id: 'workbench.userDataSync.actions.help', title: { value: SYNC_TITLE, original: 'Settings Sync' }, - category: CATEGORIES.Help, + category: Categories.Help, menu: [{ id: MenuId.CommandPalette, when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized)), @@ -1139,7 +1139,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo MenuRegistry.appendMenuItem(MenuId.ViewContainerTitle, { command: { id: 'workbench.userDataSync.actions.help', - title: CATEGORIES.Help.value + title: Categories.Help.value }, when: ContextKeyExpr.equals('viewContainer', SYNC_VIEW_CONTAINER_ID), group: '1_help', @@ -1152,7 +1152,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo constructor() { super({ id: 'workbench.userDataSync.actions.acceptMerges', - title: localize('accept merges title', "Accept Merge"), + title: localize('complete merges title', "Complete Merge"), menu: [{ id: MenuId.EditorContent, when: ContextKeyExpr.and(ctxIsMergeResultEditor, ContextKeyExpr.regex(ctxMergeBaseUri.key, new RegExp(`^${USER_DATA_SYNC_SCHEME}:`))), diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger.ts index 210601cf0db..2fd68ddc29c 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger.ts @@ -65,4 +65,3 @@ export class UserDataSyncTrigger extends Disposable implements IWorkbenchContrib return undefined; } } - diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts index 1e10f688e2f..6ba7ca6f86d 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts @@ -345,7 +345,7 @@ abstract class UserDataSyncActivityViewDataProvider implements ITreeViewDataProv description: fromNow(syncResourceHandle.created, true), themeIcon: FolderThemeIcon, syncResourceHandle, - contextValue: `sync-resource-${syncResourceHandle.syncResource}` + contextValue: `sync-resource-${syncResourceHandle.syncResource.syncResource}` }; }); } diff --git a/src/vs/workbench/contrib/userDataSync/electron-sandbox/userDataSync.contribution.ts b/src/vs/workbench/contrib/userDataSync/electron-sandbox/userDataSync.contribution.ts index 2cd27d2465b..6840848e322 100644 --- a/src/vs/workbench/contrib/userDataSync/electron-sandbox/userDataSync.contribution.ts +++ b/src/vs/workbench/contrib/userDataSync/electron-sandbox/userDataSync.contribution.ts @@ -29,7 +29,7 @@ class UserDataSyncServicesContribution implements IWorkbenchContribution { } const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(UserDataSyncServicesContribution, 'UserDataSyncServicesContribution', LifecyclePhase.Starting); +workbenchRegistry.registerWorkbenchContribution(UserDataSyncServicesContribution, LifecyclePhase.Starting); registerAction2(class OpenSyncBackupsFolder extends Action2 { constructor() { diff --git a/src/vs/workbench/contrib/watermark/browser/watermark.ts b/src/vs/workbench/contrib/watermark/browser/watermark.ts index 592b032dfee..d20cf69cc08 100644 --- a/src/vs/workbench/contrib/watermark/browser/watermark.ts +++ b/src/vs/workbench/contrib/watermark/browser/watermark.ts @@ -210,7 +210,7 @@ export class WatermarkContribution extends Disposable implements IWorkbenchContr } Registry.as(WorkbenchExtensions.Workbench) - .registerWorkbenchContribution(WatermarkContribution, 'WatermarkContribution', LifecyclePhase.Restored); + .registerWorkbenchContribution(WatermarkContribution, LifecyclePhase.Restored); Registry.as(ConfigurationExtensions.Configuration) .registerConfiguration({ diff --git a/src/vs/workbench/contrib/webview/browser/overlayWebview.ts b/src/vs/workbench/contrib/webview/browser/overlayWebview.ts index 24aeee9cfa7..50678fca436 100644 --- a/src/vs/workbench/contrib/webview/browser/overlayWebview.ts +++ b/src/vs/workbench/contrib/webview/browser/overlayWebview.ts @@ -31,7 +31,6 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { private _html: string = ''; private _initialScrollProgress: number = 0; private _state: string | undefined = undefined; - private _repositionTimeout: any | undefined = undefined; private _extension: WebviewExtensionDescription | undefined; private _contentOptions: WebviewContentOptions; @@ -42,8 +41,7 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { private readonly _scopedContextKeyService = this._register(new MutableDisposable()); private _findWidgetVisible: IContextKey | undefined; private _findWidgetEnabled: IContextKey | undefined; - // This isn't associated with an editor action so doesn't need to be a context key - private _findActiveWhenHidden: boolean | undefined = false; + private _shouldShowFindWidgetOnRestore = false; public readonly id: string; public readonly providedViewType?: string; @@ -86,8 +84,6 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { } this._firstLoadPendingMessages.clear(); - clearTimeout(this._repositionTimeout); - this._onDidDispose.fire(); super.dispose(); @@ -127,8 +123,10 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { this._scopedContextKeyService.clear(); this._scopedContextKeyService.value = contextKeyService.createScoped(this.container); + const wasFindVisible = this._findWidgetVisible?.get(); this._findWidgetVisible?.reset(); this._findWidgetVisible = KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE.bindTo(contextKeyService); + this._findWidgetVisible.set(!!wasFindVisible); this._findWidgetEnabled?.reset(); this._findWidgetEnabled = KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_ENABLED.bindTo(contextKeyService); @@ -149,10 +147,12 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { if (this._container) { this._container.style.visibility = 'hidden'; } + if (this._options.retainContextWhenHidden) { // https://github.com/microsoft/vscode/issues/157424 // We need to record the current state when retaining context so we can try to showFind() when showing webview again - this.hideFind(); + this._shouldShowFindWidgetOnRestore = !!this._findWidgetVisible?.get(); + this.hideFind(false); } else { this._webview.clear(); this._webviewEvents.clear(); @@ -160,17 +160,6 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { } public layoutWebviewOverElement(element: HTMLElement, dimension?: Dimension, clippingContainer?: HTMLElement) { - this.doLayoutWebviewOverElement(element, dimension, clippingContainer); - - // Temporary fix for https://github.com/microsoft/vscode/issues/110450 - // There is an animation that lasts about 200ms, update the webview positioning once this animation is complete. - clearTimeout(this._repositionTimeout); - this._repositionTimeout = setTimeout(() => { - this.doLayoutWebviewOverElement(element, dimension, clippingContainer); - }, 200); - } - - public doLayoutWebviewOverElement(element: HTMLElement, dimension?: Dimension, clippingContainer?: HTMLElement) { if (!this._container || !this._container.parentElement) { return; } @@ -213,9 +202,6 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { this._webview.value.setContextKeyService(this._scopedContextKeyService.value); } - // https://github.com/microsoft/vscode/issues/157424 - this.tryShowFind(); - if (this._html) { webview.html = this._html; } @@ -257,6 +243,13 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { this._firstLoadPendingMessages.clear(); } + // https://github.com/microsoft/vscode/issues/157424 + if (this.options.retainContextWhenHidden && this._shouldShowFindWidgetOnRestore) { + this.showFind(false); + // Reset + this._shouldShowFindWidgetOnRestore = false; + } + this.container.style.visibility = 'visible'; } @@ -345,31 +338,16 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { undo(): void { this._webview.value?.undo(); } redo(): void { this._webview.value?.redo(); } - /** - * Only meant to be used when we're showing webview as an attempt to reload the previously hidden - * find widget when retaining context - */ - tryShowFind() { - const shouldShowFind: boolean | undefined = ( - (this.options.retainContextWhenHidden && this._findActiveWhenHidden) - ); - - if (shouldShowFind) { - this.showFind(); - } - } - - showFind() { + showFind(animated = true) { if (this._webview.value) { - this._webview.value.showFind(); + this._webview.value.showFind(animated); this._findWidgetVisible?.set(true); } } - hideFind() { - this._findActiveWhenHidden = this._findWidgetVisible?.get(); + hideFind(animated = true) { this._findWidgetVisible?.reset(); - this._webview.value?.hideFind(); + this._webview.value?.hideFind(animated); } runFindAction(previous: boolean): void { this._webview.value?.runFindAction(previous); } diff --git a/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html b/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html index cd4afd479a5..12b1fdcda44 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html +++ b/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html @@ -512,8 +512,8 @@ } // Re-add new properties - for (const variable of Object.keys(initData.styles)) { - documentStyle.setProperty(`--${variable}`, initData.styles[variable]); + for (const [variable, value] of Object.entries(initData.styles)) { + documentStyle.setProperty(`--${variable}`, value); } } }; diff --git a/src/vs/workbench/contrib/webview/browser/pre/index.html b/src/vs/workbench/contrib/webview/browser/pre/index.html index 6c90f4d295a..3b364180027 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/index.html +++ b/src/vs/workbench/contrib/webview/browser/pre/index.html @@ -5,7 +5,7 @@ + content="default-src 'none'; script-src 'sha256-WedTLLcy1WD2tkMNIDWTY7p5GAXwF+3pVt9Bd3nh6x4=' 'self'; frame-src 'self'; style-src 'unsafe-inline';"> | undefined} - */ - get(requestId) { - const entry = this.map.get(requestId); - return entry && entry.promise; - } - /** * @returns {{ requestId: number, promise: Promise }} */ @@ -131,39 +122,37 @@ const methodNotAllowed = () => sw.addEventListener('message', async (event) => { switch (event.data.channel) { - case 'version': - { - const source = /** @type {Client} */ (event.source); - sw.clients.get(source.id).then(client => { - if (client) { - client.postMessage({ - channel: 'version', - version: VERSION - }); - } - }); - return; - } - case 'did-load-resource': - { - /** @type {ResourceResponse} */ - const response = event.data.data; - if (!resourceRequestStore.resolve(response.id, response)) { - console.log('Could not resolve unknown resource', response.path); + case 'version': { + const source = /** @type {Client} */ (event.source); + sw.clients.get(source.id).then(client => { + if (client) { + client.postMessage({ + channel: 'version', + version: VERSION + }); } - return; + }); + return; + } + case 'did-load-resource': { + /** @type {ResourceResponse} */ + const response = event.data.data; + if (!resourceRequestStore.resolve(response.id, response)) { + console.log('Could not resolve unknown resource', response.path); } - case 'did-load-localhost': - { - const data = event.data.data; - if (!localhostRequestStore.resolve(data.id, data.location)) { - console.log('Could not resolve unknown localhost', data.origin); - } - return; + return; + } + case 'did-load-localhost': { + const data = event.data.data; + if (!localhostRequestStore.resolve(data.id, data.location)) { + console.log('Could not resolve unknown localhost', data.origin); } - default: + return; + } + default: { console.log('Unknown message'); return; + } } }); @@ -183,8 +172,9 @@ sw.addEventListener('fetch', (event) => { query: requestUrl.search.replace(/^\?/, ''), })); } - default: + default: { return event.respondWith(methodNotAllowed()); + } } } @@ -195,16 +185,17 @@ sw.addEventListener('fetch', (event) => { if (requestUrl.origin !== sw.origin && requestUrl.host === remoteAuthority) { switch (event.request.method) { case 'GET': - case 'HEAD': + case 'HEAD': { return event.respondWith(processResourceRequest(event, { path: requestUrl.pathname, scheme: requestUrl.protocol.slice(0, requestUrl.protocol.length - 1), authority: requestUrl.host, query: requestUrl.search.replace(/^\?/, ''), })); - - default: + } + default: { return event.respondWith(methodNotAllowed()); + } } } diff --git a/src/vs/workbench/contrib/webview/browser/resourceLoading.ts b/src/vs/workbench/contrib/webview/browser/resourceLoading.ts index cdff87cccd0..602e3610c1b 100644 --- a/src/vs/workbench/contrib/webview/browser/resourceLoading.ts +++ b/src/vs/workbench/contrib/webview/browser/resourceLoading.ts @@ -7,7 +7,7 @@ import { VSBufferReadableStream } from 'vs/base/common/buffer'; import { CancellationToken } from 'vs/base/common/cancellation'; import { isUNC } from 'vs/base/common/extpath'; import { Schemas } from 'vs/base/common/network'; -import { sep } from 'vs/base/common/path'; +import { normalize, sep } from 'vs/base/common/path'; import { URI } from 'vs/base/common/uri'; import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; @@ -103,8 +103,8 @@ function containsResource(root: URI, resource: URI): boolean { return false; } - let rootPath = root.fsPath + (root.fsPath.endsWith(sep) ? '' : sep); - let resourceFsPath = resource.fsPath; + let resourceFsPath = normalize(resource.fsPath); + let rootPath = normalize(root.fsPath + (root.fsPath.endsWith(sep) ? '' : sep)); if (isUNC(root.fsPath) && isUNC(resource.fsPath)) { rootPath = rootPath.toLowerCase(); diff --git a/src/vs/workbench/contrib/webview/browser/webview.ts b/src/vs/workbench/contrib/webview/browser/webview.ts index 83296c95ac4..b08829ae769 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.ts @@ -10,10 +10,13 @@ import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import { isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; +import { generateUuid } from 'vs/base/common/uuid'; import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IWebviewPortMapping } from 'vs/platform/webview/common/webviewPortMapping'; +import { Memento, MementoObject } from 'vs/workbench/common/memento'; import { WebviewInitInfo } from 'vs/workbench/contrib/webview/browser/webviewElement'; /** @@ -85,9 +88,6 @@ export interface WebviewOptions { transformCssVariables?(styles: WebviewStyles): WebviewStyles; } -/** - * - */ export interface WebviewContentOptions { /** * Should the webview allow `acquireVsCodeApi` to be called multiple times? Defaults to false. @@ -192,8 +192,8 @@ export interface IWebview extends IDisposable { focus(): void; reload(): void; - showFind(): void; - hideFind(): void; + showFind(animated?: boolean): void; + hideFind(animated?: boolean): void; runFindAction(previous: boolean): void; selectAll(): void; @@ -272,3 +272,40 @@ export interface IOverlayWebview extends IWebview { */ layoutWebviewOverElement(element: HTMLElement, dimension?: Dimension, clippingContainer?: HTMLElement): void; } + +/** + * Stores the unique origins for a webview. + * + * These are randomly generated, but keyed on extension and webview viewType. + */ +export class WebviewOriginStore { + + private readonly memento: Memento; + private readonly state: MementoObject; + + constructor( + rootStorageKey: string, + @IStorageService storageService: IStorageService, + ) { + this.memento = new Memento(rootStorageKey, storageService); + this.state = this.memento.getMemento(StorageScope.APPLICATION, StorageTarget.MACHINE); + } + + public getOrigin(viewType: string, extId: ExtensionIdentifier | undefined): string { + const key = this.getKey(viewType, extId); + + const existing = this.state[key]; + if (existing && typeof existing === 'string') { + return existing; + } + + const newOrigin = generateUuid(); + this.state[key] = newOrigin; + this.memento.saveMemento(); + return newOrigin; + } + + private getKey(viewType: string, extId: ExtensionIdentifier | undefined): string { + return JSON.stringify({ viewType, extension: extId?.value }); + } +} diff --git a/src/vs/workbench/contrib/webview/browser/webview.web.contribution.ts b/src/vs/workbench/contrib/webview/browser/webview.web.contribution.ts index 2b1bb413023..9ac2a8ce935 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.web.contribution.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.web.contribution.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWebviewService } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewService } from './webviewService'; -registerSingleton(IWebviewService, WebviewService, true); +registerSingleton(IWebviewService, WebviewService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index 88266a40eeb..51b3d4db948 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -6,18 +6,16 @@ import { isFirefox } from 'vs/base/browser/browser'; import { addDisposableListener, EventType } from 'vs/base/browser/dom'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; -import { IAction } from 'vs/base/common/actions'; import { ThrottledDelayer } from 'vs/base/common/async'; import { streamToBuffer, VSBufferReadableStream } from 'vs/base/common/buffer'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { COI, Schemas } from 'vs/base/common/network'; +import { COI } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import { localize } from 'vs/nls'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; -import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -32,14 +30,14 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ITunnelService } from 'vs/platform/tunnel/common/tunnel'; import { WebviewPortMappingManager } from 'vs/platform/webview/common/webviewPortMapping'; import { parentOriginHash } from 'vs/workbench/browser/webview'; -import { asWebviewUri, decodeAuthority, webviewGenericCspSource, webviewRootResourceAuthority } from 'vs/workbench/common/webview'; +import { decodeAuthority, webviewGenericCspSource, webviewRootResourceAuthority } from 'vs/workbench/common/webview'; import { loadLocalResource, WebviewResourceResponse } from 'vs/workbench/contrib/webview/browser/resourceLoading'; import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/browser/themeing'; import { areWebviewContentOptionsEqual, IWebview, WebviewContentOptions, WebviewExtensionDescription, WebviewMessageReceivedEvent, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewFindDelegate, WebviewFindWidget } from 'vs/workbench/contrib/webview/browser/webviewFindWidget'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -export const enum WebviewMessageChannels { +const enum WebviewMessageChannels { onmessage = 'onmessage', didClickLink = 'did-click-link', didScroll = 'did-scroll', @@ -305,14 +303,14 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD this.handleFocusChange(true); })); - this._register(this.on(WebviewMessageChannels.wheel, (event: IMouseWheelEvent) => { - this._onDidWheel.fire(event); - })); - this._register(this.on(WebviewMessageChannels.didBlur, () => { this.handleFocusChange(false); })); + this._register(this.on(WebviewMessageChannels.wheel, (event: IMouseWheelEvent) => { + this._onDidWheel.fire(event); + })); + this._register(this.on(WebviewMessageChannels.didFind, (didFind: boolean) => { this._hasFindResult.fire(didFind); })); @@ -340,19 +338,14 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD return; } const elementBox = this.element.getBoundingClientRect(); + const contextKeyService = this._contextKeyService!.createOverlay([ + ...Object.entries(data.context), + [webviewIdContext, this.providedViewType], + ]); contextMenuService.showContextMenu({ - getActions: () => { - const contextKeyService = this._contextKeyService!.createOverlay([ - ...Object.entries(data.context), - [webviewIdContext, this.providedViewType], - ]); - - const result: IAction[] = []; - const menu = menuService.createMenu(MenuId.WebviewContext, contextKeyService); - createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result); - menu.dispose(); - return result; - }, + menuId: MenuId.WebviewContext, + menuActionOptions: { shouldForwardArgs: true }, + contextKeyService, getActionsContext: (): WebviewActionContext => ({ ...data.context, webview: this.providedViewType }), getAnchor: () => ({ x: elementBox.x + data.clientX, @@ -386,7 +379,6 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD })); this._register(Event.runAndSubscribe(webviewThemeDataProvider.onThemeDataChanged, () => this.style())); - this._register(_accessibilityService.onDidChangeReducedMotion(() => this.style())); this._register(_accessibilityService.onDidChangeScreenReaderOptimized(() => this.style())); @@ -475,7 +467,7 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD return this._send('message', { message, transfer }); } - protected async _send(channel: string, data?: any, transferable: Transferable[] = []): Promise { + private async _send(channel: string, data?: any, transferable: Transferable[] = []): Promise { if (this._state.type === WebviewState.Type.Initializing) { let resolve: (x: boolean) => void; const promise = new Promise(r => resolve = r); @@ -532,7 +524,6 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD params.purpose = options.purpose; } - COI.addSearchParam(params, true, true); const queryString = new URLSearchParams(params).toString(); @@ -552,15 +543,17 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD parent.appendChild(this._webviewFindWidget.getDomNode()); } - [EventType.MOUSE_DOWN, EventType.MOUSE_MOVE, EventType.DROP].forEach(eventName => { + for (const eventName of [EventType.MOUSE_DOWN, EventType.MOUSE_MOVE, EventType.DROP]) { this._register(addDisposableListener(parent, eventName, () => { this.stopBlockingIframeDragEvents(); })); - }); + } - [parent, window].forEach(node => this._register(addDisposableListener(node as HTMLElement, EventType.DRAG_END, () => { - this.stopBlockingIframeDragEvents(); - }))); + for (const node of [parent, window]) { + this._register(addDisposableListener(node, EventType.DRAG_END, () => { + this.stopBlockingIframeDragEvents(); + })); + } parent.appendChild(this.element); } @@ -603,7 +596,7 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD return false; } - protected on(channel: WebviewMessageChannels, handler: (data: T, e: MessageEvent) => void): IDisposable { + private on(channel: WebviewMessageChannels, handler: (data: T, e: MessageEvent) => void): IDisposable { let handlers = this._messageHandlers.get(channel); if (!handlers) { handlers = new Set(); @@ -652,37 +645,14 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD } public set html(value: string) { - const rewrittenHtml = this.rewriteVsCodeResourceUrls(value); this.doUpdateContent({ - html: rewrittenHtml, + html: value, options: this.content.options, state: this.content.state, }); this._onDidHtmlChange.fire(value); } - private rewriteVsCodeResourceUrls(value: string): string { - const isRemote = this.extension?.location?.scheme === Schemas.vscodeRemote; - const remoteAuthority = this.extension?.location?.scheme === Schemas.vscodeRemote ? this.extension.location.authority : undefined; - return value - .replace(/(["'])(?:vscode-resource):(\/\/([^\s\/'"]+?)(?=\/))?([^\s'"]+?)(["'])/gi, (_match, startQuote, _1, scheme, path, endQuote) => { - const uri = URI.from({ - scheme: scheme || 'file', - path: decodeURIComponent(path), - }); - const webviewUri = asWebviewUri(uri, { isRemote, authority: remoteAuthority }).toString(); - return `${startQuote}${webviewUri}${endQuote}`; - }) - .replace(/(["'])(?:vscode-webview-resource):(\/\/[^\s\/'"]+\/([^\s\/'"]+?)(?=\/))?([^\s'"]+?)(["'])/gi, (_match, startQuote, _1, scheme, path, endQuote) => { - const uri = URI.from({ - scheme: scheme || 'file', - path: decodeURIComponent(path), - }); - const webviewUri = asWebviewUri(uri, { isRemote, authority: remoteAuthority }).toString(); - return `${startQuote}${webviewUri}${endQuote}`; - }); - } - public set contentOptions(options: WebviewContentOptions) { this._logService.debug(`Webview(${this.id}): will update content options`); @@ -754,7 +724,7 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD this._webviewFindWidget?.updateTheme(this.webviewThemeDataProvider.getTheme()); } - private handleFocusChange(isFocused: boolean): void { + protected handleFocusChange(isFocused: boolean): void { this._focused = isFocused; if (isFocused) { this._onDidFocus.fire(); @@ -957,12 +927,12 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD this._onDidStopFind.fire(); } - public showFind() { - this._webviewFindWidget?.reveal(); + public showFind(animated = true) { + this._webviewFindWidget?.reveal(undefined, animated); } - public hideFind() { - this._webviewFindWidget?.hide(); + public hideFind(animated = true) { + this._webviewFindWidget?.hide(animated); } public runFindAction(previous: boolean) { diff --git a/src/vs/workbench/contrib/webview/browser/webviewFindWidget.ts b/src/vs/workbench/contrib/webview/browser/webviewFindWidget.ts index e75d7ed26cd..29de5c74126 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewFindWidget.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewFindWidget.ts @@ -53,8 +53,8 @@ export class WebviewFindWidget extends SimpleFindWidget { } } - public override hide() { - super.hide(); + public override hide(animated = true) { + super.hide(animated); this._delegate.stopFind(true); this._delegate.focus(); } diff --git a/src/vs/workbench/contrib/webview/electron-sandbox/webview.contribution.ts b/src/vs/workbench/contrib/webview/electron-sandbox/webview.contribution.ts index 97ca0f24cdc..a19e67cf9c5 100644 --- a/src/vs/workbench/contrib/webview/electron-sandbox/webview.contribution.ts +++ b/src/vs/workbench/contrib/webview/electron-sandbox/webview.contribution.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { registerAction2 } from 'vs/platform/actions/common/actions'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWebviewService } from 'vs/workbench/contrib/webview/browser/webview'; import * as webviewCommands from 'vs/workbench/contrib/webview/electron-sandbox/webviewCommands'; import { ElectronWebviewService } from 'vs/workbench/contrib/webview/electron-sandbox/webviewService'; -registerSingleton(IWebviewService, ElectronWebviewService, true); +registerSingleton(IWebviewService, ElectronWebviewService, InstantiationType.Delayed); registerAction2(webviewCommands.OpenWebviewDeveloperToolsAction); diff --git a/src/vs/workbench/contrib/webview/electron-sandbox/webviewCommands.ts b/src/vs/workbench/contrib/webview/electron-sandbox/webviewCommands.ts index 086853c99d9..222d6088bfc 100644 --- a/src/vs/workbench/contrib/webview/electron-sandbox/webviewCommands.ts +++ b/src/vs/workbench/contrib/webview/electron-sandbox/webviewCommands.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { Action2 } from 'vs/platform/actions/common/actions'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; export class OpenWebviewDeveloperToolsAction extends Action2 { @@ -15,7 +15,7 @@ export class OpenWebviewDeveloperToolsAction extends Action2 { super({ id: 'workbench.action.webview.openDeveloperTools', title: { value: nls.localize('openToolsLabel', "Open Webview Developer Tools"), original: 'Open Webview Developer Tools' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true }); } diff --git a/src/vs/workbench/contrib/webview/electron-sandbox/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-sandbox/webviewElement.ts index c119358978c..bdc676cff07 100644 --- a/src/vs/workbench/contrib/webview/electron-sandbox/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-sandbox/webviewElement.ts @@ -23,7 +23,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ITunnelService } from 'vs/platform/tunnel/common/tunnel'; import { FindInFrameOptions, IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService'; import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/browser/themeing'; -import { WebviewElement, WebviewInitInfo, WebviewMessageChannels } from 'vs/workbench/contrib/webview/browser/webviewElement'; +import { WebviewElement, WebviewInitInfo } from 'vs/workbench/contrib/webview/browser/webviewElement'; import { WindowIgnoreMenuShortcutsManager } from 'vs/workbench/contrib/webview/electron-sandbox/windowIgnoreMenuShortcutsManager'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -68,14 +68,6 @@ export class ElectronWebviewElement extends WebviewElement { this._webviewMainService = ProxyChannel.toService(mainProcessService.getChannel('webview')); - this._register(this.on(WebviewMessageChannels.didFocus, () => { - this._webviewKeyboardHandler.didFocus(); - })); - - this._register(this.on(WebviewMessageChannels.didBlur, () => { - this._webviewKeyboardHandler.didBlur(); - })); - if (initInfo.options.enableFindWidget) { this._register(this.onDidHtmlChange((newContent) => { if (this._findStarted && this._cachedHtmlContent !== newContent) { @@ -167,4 +159,13 @@ export class ElectronWebviewElement extends WebviewElement { }); this._onDidStopFind.fire(); } + + protected override handleFocusChange(isFocused: boolean): void { + super.handleFocusChange(isFocused); + if (isFocused) { + this._webviewKeyboardHandler.didFocus(); + } else { + this._webviewKeyboardHandler.didBlur(); + } + } } diff --git a/src/vs/workbench/contrib/webviewPanel/browser/webviewCommands.ts b/src/vs/workbench/contrib/webviewPanel/browser/webviewCommands.ts index 95a869f9ffa..a9f8728585c 100644 --- a/src/vs/workbench/contrib/webviewPanel/browser/webviewCommands.ts +++ b/src/vs/workbench/contrib/webviewPanel/browser/webviewCommands.ts @@ -10,7 +10,7 @@ import { Action2, MenuId } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { IWebviewService, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_ENABLED, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, IWebview } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewEditor } from 'vs/workbench/contrib/webviewPanel/browser/webviewEditor'; import { WebviewInput } from 'vs/workbench/contrib/webviewPanel/browser/webviewEditorInput'; @@ -110,7 +110,7 @@ export class ReloadWebviewAction extends Action2 { super({ id: ReloadWebviewAction.ID, title: { value: ReloadWebviewAction.LABEL, original: 'Reload Webviews' }, - category: CATEGORIES.Developer, + category: Categories.Developer, menu: [{ id: MenuId.CommandPalette }] diff --git a/src/vs/workbench/contrib/webviewPanel/browser/webviewEditorInputSerializer.ts b/src/vs/workbench/contrib/webviewPanel/browser/webviewEditorInputSerializer.ts index 9adca06119c..5e0aa8c5be8 100644 --- a/src/vs/workbench/contrib/webviewPanel/browser/webviewEditorInputSerializer.ts +++ b/src/vs/workbench/contrib/webviewPanel/browser/webviewEditorInputSerializer.ts @@ -77,7 +77,7 @@ export class WebviewEditorInputSerializer implements IEditorSerializer { serializedEditorInput: string ): WebviewInput { const data = this.fromJson(JSON.parse(serializedEditorInput)); - return this._webviewWorkbenchService.reviveWebview({ + return this._webviewWorkbenchService.openRevivedWebview({ webviewInitInfo: { id: data.id, providedViewType: data.providedId, diff --git a/src/vs/workbench/contrib/webviewPanel/browser/webviewPanel.contribution.ts b/src/vs/workbench/contrib/webviewPanel/browser/webviewPanel.contribution.ts index 0e6e8e11e66..8d49c0221a1 100644 --- a/src/vs/workbench/contrib/webviewPanel/browser/webviewPanel.contribution.ts +++ b/src/vs/workbench/contrib/webviewPanel/browser/webviewPanel.contribution.ts @@ -8,7 +8,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; import { registerAction2 } from 'vs/platform/actions/common/actions'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Registry } from 'vs/platform/registry/common/platform'; import { EditorPaneDescriptor, IEditorPaneRegistry } from 'vs/workbench/browser/editor'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; @@ -82,13 +82,13 @@ class WebviewPanelContribution extends Disposable implements IWorkbenchContribut } const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchContributionsRegistry.registerWorkbenchContribution(WebviewPanelContribution, 'WebviewPanelContribution', LifecyclePhase.Starting); +workbenchContributionsRegistry.registerWorkbenchContribution(WebviewPanelContribution, LifecyclePhase.Starting); Registry.as(EditorExtensions.EditorFactory).registerEditorSerializer( WebviewEditorInputSerializer.ID, WebviewEditorInputSerializer); -registerSingleton(IWebviewWorkbenchService, WebviewEditorService, true); +registerSingleton(IWebviewWorkbenchService, WebviewEditorService, InstantiationType.Delayed); registerAction2(ShowWebViewEditorFindWidgetAction); registerAction2(HideWebViewEditorFindCommand); diff --git a/src/vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService.ts b/src/vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService.ts index 97e206c6ec9..ff558a0c806 100644 --- a/src/vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService.ts +++ b/src/vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService.ts @@ -24,26 +24,45 @@ import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsSe import { ACTIVE_GROUP_TYPE, IEditorService, SIDE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService'; import { WebviewInput, WebviewInputInitInfo } from './webviewEditorInput'; -export const IWebviewWorkbenchService = createDecorator('webviewEditorService'); - -export interface ICreateWebViewShowOptions { +export interface IWebViewShowOptions { readonly group?: IEditorGroup | GroupIdentifier | ACTIVE_GROUP_TYPE | SIDE_GROUP_TYPE; readonly preserveFocus?: boolean; } +export const IWebviewWorkbenchService = createDecorator('webviewEditorService'); + +/** + * Service responsible for showing and managing webview editors in the workbench. + */ export interface IWebviewWorkbenchService { readonly _serviceBrand: undefined; + /** + * Manages setting the icons show for a given webview. + */ readonly iconManager: WebviewIconManager; - createWebview( + /** + * Event fired when focus switches to a different webview editor. + * + * Fires `undefined` if focus switches to a non-webview editor. + */ + readonly onDidChangeActiveWebviewEditor: Event; + + /** + * Create a new webview editor and open it in the workbench. + */ + openWebview( webviewInitInfo: WebviewInitInfo, viewType: string, title: string, - showOptions: ICreateWebViewShowOptions, + showOptions: IWebViewShowOptions, ): WebviewInput; - reviveWebview(options: { + /** + * Open a webview that is being restored from serialization. + */ + openRevivedWebview(options: { webviewInitInfo: WebviewInitInfo; viewType: string; title: string; @@ -52,49 +71,57 @@ export interface IWebviewWorkbenchService { group: number | undefined; }): WebviewInput; + /** + * Reveal an already opened webview editor in the workbench. + */ revealWebview( webview: WebviewInput, group: IEditorGroup | GroupIdentifier | ACTIVE_GROUP_TYPE | SIDE_GROUP_TYPE, preserveFocus: boolean ): void; - registerResolver( - resolver: WebviewResolver - ): IDisposable; + /** + * Register a new {@link WebviewResolver}. + * + * If there are any webviews awaiting revival that this resolver can handle, they will be resolved by it. + */ + registerResolver(resolver: WebviewResolver): IDisposable; - shouldPersist( - input: WebviewInput - ): boolean; + /** + * Check if a webview should be serialized across window reloads. + */ + shouldPersist(input: WebviewInput): boolean; - resolveWebview( - webview: WebviewInput, - ): CancelablePromise; - - readonly onDidChangeActiveWebviewEditor: Event; + /** + * Try to resolve a webview. This will block until a resolver is registered for the webview. + */ + resolveWebview(webview: WebviewInput, token: CancellationToken): Promise; } +/** + * Handles filling in the content of webview before it can be shown to the user. + */ export interface WebviewResolver { - canResolve( - webview: WebviewInput, - ): boolean; + /** + * Returns true if the resolver can resolve the given webview. + */ + canResolve(webview: WebviewInput): boolean; - resolveWebview( - webview: WebviewInput, - cancellation: CancellationToken, - ): Promise; + /** + * Resolves the webview. + */ + resolveWebview(webview: WebviewInput, token: CancellationToken): Promise; } function canRevive(reviver: WebviewResolver, webview: WebviewInput): boolean { return reviver.canResolve(webview); } - export class LazilyResolvedWebviewEditorInput extends WebviewInput { #resolved = false; #resolvePromise?: CancelablePromise; - constructor( init: WebviewInputInitInfo, webview: IOverlayWebview, @@ -113,7 +140,7 @@ export class LazilyResolvedWebviewEditorInput extends WebviewInput { public override async resolve() { if (!this.#resolved) { this.#resolved = true; - this.#resolvePromise = this._webviewWorkbenchService.resolveWebview(this); + this.#resolvePromise = createCancelablePromise(token => this._webviewWorkbenchService.resolveWebview(this, token)); try { await this.#resolvePromise; } catch (e) { @@ -248,11 +275,11 @@ export class WebviewEditorService extends Disposable implements IWebviewWorkbenc } } - public createWebview( + public openWebview( webviewInitInfo: WebviewInitInfo, viewType: string, title: string, - showOptions: ICreateWebViewShowOptions, + showOptions: IWebViewShowOptions, ): WebviewInput { const webview = this._webviewService.createWebviewOverlay(webviewInitInfo); const webviewInput = this._instantiationService.createInstance(WebviewInput, { id: webviewInitInfo.id, viewType, name: title, providedId: webviewInitInfo.providedViewType }, webview, this.iconManager); @@ -295,7 +322,7 @@ export class WebviewEditorService extends Disposable implements IWebviewWorkbenc return webview; } - public reviveWebview(options: { + public openRevivedWebview(options: { webviewInitInfo: WebviewInitInfo; viewType: string; title: string; @@ -315,9 +342,7 @@ export class WebviewEditorService extends Disposable implements IWebviewWorkbenc return webviewInput; } - public registerResolver( - reviver: WebviewResolver - ): IDisposable { + public registerResolver(reviver: WebviewResolver): IDisposable { this._revivers.add(reviver); const cts = new CancellationTokenSource(); @@ -329,10 +354,8 @@ export class WebviewEditorService extends Disposable implements IWebviewWorkbenc }); } - public shouldPersist( - webview: WebviewInput - ): boolean { - // Revived webviews may not have an actively registered reviver but we still want to presist them + public shouldPersist(webview: WebviewInput): boolean { + // Revived webviews may not have an actively registered reviver but we still want to persist them // since a reviver should exist when it is actually needed. if (webview instanceof LazilyResolvedWebviewEditorInput) { return true; @@ -341,27 +364,22 @@ export class WebviewEditorService extends Disposable implements IWebviewWorkbenc return Iterable.some(this._revivers.values(), reviver => canRevive(reviver, webview)); } - private async tryRevive( - webview: WebviewInput, - cancellation: CancellationToken, - ): Promise { + private async tryRevive(webview: WebviewInput, token: CancellationToken): Promise { for (const reviver of this._revivers.values()) { if (canRevive(reviver, webview)) { - await reviver.resolveWebview(webview, cancellation); + await reviver.resolveWebview(webview, token); return true; } } return false; } - public resolveWebview(webview: WebviewInput): CancelablePromise { - return createCancelablePromise(async (cancellation) => { - const didRevive = await this.tryRevive(webview, cancellation); - if (!didRevive) { - // A reviver may not be registered yet. Put into pool and resolve promise when we can revive - return this._revivalPool.enqueueForRestoration(webview, cancellation); - } - }); + public async resolveWebview(webview: WebviewInput, token: CancellationToken): Promise { + const didRevive = await this.tryRevive(webview, token); + if (!didRevive && !token.isCancellationRequested) { + // A reviver may not be registered yet. Put into pool and resolve promise when we can revive + return this._revivalPool.enqueueForRestoration(webview, token); + } } public setIcons(id: string, iconPath: WebviewIcons | undefined): void { diff --git a/src/vs/workbench/contrib/webviewView/browser/webviewView.contribution.ts b/src/vs/workbench/contrib/webviewView/browser/webviewView.contribution.ts index 9dd05114160..91d7abcc1da 100644 --- a/src/vs/workbench/contrib/webviewView/browser/webviewView.contribution.ts +++ b/src/vs/workbench/contrib/webviewView/browser/webviewView.contribution.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWebviewViewService, WebviewViewService } from 'vs/workbench/contrib/webviewView/browser/webviewViewService'; -registerSingleton(IWebviewViewService, WebviewViewService, true); +registerSingleton(IWebviewViewService, WebviewViewService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts b/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts index d72f98cb736..e9cc85ee973 100644 --- a/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts +++ b/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts @@ -24,7 +24,7 @@ import { ViewPane } from 'vs/workbench/browser/parts/views/viewPane'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { Memento, MementoObject } from 'vs/workbench/common/memento'; import { IViewBadge, IViewDescriptorService, IViewsService } from 'vs/workbench/common/views'; -import { IOverlayWebview, IWebviewService, WebviewContentPurpose } from 'vs/workbench/contrib/webview/browser/webview'; +import { IOverlayWebview, IWebviewService, WebviewContentPurpose, WebviewOriginStore } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewWindowDragMonitor } from 'vs/workbench/contrib/webview/browser/webviewWindowDragMonitor'; import { IWebviewViewService, WebviewView } from 'vs/workbench/contrib/webviewView/browser/webviewViewService'; import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; @@ -38,6 +38,13 @@ const storageKeys = { export class WebviewViewPane extends ViewPane { + private static _originStore?: WebviewOriginStore; + + private static getOriginStore(storageService: IStorageService): WebviewOriginStore { + this._originStore ??= new WebviewOriginStore('webviewViews.origins', storageService); + return this._originStore; + } + private readonly _webview = this._register(new MutableDisposable()); private readonly _webviewDisposables = this._register(new DisposableStore()); private _activated = false; @@ -56,24 +63,26 @@ export class WebviewViewPane extends ViewPane { private readonly viewState: MementoObject; private readonly extensionId?: ExtensionIdentifier; + private _repositionTimeout?: any; + constructor( options: IViewletViewOptions, - @IKeybindingService keybindingService: IKeybindingService, - @IContextMenuService contextMenuService: IContextMenuService, @IConfigurationService configurationService: IConfigurationService, @IContextKeyService contextKeyService: IContextKeyService, - @IViewDescriptorService viewDescriptorService: IViewDescriptorService, + @IContextMenuService contextMenuService: IContextMenuService, @IInstantiationService instantiationService: IInstantiationService, + @IKeybindingService keybindingService: IKeybindingService, @IOpenerService openerService: IOpenerService, - @IThemeService themeService: IThemeService, @ITelemetryService telemetryService: ITelemetryService, - @IStorageService storageService: IStorageService, + @IThemeService themeService: IThemeService, + @IViewDescriptorService viewDescriptorService: IViewDescriptorService, + @IActivityService private readonly activityService: IActivityService, @IExtensionService private readonly extensionService: IExtensionService, @IProgressService private readonly progressService: IProgressService, + @IStorageService private readonly storageService: IStorageService, + @IViewsService private readonly viewService: IViewsService, @IWebviewService private readonly webviewService: IWebviewService, @IWebviewViewService private readonly webviewViewService: IWebviewViewService, - @IViewsService private readonly viewService: IViewsService, - @IActivityService private activityService: IActivityService ) { super({ ...options, titleMenuId: MenuId.ViewTitle }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); this.extensionId = options.fromExtensionId; @@ -103,6 +112,8 @@ export class WebviewViewPane extends ViewPane { override dispose() { this._onDispose.fire(); + clearTimeout(this._repositionTimeout); + super.dispose(); } @@ -167,8 +178,10 @@ export class WebviewViewPane extends ViewPane { this._activated = true; const webviewId = generateUuid(); + const origin = WebviewViewPane.getOriginStore(this.storageService).getOrigin(this.id, this.extensionId); const webview = this.webviewService.createWebviewOverlay({ id: webviewId, + origin, providedViewType: this.id, options: { purpose: WebviewContentPurpose.WebviewView }, contentOptions: {}, @@ -277,16 +290,21 @@ export class WebviewViewPane extends ViewPane { return; } - webviewEntry.layoutWebviewOverElement(this._container); - if (!this._rootContainer || !this._rootContainer.isConnected) { this._rootContainer = this.findRootContainer(this._container); } + webviewEntry.layoutWebviewOverElement(this._container); + if (this._rootContainer) { const { top, left, right, bottom } = computeClippingRect(this._container, this._rootContainer); webviewEntry.container.style.clipPath = `polygon(${left}px ${top}px, ${right}px ${top}px, ${right}px ${bottom}px, ${left}px ${bottom}px)`; } + + // Temporary fix for https://github.com/microsoft/vscode/issues/110450 + // There is an animation that lasts about 200ms, update the webview positioning once this animation is complete. + clearTimeout(this._repositionTimeout); + this._repositionTimeout = setTimeout(() => this.layoutWebview(), 200); } private findRootContainer(container: HTMLElement): HTMLElement | undefined { diff --git a/src/vs/workbench/contrib/webviewView/browser/webviewViewService.ts b/src/vs/workbench/contrib/webviewView/browser/webviewViewService.ts index c4ebff8bfee..8d0cb5929d0 100644 --- a/src/vs/workbench/contrib/webviewView/browser/webviewViewService.ts +++ b/src/vs/workbench/contrib/webviewView/browser/webviewViewService.ts @@ -10,35 +10,84 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { IViewBadge } from 'vs/workbench/common/views'; import { IOverlayWebview } from 'vs/workbench/contrib/webview/browser/webview'; -export const IWebviewViewService = createDecorator('webviewViewService'); - +/** + * A webview shown in a view pane. + */ export interface WebviewView { + /** + * The text displayed in the view's title. + */ title?: string; + + /** + * Additional text shown for this view. + */ description?: string; + + /** + * The badge shown for this view. + */ badge?: IViewBadge; + /** + * The webview associated with this webview view. + */ readonly webview: IOverlayWebview; + /** + * Fired when the visibility of the webview view changes. + * + * This can happen when the view itself is hidden, when the view is collapsed, or when the user switches away from + * the view. + */ readonly onDidChangeVisibility: Event; + + /** + * Fired when the webview view has been disposed of. + */ readonly onDispose: Event; + /** + * Dispose of the webview view and clean up any associated resources. + */ dispose(): void; + /** + * Force the webview view to show. + */ show(preserveFocus: boolean): void; } +/** + * Fill in the contents of a newly created webview view. + */ export interface IWebviewViewResolver { + /** + * Fill in the contents of a webview view. + */ resolve(webviewView: WebviewView, cancellation: CancellationToken): Promise; } +export const IWebviewViewService = createDecorator('webviewViewService'); + export interface IWebviewViewService { readonly _serviceBrand: undefined; + /** + * Fired when a resolver has been registered + */ readonly onNewResolverRegistered: Event<{ readonly viewType: string }>; - register(type: string, resolver: IWebviewViewResolver): IDisposable; + /** + * Register a new {@link IWebviewViewResolver webview view resolver}. + */ + register(viewType: string, resolver: IWebviewViewResolver): IDisposable; + /** + * Try to resolve a webview view. The promise will not resolve until a resolver for the webview has been registered + * and run + */ resolve(viewType: string, webview: WebviewView, cancellation: CancellationToken): Promise; } @@ -48,7 +97,7 @@ export class WebviewViewService extends Disposable implements IWebviewViewServic private readonly _resolvers = new Map(); - private readonly _awaitingRevival = new Map void }>(); + private readonly _awaitingRevival = new Map void }>(); private readonly _onNewResolverRegistered = this._register(new Emitter<{ readonly viewType: string }>()); public readonly onNewResolverRegistered = this._onNewResolverRegistered.event; @@ -90,4 +139,3 @@ export class WebviewViewService extends Disposable implements IWebviewViewServic return resolver.resolve(webview, cancellation); } } - diff --git a/src/vs/workbench/contrib/welcomeBanner/browser/welcomeBanner.contribution.ts b/src/vs/workbench/contrib/welcomeBanner/browser/welcomeBanner.contribution.ts index 2aa549c7556..543ff8df1be 100644 --- a/src/vs/workbench/contrib/welcomeBanner/browser/welcomeBanner.contribution.ts +++ b/src/vs/workbench/contrib/welcomeBanner/browser/welcomeBanner.contribution.ts @@ -50,4 +50,4 @@ class WelcomeBannerContribution { } Registry.as(WorkbenchExtensions.Workbench) - .registerWorkbenchContribution(WelcomeBannerContribution, 'WelcomeBannerContribution', LifecyclePhase.Restored); + .registerWorkbenchContribution(WelcomeBannerContribution, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts index 2c18fde1e75..2c5c3b04784 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts @@ -30,6 +30,7 @@ import { IExtensionManagementServerService } from 'vs/workbench/services/extensi import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { StartupPageContribution, } from 'vs/workbench/contrib/welcomeGettingStarted/browser/startupPage'; import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; export * as icons from 'vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedIcons'; @@ -39,7 +40,7 @@ registerAction2(class extends Action2 { super({ id: 'workbench.action.openWalkthrough', title: { value: localize('miGetStarted', "Get Started"), original: 'Get Started' }, - category: localize('help', "Help"), + category: Categories.Help, f1: true, menu: { id: MenuId.MenubarHelpMenu, @@ -123,7 +124,7 @@ Registry.as(EditorExtensions.EditorPane).registerEditorPane ] ); -const category = localize('getStarted', "Get Started"); +const category = { value: localize('getStarted', "Get Started"), original: 'Get Started' }; registerAction2(class extends Action2 { constructor() { @@ -285,7 +286,7 @@ class WorkspacePlatformContribution { } Registry.as(WorkbenchExtensions.Workbench) - .registerWorkbenchContribution(WorkspacePlatformContribution, 'WorkspacePlatformContribution', LifecyclePhase.Restored); + .registerWorkbenchContribution(WorkspacePlatformContribution, LifecyclePhase.Restored); const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); @@ -336,4 +337,4 @@ configurationRegistry.registerConfiguration({ Registry.as(WorkbenchExtensions.Workbench) - .registerWorkbenchContribution(StartupPageContribution, 'StartupPageContribution', LifecyclePhase.Restored); + .registerWorkbenchContribution(StartupPageContribution, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts index 95646185780..37fce5dcf39 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts @@ -1054,9 +1054,9 @@ export class GettingStartedPage extends EditorPane { this.layoutMarkdown?.(); - this.container.classList[size.height <= 600 ? 'add' : 'remove']('height-constrained'); - this.container.classList[size.width <= 400 ? 'add' : 'remove']('width-constrained'); - this.container.classList[size.width <= 800 ? 'add' : 'remove']('width-semi-constrained'); + this.container.classList.toggle('height-constrained', size.height <= 600); + this.container.classList.toggle('width-constrained', size.width <= 400); + this.container.classList.toggle('width-semi-constrained', size.width <= 800); } private updateCategoryProgress() { diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService.ts index cf5d7f7671a..63ceca1e990 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService.ts @@ -24,7 +24,7 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILink, LinkedText, parseLinkedText } from 'vs/base/common/linkedText'; import { walkthroughsExtensionPoint } from 'vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedExtensionPoint'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { dirname } from 'vs/base/common/path'; import { coalesce, flatten } from 'vs/base/common/arrays'; import { IViewsService } from 'vs/workbench/common/views'; @@ -716,4 +716,4 @@ registerAction2(class extends Action2 { } }); -registerSingleton(IWalkthroughsService, WalkthroughsService, false); +registerSingleton(IWalkthroughsService, WalkthroughsService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css b/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css index 7ca7220f167..d9136fe108a 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css @@ -66,7 +66,7 @@ } .monaco-workbench .part.editor>.content .gettingStartedContainer h2 { - font-weight: 200; + font-weight: 400; margin-top: 0; margin-bottom: 5px; font-size: 1.5em; @@ -333,7 +333,7 @@ .monaco-workbench .part.editor>.content .gettingStartedContainer .gettingStartedSlide .getting-started-category .featured .featured-icon { top: -30px; left: 4px; - font-size: 12pt; + font-size: 14px; } .monaco-workbench .part.editor>.content .gettingStartedContainer .gettingStartedSlide .getting-started-category .codicon.hide-category-button { @@ -751,6 +751,8 @@ text-align: center; display: flex; justify-content: center; + align-items: center; + gap: 8px; } .monaco-workbench .part.editor>.content .gettingStartedContainer .gettingStartedSlide .getting-started-checkbox { diff --git a/src/vs/workbench/contrib/welcomeOverlay/browser/welcomeOverlay.ts b/src/vs/workbench/contrib/welcomeOverlay/browser/welcomeOverlay.ts index 78cd06a4e0f..5e7a51a2ba0 100644 --- a/src/vs/workbench/contrib/welcomeOverlay/browser/welcomeOverlay.ts +++ b/src/vs/workbench/contrib/welcomeOverlay/browser/welcomeOverlay.ts @@ -5,24 +5,23 @@ import 'vs/css!./media/welcomeOverlay'; import * as dom from 'vs/base/browser/dom'; -import { Registry } from 'vs/platform/registry/common/platform'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ShowAllCommandsAction } from 'vs/workbench/contrib/quickaccess/browser/commandsQuickAccess'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { localize } from 'vs/nls'; -import { Action } from 'vs/base/common/actions'; -import { IWorkbenchActionRegistry, Extensions, CATEGORIES } from 'vs/workbench/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; +import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { Disposable } from 'vs/base/common/lifecycle'; import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { textPreformatForeground, foreground } from 'vs/platform/theme/common/colorRegistry'; import { Color } from 'vs/base/common/color'; import { Codicon } from 'vs/base/common/codicons'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; const $ = dom.$; @@ -109,38 +108,49 @@ const OVERLAY_VISIBLE = new RawContextKey('interfaceOverviewVisible', f let welcomeOverlay: WelcomeOverlay; -export class WelcomeOverlayAction extends Action { +export class WelcomeOverlayAction extends Action2 { public static readonly ID = 'workbench.action.showInterfaceOverview'; - public static readonly LABEL = localize('welcomeOverlay', "User Interface Overview"); + public static readonly LABEL = { value: localize('welcomeOverlay', "User Interface Overview"), original: 'User Interface Overview' }; constructor( - id: string, - label: string, - @IInstantiationService private readonly instantiationService: IInstantiationService ) { - super(id, label); + super({ + id: WelcomeOverlayAction.ID, + title: WelcomeOverlayAction.LABEL, + category: Categories.Help, + f1: true + }); } - public override run(): Promise { + public override run(accessor: ServicesAccessor): Promise { + const instantiationService = accessor.get(IInstantiationService); if (!welcomeOverlay) { - welcomeOverlay = this.instantiationService.createInstance(WelcomeOverlay); + welcomeOverlay = instantiationService.createInstance(WelcomeOverlay); } welcomeOverlay.show(); return Promise.resolve(); } } -export class HideWelcomeOverlayAction extends Action { +export class HideWelcomeOverlayAction extends Action2 { public static readonly ID = 'workbench.action.hideInterfaceOverview'; - public static readonly LABEL = localize('hideWelcomeOverlay', "Hide Interface Overview"); + public static readonly LABEL = { value: localize('hideWelcomeOverlay', "Hide Interface Overview"), original: 'Hide Interface Overview' }; - constructor( - id: string, - label: string - ) { - super(id, label); + constructor() { + super({ + id: HideWelcomeOverlayAction.ID, + title: HideWelcomeOverlayAction.LABEL, + category: Categories.Help, + f1: true, + keybinding: { + primary: KeyCode.Escape, + when: OVERLAY_VISIBLE, + weight: KeybindingWeight.WorkbenchContrib + }, + precondition: OVERLAY_VISIBLE + }); } public override run(): Promise { @@ -256,11 +266,8 @@ class WelcomeOverlay extends Disposable { } } -Registry.as(Extensions.WorkbenchActions) - .registerWorkbenchAction(SyncActionDescriptor.from(WelcomeOverlayAction), 'Help: User Interface Overview', CATEGORIES.Help.value); - -Registry.as(Extensions.WorkbenchActions) - .registerWorkbenchAction(SyncActionDescriptor.from(HideWelcomeOverlayAction, { primary: KeyCode.Escape }, OVERLAY_VISIBLE), 'Help: Hide Interface Overview', CATEGORIES.Help.value); +registerAction2(WelcomeOverlayAction); +registerAction2(HideWelcomeOverlayAction); // theming diff --git a/src/vs/workbench/contrib/welcomeViews/common/newFile.contribution.ts b/src/vs/workbench/contrib/welcomeViews/common/newFile.contribution.ts index a6d0e9e5e03..562f3412963 100644 --- a/src/vs/workbench/contrib/welcomeViews/common/newFile.contribution.ts +++ b/src/vs/workbench/contrib/welcomeViews/common/newFile.contribution.ts @@ -7,6 +7,7 @@ import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { assertIsDefined } from 'vs/base/common/types'; import { localize } from 'vs/nls'; +import { ILocalizedString } from 'vs/platform/action/common/action'; import { Action2, IMenuService, MenuId, registerAction2, IMenu, MenuRegistry, MenuItemAction } from 'vs/platform/actions/common/actions'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -19,7 +20,7 @@ import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } fr import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; const builtInSource = localize('Built-In', "Built-In"); -const category = localize('Create', "Create"); +const category: ILocalizedString = { value: localize('Create', "Create"), original: 'Create' }; registerAction2(class extends Action2 { constructor() { @@ -206,7 +207,7 @@ class NewFileTemplatesManager extends Disposable { } Registry.as(WorkbenchExtensions.Workbench) - .registerWorkbenchContribution(NewFileTemplatesManager, 'NewFileTemplatesManager', LifecyclePhase.Restored); + .registerWorkbenchContribution(NewFileTemplatesManager, LifecyclePhase.Restored); MenuRegistry.appendMenuItem(MenuId.NewFile, { group: 'file', diff --git a/src/vs/workbench/contrib/welcomeViews/common/viewsWelcome.contribution.ts b/src/vs/workbench/contrib/welcomeViews/common/viewsWelcome.contribution.ts index 86e4ddca529..e68958ca71b 100644 --- a/src/vs/workbench/contrib/welcomeViews/common/viewsWelcome.contribution.ts +++ b/src/vs/workbench/contrib/welcomeViews/common/viewsWelcome.contribution.ts @@ -22,4 +22,4 @@ class WorkbenchConfigurationContribution { } Registry.as(WorkbenchExtensions.Workbench) - .registerWorkbenchContribution(WorkbenchConfigurationContribution, 'WorkbenchConfigurationContribution', LifecyclePhase.Restored); + .registerWorkbenchContribution(WorkbenchConfigurationContribution, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/welcomeWalkthrough/browser/editor/editorWalkThrough.ts b/src/vs/workbench/contrib/welcomeWalkthrough/browser/editor/editorWalkThrough.ts index 51b2b120941..906d5a4d76c 100644 --- a/src/vs/workbench/contrib/welcomeWalkthrough/browser/editor/editorWalkThrough.ts +++ b/src/vs/workbench/contrib/welcomeWalkthrough/browser/editor/editorWalkThrough.ts @@ -6,12 +6,13 @@ import 'vs/workbench/contrib/welcomeWalkthrough/browser/editor/vs_code_editor_walkthrough'; import { localize } from 'vs/nls'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { Action } from 'vs/base/common/actions'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { WalkThroughInput, WalkThroughInputOptions } from 'vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughInput'; import { FileAccess, Schemas } from 'vs/base/common/network'; import { IEditorSerializer } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; +import { Action2 } from 'vs/platform/actions/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; const typeId = 'workbench.editors.walkThroughInput'; const inputOptions: WalkThroughInputOptions = { @@ -25,24 +26,26 @@ const inputOptions: WalkThroughInputOptions = { telemetryFrom: 'walkThrough' }; -export class EditorWalkThroughAction extends Action { +export class EditorWalkThroughAction extends Action2 { public static readonly ID = 'workbench.action.showInteractivePlayground'; - public static readonly LABEL = localize('editorWalkThrough', "Interactive Editor Playground"); + public static readonly LABEL = { value: localize('editorWalkThrough', "Interactive Editor Playground"), original: 'Interactive Editor Playground' }; - constructor( - id: string, - label: string, - @IEditorService private readonly editorService: IEditorService, - @IInstantiationService private readonly instantiationService: IInstantiationService - ) { - super(id, label); + constructor() { + super({ + id: EditorWalkThroughAction.ID, + title: EditorWalkThroughAction.LABEL, + category: Categories.Help, + f1: true + }); } - public override run(): Promise { - const input = this.instantiationService.createInstance(WalkThroughInput, inputOptions); + public override run(serviceAccessor: ServicesAccessor): Promise { + const editorService = serviceAccessor.get(IEditorService); + const instantiationService = serviceAccessor.get(IInstantiationService); + const input = instantiationService.createInstance(WalkThroughInput, inputOptions); // TODO @lramos15 adopt the resolver here - return this.editorService.openEditor(input, { pinned: true }) + return editorService.openEditor(input, { pinned: true }) .then(() => void (0)); } } diff --git a/src/vs/workbench/contrib/welcomeWalkthrough/browser/walkThrough.contribution.ts b/src/vs/workbench/contrib/welcomeWalkthrough/browser/walkThrough.contribution.ts index 45ba2b555d0..39957579811 100644 --- a/src/vs/workbench/contrib/welcomeWalkthrough/browser/walkThrough.contribution.ts +++ b/src/vs/workbench/contrib/welcomeWalkthrough/browser/walkThrough.contribution.ts @@ -12,8 +12,7 @@ import { EditorWalkThroughAction, EditorWalkThroughInputSerializer } from 'vs/wo import { Registry } from 'vs/platform/registry/common/platform'; import { EditorExtensions, IEditorFactoryRegistry } from 'vs/workbench/common/editor'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { IWorkbenchActionRegistry, Extensions, CATEGORIES } from 'vs/workbench/common/actions'; -import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; +import { MenuRegistry, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { IEditorPaneRegistry, EditorPaneDescriptor } from 'vs/workbench/browser/editor'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; @@ -27,15 +26,12 @@ Registry.as(EditorExtensions.EditorPane) ), [new SyncDescriptor(WalkThroughInput)]); -Registry.as(Extensions.WorkbenchActions) - .registerWorkbenchAction( - SyncActionDescriptor.from(EditorWalkThroughAction), - 'Help: Interactive Editor Playground', CATEGORIES.Help.value); +registerAction2(EditorWalkThroughAction); Registry.as(EditorExtensions.EditorFactory).registerEditorSerializer(EditorWalkThroughInputSerializer.ID, EditorWalkThroughInputSerializer); Registry.as(WorkbenchExtensions.Workbench) - .registerWorkbenchContribution(WalkThroughSnippetContentProvider, 'WalkThroughSnippetContentProvider', LifecyclePhase.Starting); + .registerWorkbenchContribution(WalkThroughSnippetContentProvider, LifecyclePhase.Starting); KeybindingsRegistry.registerCommandAndKeybindingRule(WalkThroughArrowUp); diff --git a/src/vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughPart.ts b/src/vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughPart.ts index 01504f62f36..3e736453267 100644 --- a/src/vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughPart.ts +++ b/src/vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughPart.ts @@ -220,8 +220,7 @@ export class WalkThroughPart extends EditorPane { private updateSizeClasses() { const innerContent = this.content.firstElementChild; if (this.size && innerContent) { - const classList = innerContent.classList; - classList[this.size.height <= 685 ? 'add' : 'remove']('max-height-685px'); + innerContent.classList.toggle('max-height-685px', this.size.height <= 685); } } diff --git a/src/vs/workbench/contrib/workspace/browser/media/trusted-badge.png b/src/vs/workbench/contrib/workspace/browser/media/trusted-badge.png deleted file mode 100644 index c53b0eea12f..00000000000 Binary files a/src/vs/workbench/contrib/workspace/browser/media/trusted-badge.png and /dev/null differ diff --git a/src/vs/workbench/contrib/workspace/browser/media/untrusted-status.png b/src/vs/workbench/contrib/workspace/browser/media/untrusted-status.png deleted file mode 100644 index c8b4f6ae7c8..00000000000 Binary files a/src/vs/workbench/contrib/workspace/browser/media/untrusted-status.png and /dev/null differ diff --git a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts index 67ae288b9a6..cccb98cbefb 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts +++ b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts @@ -72,7 +72,7 @@ export class WorkspaceTrustContextKeys extends Disposable implements IWorkbenchC } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(WorkspaceTrustContextKeys, 'WorkspaceTrustContextKeys', LifecyclePhase.Restored); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(WorkspaceTrustContextKeys, LifecyclePhase.Restored); /* @@ -271,7 +271,7 @@ export class WorkspaceTrustUXHandler extends Disposable implements IWorkbenchCon localize('addWorkspaceFolderMessage', "Do you trust the authors of the files in this folder?"), [localize('yes', 'Yes'), localize('no', 'No')], { - detail: localize('addWorkspaceFolderDetail', "You are adding files to a trusted workspace that are not currently trusted. Do you trust the authors of these new files?"), + detail: localize('addWorkspaceFolderDetail', "You are adding files that are not currently trusted to a trusted workspace. Do you trust the authors of these new files?"), cancelId: 1, custom: { icon: Codicon.shield } } @@ -578,8 +578,8 @@ export class WorkspaceTrustUXHandler extends Disposable implements IWorkbenchCon //#endregion } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(WorkspaceTrustRequestHandler, 'WorkspaceTrustRequestHandler', LifecyclePhase.Ready); -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(WorkspaceTrustUXHandler, 'WorkspaceTrustUXHandler', LifecyclePhase.Restored); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(WorkspaceTrustRequestHandler, LifecyclePhase.Ready); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(WorkspaceTrustUXHandler, LifecyclePhase.Restored); /** @@ -622,6 +622,7 @@ Registry.as(EditorExtensions.EditorPane).registerEditorPane // Configure Workspace Trust const CONFIGURE_TRUST_COMMAND_ID = 'workbench.trust.configure'; +const WORKSPACES_CATEGORY = { value: localize('workspacesCategory', "Workspaces"), original: 'Workspaces' }; registerAction2(class extends Action2 { constructor() { @@ -629,7 +630,7 @@ registerAction2(class extends Action2 { id: CONFIGURE_TRUST_COMMAND_ID, title: { original: 'Configure Workspace Trust', value: localize('configureWorkspaceTrust', "Configure Workspace Trust") }, precondition: ContextKeyExpr.and(WorkspaceTrustContext.IsEnabled, ContextKeyExpr.equals(`config.${WORKSPACE_TRUST_ENABLED}`, true)), - category: localize('workspacesCategory', "Workspaces"), + category: WORKSPACES_CATEGORY, f1: true }); } @@ -647,7 +648,7 @@ registerAction2(class extends Action2 { id: MANAGE_TRUST_COMMAND_ID, title: { original: 'Manage Workspace Trust', value: localize('manageWorkspaceTrust', "Manage Workspace Trust") }, precondition: ContextKeyExpr.and(WorkspaceTrustContext.IsEnabled, ContextKeyExpr.equals(`config.${WORKSPACE_TRUST_ENABLED}`, true)), - category: localize('workspacesCategory', "Workspaces"), + category: WORKSPACES_CATEGORY, f1: true, menu: { id: MenuId.GlobalActivity, @@ -857,4 +858,4 @@ class WorkspaceTrustTelemetryContribution extends Disposable implements IWorkben } Registry.as(WorkbenchExtensions.Workbench) - .registerWorkbenchContribution(WorkspaceTrustTelemetryContribution, 'WorkspaceTrustTelemetryContribution', LifecyclePhase.Restored); + .registerWorkbenchContribution(WorkspaceTrustTelemetryContribution, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/workspaces/browser/workspaces.contribution.ts b/src/vs/workbench/contrib/workspaces/browser/workspaces.contribution.ts index c274839d5bd..26d1f82e6b7 100644 --- a/src/vs/workbench/contrib/workspaces/browser/workspaces.contribution.ts +++ b/src/vs/workbench/contrib/workspaces/browser/workspaces.contribution.ts @@ -94,7 +94,7 @@ export class WorkspacesFinderContribution extends Disposable implements IWorkben } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(WorkspacesFinderContribution, 'WorkspacesFinderContribution', LifecyclePhase.Eventually); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(WorkspacesFinderContribution, LifecyclePhase.Eventually); // Render "Open Workspace" button in *.code-workspace files diff --git a/src/vs/workbench/electron-sandbox/actions/developerActions.ts b/src/vs/workbench/electron-sandbox/actions/developerActions.ts index 618b5d37319..ab1d188313e 100644 --- a/src/vs/workbench/electron-sandbox/actions/developerActions.ts +++ b/src/vs/workbench/electron-sandbox/actions/developerActions.ts @@ -7,7 +7,7 @@ import { localize } from 'vs/nls'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { Action2, MenuId } from 'vs/platform/actions/common/actions'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -20,7 +20,7 @@ export class ToggleDevToolsAction extends Action2 { super({ id: 'workbench.action.toggleDevTools', title: { value: localize('toggleDevTools', "Toggle Developer Tools"), original: 'Toggle Developer Tools' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true, keybinding: { weight: KeybindingWeight.WorkbenchContrib + 50, @@ -49,7 +49,7 @@ export class ConfigureRuntimeArgumentsAction extends Action2 { super({ id: 'workbench.action.configureRuntimeArguments', title: { value: localize('configureRuntimeArguments', "Configure Runtime Arguments"), original: 'Configure Runtime Arguments' }, - category: CATEGORIES.Preferences, + category: Categories.Preferences, f1: true }); } @@ -72,7 +72,7 @@ export class ToggleSharedProcessAction extends Action2 { super({ id: 'workbench.action.toggleSharedProcess', title: { value: localize('toggleSharedProcess', "Toggle Shared Process"), original: 'Toggle Shared Process' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true }); } @@ -88,7 +88,7 @@ export class ReloadWindowWithExtensionsDisabledAction extends Action2 { super({ id: 'workbench.action.reloadWindowWithExtensionsDisabled', title: { value: localize('reloadWindowWithExtensionsDisabled', "Reload With Extensions Disabled"), original: 'Reload With Extensions Disabled' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true }); } diff --git a/src/vs/workbench/electron-sandbox/actions/windowActions.ts b/src/vs/workbench/electron-sandbox/actions/windowActions.ts index 438925fd576..2c8e6d83bfc 100644 --- a/src/vs/workbench/electron-sandbox/actions/windowActions.ts +++ b/src/vs/workbench/electron-sandbox/actions/windowActions.ts @@ -21,7 +21,7 @@ import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { Codicon } from 'vs/base/common/codicons'; import { isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; import { Action2, IAction2Options, MenuId } from 'vs/platform/actions/common/actions'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -95,7 +95,7 @@ export class ZoomInAction extends BaseZoomAction { mnemonicTitle: localize({ key: 'miZoomIn', comment: ['&& denotes a mnemonic'] }, "&&Zoom In"), original: 'Zoom In' }, - category: CATEGORIES.View, + category: Categories.View, f1: true, keybinding: { weight: KeybindingWeight.WorkbenchContrib, @@ -125,7 +125,7 @@ export class ZoomOutAction extends BaseZoomAction { mnemonicTitle: localize({ key: 'miZoomOut', comment: ['&& denotes a mnemonic'] }, "&&Zoom Out"), original: 'Zoom Out' }, - category: CATEGORIES.View, + category: Categories.View, f1: true, keybinding: { weight: KeybindingWeight.WorkbenchContrib, @@ -159,7 +159,7 @@ export class ZoomResetAction extends BaseZoomAction { mnemonicTitle: localize({ key: 'miZoomReset', comment: ['&& denotes a mnemonic'] }, "&&Reset Zoom"), original: 'Reset Zoom' }, - category: CATEGORIES.View, + category: Categories.View, f1: true, keybinding: { weight: KeybindingWeight.WorkbenchContrib, diff --git a/src/vs/workbench/electron-sandbox/desktop.contribution.ts b/src/vs/workbench/electron-sandbox/desktop.contribution.ts index 95e31623896..d2a4a62970b 100644 --- a/src/vs/workbench/electron-sandbox/desktop.contribution.ts +++ b/src/vs/workbench/electron-sandbox/desktop.contribution.ts @@ -170,7 +170,8 @@ import product from 'vs/platform/product/common/product'; 'type': 'number', 'default': 0, 'description': localize('zoomLevel', "Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity."), - ignoreSync: true + ignoreSync: true, + tags: ['accessibility'] }, 'window.newWindowDimensions': { 'type': 'string', diff --git a/src/vs/workbench/electron-sandbox/desktop.main.ts b/src/vs/workbench/electron-sandbox/desktop.main.ts index ac5e9d14c95..a460d71c5f3 100644 --- a/src/vs/workbench/electron-sandbox/desktop.main.ts +++ b/src/vs/workbench/electron-sandbox/desktop.main.ts @@ -49,7 +49,7 @@ import { isCI, isMacintosh } from 'vs/base/common/platform'; import { Schemas } from 'vs/base/common/network'; import { DiskFileSystemProvider } from 'vs/workbench/services/files/electron-sandbox/diskFileSystemProvider'; import { FileUserDataProvider } from 'vs/platform/userData/common/fileUserDataProvider'; -import { IUserDataProfilesService, reviveProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfilesService, PROFILES_ENABLEMENT_CONFIG, reviveProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { UserDataProfilesNativeService } from 'vs/platform/userDataProfile/electron-sandbox/userDataProfile'; import { PolicyChannelClient } from 'vs/platform/policy/common/policyIpc'; import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; @@ -283,6 +283,8 @@ export class DesktopMain extends Disposable { }) ]); + userDataProfilesService.setEnablement(productService.quality !== 'stable' || configurationService.getValue(PROFILES_ENABLEMENT_CONFIG)); + // Workspace Trust Service const workspaceTrustEnablementService = new WorkspaceTrustEnablementService(configurationService, environmentService); serviceCollection.set(IWorkspaceTrustEnablementService, workspaceTrustEnablementService); diff --git a/src/vs/workbench/electron-sandbox/parts/dialogs/dialog.contribution.ts b/src/vs/workbench/electron-sandbox/parts/dialogs/dialog.contribution.ts index 07792869840..60eb43ec230 100644 --- a/src/vs/workbench/electron-sandbox/parts/dialogs/dialog.contribution.ts +++ b/src/vs/workbench/electron-sandbox/parts/dialogs/dialog.contribution.ts @@ -99,4 +99,4 @@ export class DialogHandlerContribution extends Disposable implements IWorkbenchC } const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(DialogHandlerContribution, 'DialogHandlerContribution', LifecyclePhase.Starting); +workbenchRegistry.registerWorkbenchContribution(DialogHandlerContribution, LifecyclePhase.Starting); diff --git a/src/vs/workbench/electron-sandbox/parts/titlebar/menubarControl.ts b/src/vs/workbench/electron-sandbox/parts/titlebar/menubarControl.ts index b9aa17ff080..391bc05355c 100644 --- a/src/vs/workbench/electron-sandbox/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/electron-sandbox/parts/titlebar/menubarControl.ts @@ -25,6 +25,7 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { OpenRecentAction } from 'vs/workbench/browser/actions/windowActions'; +import { isICommandActionToggleInfo } from 'vs/platform/action/common/action'; export class NativeMenubarControl extends MenubarControl { @@ -149,6 +150,10 @@ export class NativeMenubarControl extends MenubarControl { label: title }; + if (isICommandActionToggleInfo(menuItem.item.toggled)) { + menubarMenuItem.label = menuItem.item.toggled.mnemonicTitle ?? menuItem.item.toggled.title ?? title; + } + if (menuItem.checked) { menubarMenuItem.checked = true; } diff --git a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts index 2f991be2339..062d06db50f 100644 --- a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts @@ -11,7 +11,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { isMacintosh, isWindows, isLinux, isNative } from 'vs/base/common/platform'; -import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { MenuId } from 'vs/platform/actions/common/actions'; import { TitlebarPart as BrowserTitleBarPart } from 'vs/workbench/browser/parts/titlebar/titlebarPart'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -62,13 +62,12 @@ export class TitlebarPart extends BrowserTitleBarPart { @IThemeService themeService: IThemeService, @IStorageService storageService: IStorageService, @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, - @IMenuService menuService: IMenuService, @IContextKeyService contextKeyService: IContextKeyService, @IHostService hostService: IHostService, @INativeHostService private readonly nativeHostService: INativeHostService, @IHoverService hoverService: IHoverService, ) { - super(contextMenuService, configurationService, environmentService, instantiationService, themeService, storageService, layoutService, menuService, contextKeyService, hostService, hoverService); + super(contextMenuService, configurationService, environmentService, instantiationService, themeService, storageService, layoutService, contextKeyService, hostService, hoverService); this.environmentService = environmentService; } diff --git a/src/vs/workbench/electron-sandbox/window.ts b/src/vs/workbench/electron-sandbox/window.ts index 9a11377d30d..388e0886346 100644 --- a/src/vs/workbench/electron-sandbox/window.ts +++ b/src/vs/workbench/electron-sandbox/window.ts @@ -669,12 +669,21 @@ export class NativeWindow extends Disposable { // Installation Dir Warning if (this.environmentService.isBuilt) { - const installLocationUri = URI.file(this.environmentService.appRoot); + let installLocationUri: URI; + if (isMacintosh) { + // appRoot = /Applications/Visual Studio Code - Insiders.app/Contents/Resources/app + installLocationUri = dirname(dirname(dirname(URI.file(this.environmentService.appRoot)))); + } else { + // appRoot = C:\Users\\AppData\Local\Programs\Microsoft VS Code Insiders\resources\app + // appRoot = /usr/share/code-insiders/resources/app + installLocationUri = dirname(dirname(URI.file(this.environmentService.appRoot))); + } + for (const folder of this.contextService.getWorkspace().folders) { if (this.uriIdentityService.extUri.isEqualOrParent(folder.uri, installLocationUri)) { this.bannerService.show({ id: 'appRootWarning.banner', - message: localize('appRootWarning.banner', "Files you store within the installation folder ('{0}') may be OVERWRITTEN or DELETED IRREVERSIBLY without warning at update time.", this.environmentService.appRoot), + message: localize('appRootWarning.banner', "Files you store within the installation folder ('{0}') may be OVERWRITTEN or DELETED IRREVERSIBLY without warning at update time.", this.labelService.getUriLabel(installLocationUri)), icon: Codicon.warning }); diff --git a/src/vs/workbench/services/accessibility/electron-sandbox/accessibilityService.ts b/src/vs/workbench/services/accessibility/electron-sandbox/accessibilityService.ts index c02ebba5884..975240757c0 100644 --- a/src/vs/workbench/services/accessibility/electron-sandbox/accessibilityService.ts +++ b/src/vs/workbench/services/accessibility/electron-sandbox/accessibilityService.ts @@ -10,7 +10,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Registry } from 'vs/platform/registry/common/platform'; import { AccessibilityService } from 'vs/platform/accessibility/browser/accessibilityService'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; @@ -67,7 +67,7 @@ export class NativeAccessibilityService extends AccessibilityService implements } } -registerSingleton(IAccessibilityService, NativeAccessibilityService, true); +registerSingleton(IAccessibilityService, NativeAccessibilityService, InstantiationType.Delayed); // On linux we do not automatically detect that a screen reader is detected, thus we have to implicitly notify the renderer to enable accessibility when user configures it in settings class LinuxAccessibilityContribution implements IWorkbenchContribution { @@ -87,5 +87,5 @@ class LinuxAccessibilityContribution implements IWorkbenchContribution { } if (isLinux) { - Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(LinuxAccessibilityContribution, 'LinuxAccessibilityContribution', LifecyclePhase.Ready); + Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(LinuxAccessibilityContribution, LifecyclePhase.Ready); } diff --git a/src/vs/workbench/services/activity/browser/activityService.ts b/src/vs/workbench/services/activity/browser/activityService.ts index 1d12dc5ffd6..77a8841dcb5 100644 --- a/src/vs/workbench/services/activity/browser/activityService.ts +++ b/src/vs/workbench/services/activity/browser/activityService.ts @@ -5,7 +5,7 @@ import { IActivityService, IActivity } from 'vs/workbench/services/activity/common/activity'; import { IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; import { GLOBAL_ACTIVITY_ID, ACCOUNTS_ACTIVITY_ID } from 'vs/workbench/common/activity'; import { Event } from 'vs/base/common/event'; @@ -113,4 +113,4 @@ export class ActivityService implements IActivityService { } } -registerSingleton(IActivityService, ActivityService, true); +registerSingleton(IActivityService, ActivityService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/assignment/common/assignmentService.ts b/src/vs/workbench/services/assignment/common/assignmentService.ts index 942388eb947..d3d939253f3 100644 --- a/src/vs/workbench/services/assignment/common/assignmentService.ts +++ b/src/vs/workbench/services/assignment/common/assignmentService.ts @@ -9,7 +9,7 @@ import { MementoObject, Memento } from 'vs/workbench/common/memento'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryData } from 'vs/base/common/actions'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IProductService } from 'vs/platform/product/common/productService'; import { IAssignmentService } from 'vs/platform/assignment/common/assignment'; @@ -131,4 +131,4 @@ export class WorkbenchAssignmentService extends BaseAssignmentService { } } -registerSingleton(IWorkbenchAssignmentService, WorkbenchAssignmentService, false); +registerSingleton(IWorkbenchAssignmentService, WorkbenchAssignmentService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/authentication/browser/authenticationService.ts b/src/vs/workbench/services/authentication/browser/authenticationService.ts index 69cb0d2ba48..d5e2b750297 100644 --- a/src/vs/workbench/services/authentication/browser/authenticationService.ts +++ b/src/vs/workbench/services/authentication/browser/authenticationService.ts @@ -92,13 +92,17 @@ export type AuthenticationSessionInfo = { readonly id: string; readonly accessTo export async function getCurrentAuthenticationSessionInfo(credentialsService: ICredentialsService, productService: IProductService): Promise { const authenticationSessionValue = await credentialsService.getPassword(`${productService.urlProtocol}.login`, 'account'); if (authenticationSessionValue) { - const authenticationSessionInfo: AuthenticationSessionInfo = JSON.parse(authenticationSessionValue); - if (authenticationSessionInfo - && isString(authenticationSessionInfo.id) - && isString(authenticationSessionInfo.accessToken) - && isString(authenticationSessionInfo.providerId) - ) { - return authenticationSessionInfo; + try { + const authenticationSessionInfo: AuthenticationSessionInfo = JSON.parse(authenticationSessionValue); + if (authenticationSessionInfo + && isString(authenticationSessionInfo.id) + && isString(authenticationSessionInfo.accessToken) + && isString(authenticationSessionInfo.providerId) + ) { + return authenticationSessionInfo; + } + } catch (e) { + // ignore as this is a best effort operation. } } return undefined; diff --git a/src/vs/workbench/services/clipboard/browser/clipboardService.ts b/src/vs/workbench/services/clipboard/browser/clipboardService.ts index a59960511a7..21ecf79467e 100644 --- a/src/vs/workbench/services/clipboard/browser/clipboardService.ts +++ b/src/vs/workbench/services/clipboard/browser/clipboardService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { BrowserClipboardService as BaseBrowserClipboardService } from 'vs/platform/clipboard/browser/clipboardService'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; @@ -68,4 +68,4 @@ export class BrowserClipboardService extends BaseBrowserClipboardService { } } -registerSingleton(IClipboardService, BrowserClipboardService, true); +registerSingleton(IClipboardService, BrowserClipboardService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/clipboard/electron-sandbox/clipboardService.ts b/src/vs/workbench/services/clipboard/electron-sandbox/clipboardService.ts index 326d09c3a37..43443e6cdc5 100644 --- a/src/vs/workbench/services/clipboard/electron-sandbox/clipboardService.ts +++ b/src/vs/workbench/services/clipboard/electron-sandbox/clipboardService.ts @@ -6,7 +6,7 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { URI } from 'vs/base/common/uri'; import { isMacintosh } from 'vs/base/common/platform'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { VSBuffer } from 'vs/base/common/buffer'; @@ -78,4 +78,4 @@ export class NativeClipboardService implements IClipboardService { } } -registerSingleton(IClipboardService, NativeClipboardService, true); +registerSingleton(IClipboardService, NativeClipboardService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/commands/common/commandService.ts b/src/vs/workbench/services/commands/common/commandService.ts index 85faaa2fb3d..8a719346c3f 100644 --- a/src/vs/workbench/services/commands/common/commandService.ts +++ b/src/vs/workbench/services/commands/common/commandService.ts @@ -9,7 +9,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { Event, Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { timeout } from 'vs/base/common/async'; export class CommandService extends Disposable implements ICommandService { @@ -101,4 +101,4 @@ export class CommandService extends Disposable implements ICommandService { } } -registerSingleton(ICommandService, CommandService, true); +registerSingleton(ICommandService, CommandService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index ed219b35242..025115b840b 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -562,7 +562,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat if (!this.localUserConfiguration.hasTasksLoaded) { // Reload local user configuration again to load user tasks - this._register(runWhenIdle(() => this.reloadLocalUserConfiguration(), 5000)); + this._register(runWhenIdle(() => this.reloadLocalUserConfiguration())); } } @@ -1301,6 +1301,6 @@ class UpdateExperimentalSettingsDefaults extends Disposable implements IWorkbenc } const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchContributionsRegistry.registerWorkbenchContribution(RegisterConfigurationSchemasContribution, 'RegisterConfigurationSchemasContribution', LifecyclePhase.Restored); -workbenchContributionsRegistry.registerWorkbenchContribution(ResetConfigurationDefaultsOverridesCache, 'ResetConfigurationDefaultsOverridesCache', LifecyclePhase.Eventually); -workbenchContributionsRegistry.registerWorkbenchContribution(UpdateExperimentalSettingsDefaults, 'UpdateExperimentalSettingsDefaults', LifecyclePhase.Restored); +workbenchContributionsRegistry.registerWorkbenchContribution(RegisterConfigurationSchemasContribution, LifecyclePhase.Restored); +workbenchContributionsRegistry.registerWorkbenchContribution(ResetConfigurationDefaultsOverridesCache, LifecyclePhase.Eventually); +workbenchContributionsRegistry.registerWorkbenchContribution(UpdateExperimentalSettingsDefaults, LifecyclePhase.Restored); diff --git a/src/vs/workbench/services/configuration/common/jsonEditingService.ts b/src/vs/workbench/services/configuration/common/jsonEditingService.ts index 5251efc25b5..e906a376240 100644 --- a/src/vs/workbench/services/configuration/common/jsonEditingService.ts +++ b/src/vs/workbench/services/configuration/common/jsonEditingService.ts @@ -18,7 +18,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; import { IJSONEditingService, IJSONValue, JSONEditingError, JSONEditingErrorCode } from 'vs/workbench/services/configuration/common/jsonEditing'; import { ITextModel } from 'vs/editor/common/model'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class JSONEditingService implements IJSONEditingService { @@ -140,4 +140,4 @@ export class JSONEditingService implements IJSONEditingService { } } -registerSingleton(IJSONEditingService, JSONEditingService, true); +registerSingleton(IJSONEditingService, JSONEditingService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts index b09c88322f5..eab8dede672 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts @@ -116,7 +116,7 @@ suite('WorkspaceContextService - Folder', () => { assert.strictEqual(actual, testObject.getWorkspace().folders[0]); }); - test('getWorkspaceFolder() - queries in workspace folder', async () => { + test('getWorkspaceFolder() - queries in workspace folder', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const logService = new NullLogService(); const fileService = disposables.add(new FileService(logService)); @@ -136,9 +136,9 @@ suite('WorkspaceContextService - Folder', () => { const actual = testObject.getWorkspaceFolder(joinPath(folder, 'a')); assert.strictEqual(actual, testObject.getWorkspace().folders[0]); - }); + })); - test('getWorkspaceFolder() - queries in resource', async () => { + test('getWorkspaceFolder() - queries in resource', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const logService = new NullLogService(); const fileService = disposables.add(new FileService(logService)); @@ -159,7 +159,7 @@ suite('WorkspaceContextService - Folder', () => { const actual = testObject.getWorkspaceFolder(joinPath(folder, 'a').with({ query: 'myquery=1' })); assert.strictEqual(actual, testObject.getWorkspace().folders[0]); - }); + })); test('isCurrentWorkspace() => true', () => { assert.ok(testObject.isCurrentWorkspace(folder)); @@ -277,7 +277,7 @@ suite('WorkspaceContextService - Workspace Editing', () => { teardown(() => disposables.clear()); - test('add folders', async () => { + test('add folders', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await testObject.addFolders([{ uri: joinPath(ROOT, 'd') }, { uri: joinPath(ROOT, 'c') }]); const actual = testObject.getWorkspace().folders; @@ -286,9 +286,9 @@ suite('WorkspaceContextService - Workspace Editing', () => { assert.strictEqual(basename(actual[1].uri), 'b'); assert.strictEqual(basename(actual[2].uri), 'd'); assert.strictEqual(basename(actual[3].uri), 'c'); - }); + })); - test('add folders (at specific index)', async () => { + test('add folders (at specific index)', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await testObject.addFolders([{ uri: joinPath(ROOT, 'd') }, { uri: joinPath(ROOT, 'c') }], 0); const actual = testObject.getWorkspace().folders; @@ -297,9 +297,9 @@ suite('WorkspaceContextService - Workspace Editing', () => { assert.strictEqual(basename(actual[1].uri), 'c'); assert.strictEqual(basename(actual[2].uri), 'a'); assert.strictEqual(basename(actual[3].uri), 'b'); - }); + })); - test('add folders (at specific wrong index)', async () => { + test('add folders (at specific wrong index)', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await testObject.addFolders([{ uri: joinPath(ROOT, 'd') }, { uri: joinPath(ROOT, 'c') }], 10); const actual = testObject.getWorkspace().folders; @@ -308,9 +308,9 @@ suite('WorkspaceContextService - Workspace Editing', () => { assert.strictEqual(basename(actual[1].uri), 'b'); assert.strictEqual(basename(actual[2].uri), 'd'); assert.strictEqual(basename(actual[3].uri), 'c'); - }); + })); - test('add folders (with name)', async () => { + test('add folders (with name)', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await testObject.addFolders([{ uri: joinPath(ROOT, 'd'), name: 'DDD' }, { uri: joinPath(ROOT, 'c'), name: 'CCC' }]); const actual = testObject.getWorkspace().folders; @@ -321,9 +321,9 @@ suite('WorkspaceContextService - Workspace Editing', () => { assert.strictEqual(basename(actual[3].uri), 'c'); assert.strictEqual(actual[2].name, 'DDD'); assert.strictEqual(actual[3].name, 'CCC'); - }); + })); - test('add folders triggers change event', async () => { + test('add folders triggers change event', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const target = sinon.spy(); testObject.onWillChangeWorkspaceFolders(target); testObject.onDidChangeWorkspaceFolders(target); @@ -336,17 +336,17 @@ suite('WorkspaceContextService - Workspace Editing', () => { assert.deepStrictEqual(actual_1.added.map(r => r.uri.toString()), addedFolders.map(a => a.uri.toString())); assert.deepStrictEqual(actual_1.removed, []); assert.deepStrictEqual(actual_1.changed, []); - }); + })); - test('remove folders', async () => { + test('remove folders', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await testObject.removeFolders([testObject.getWorkspace().folders[0].uri]); const actual = testObject.getWorkspace().folders; assert.strictEqual(actual.length, 1); assert.strictEqual(basename(actual[0].uri), 'b'); - }); + })); - test('remove folders triggers change event', async () => { + test('remove folders triggers change event', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const target = sinon.spy(); testObject.onWillChangeWorkspaceFolders(target); testObject.onDidChangeWorkspaceFolders(target); @@ -358,9 +358,9 @@ suite('WorkspaceContextService - Workspace Editing', () => { assert.deepStrictEqual(actual_1.added, []); assert.deepStrictEqual(actual_1.removed.map(r => r.uri.toString()), [removedFolder.uri.toString()]); assert.deepStrictEqual(actual_1.changed.map(c => c.uri.toString()), [testObject.getWorkspace().folders[0].uri.toString()]); - }); + })); - test('remove folders and add them back by writing into the file', async () => { + test('remove folders and add them back by writing into the file', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const folders = testObject.getWorkspace().folders; await testObject.removeFolders([folders[0].uri]); @@ -378,9 +378,9 @@ suite('WorkspaceContextService - Workspace Editing', () => { const workspace = { folders: [{ path: folders[0].uri.path }, { path: folders[1].uri.path }] }; await fileService.writeFile(testObject.getWorkspace().configuration!, VSBuffer.fromString(JSON.stringify(workspace, null, '\t'))); await promise; - }); + })); - test('update folders (remove last and add to end)', async () => { + test('update folders (remove last and add to end)', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const target = sinon.spy(); testObject.onWillChangeWorkspaceFolders(target); testObject.onDidChangeWorkspaceFolders(target); @@ -393,9 +393,9 @@ suite('WorkspaceContextService - Workspace Editing', () => { assert.deepStrictEqual(actual_1.added.map(r => r.uri.toString()), addedFolders.map(a => a.uri.toString())); assert.deepStrictEqual(actual_1.removed.map(r_1 => r_1.uri.toString()), removedFolders.map(a_1 => a_1.toString())); assert.deepStrictEqual(actual_1.changed, []); - }); + })); - test('update folders (rename first via add and remove)', async () => { + test('update folders (rename first via add and remove)', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const target = sinon.spy(); testObject.onWillChangeWorkspaceFolders(target); testObject.onDidChangeWorkspaceFolders(target); @@ -408,9 +408,9 @@ suite('WorkspaceContextService - Workspace Editing', () => { assert.deepStrictEqual(actual_1.added, []); assert.deepStrictEqual(actual_1.removed, []); assert.deepStrictEqual(actual_1.changed.map(r => r.uri.toString()), removedFolders.map(a => a.toString())); - }); + })); - test('update folders (remove first and add to end)', async () => { + test('update folders (remove first and add to end)', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const target = sinon.spy(); testObject.onWillChangeWorkspaceFolders(target); testObject.onDidChangeWorkspaceFolders(target); @@ -424,9 +424,9 @@ suite('WorkspaceContextService - Workspace Editing', () => { assert.deepStrictEqual(actual_1.added.map(r => r.uri.toString()), addedFolders.map(a => a.uri.toString())); assert.deepStrictEqual(actual_1.removed.map(r_1 => r_1.uri.toString()), removedFolders.map(a_1 => a_1.toString())); assert.deepStrictEqual(actual_1.changed.map(r_2 => r_2.uri.toString()), changedFolders.map(a_2 => a_2.toString())); - }); + })); - test('reorder folders trigger change event', async () => { + test('reorder folders trigger change event', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const target = sinon.spy(); testObject.onWillChangeWorkspaceFolders(target); testObject.onDidChangeWorkspaceFolders(target); @@ -439,9 +439,9 @@ suite('WorkspaceContextService - Workspace Editing', () => { assert.deepStrictEqual(actual_1.added, []); assert.deepStrictEqual(actual_1.removed, []); assert.deepStrictEqual(actual_1.changed.map(c => c.uri.toString()), testObject.getWorkspace().folders.map(f => f.uri.toString()).reverse()); - }); + })); - test('rename folders trigger change event', async () => { + test('rename folders trigger change event', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const target = sinon.spy(); testObject.onWillChangeWorkspaceFolders(target); testObject.onDidChangeWorkspaceFolders(target); @@ -454,7 +454,7 @@ suite('WorkspaceContextService - Workspace Editing', () => { assert.deepStrictEqual(actual_1.added, []); assert.deepStrictEqual(actual_1.removed, []); assert.deepStrictEqual(actual_1.changed.map(c => c.uri.toString()), [testObject.getWorkspace().folders[0].uri.toString()]); - }); + })); }); @@ -522,7 +522,7 @@ suite('WorkspaceService - Initialization', () => { teardown(() => disposables.clear()); - (isMacintosh ? test.skip : test)('initialize a folder workspace from an empty workspace with no configuration changes', async () => { + (isMacintosh ? test.skip : test)('initialize a folder workspace from an empty workspace with no configuration changes', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); @@ -545,9 +545,9 @@ suite('WorkspaceService - Initialization', () => { assert.deepStrictEqual((target.args[3][0]).removed, []); assert.deepStrictEqual((target.args[3][0]).changed, []); - }); + })); - (isMacintosh ? test.skip : test)('initialize a folder workspace from an empty workspace with configuration changes', async () => { + (isMacintosh ? test.skip : test)('initialize a folder workspace from an empty workspace with configuration changes', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); @@ -572,9 +572,9 @@ suite('WorkspaceService - Initialization', () => { assert.deepStrictEqual((target.args[4][0]).removed, []); assert.deepStrictEqual((target.args[4][0]).changed, []); - }); + })); - (isMacintosh ? test.skip : test)('initialize a multi root workspace from an empty workspace with no configuration changes', async () => { + (isMacintosh ? test.skip : test)('initialize a multi root workspace from an empty workspace with no configuration changes', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); @@ -595,9 +595,9 @@ suite('WorkspaceService - Initialization', () => { assert.deepStrictEqual((target.args[3][0]).removed, []); assert.deepStrictEqual((target.args[3][0]).changed, []); - }); + })); - (isMacintosh ? test.skip : test)('initialize a multi root workspace from an empty workspace with configuration changes', async () => { + (isMacintosh ? test.skip : test)('initialize a multi root workspace from an empty workspace with configuration changes', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); @@ -621,9 +621,9 @@ suite('WorkspaceService - Initialization', () => { assert.deepStrictEqual((target.args[4][0]).removed, []); assert.deepStrictEqual((target.args[4][0]).changed, []); - }); + })); - (isMacintosh ? test.skip : test)('initialize a folder workspace from a folder workspace with no configuration changes', async () => { + (isMacintosh ? test.skip : test)('initialize a folder workspace from a folder workspace with no configuration changes', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await testObject.initialize(convertToWorkspacePayload(joinPath(ROOT, 'a'))); await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); @@ -643,9 +643,9 @@ suite('WorkspaceService - Initialization', () => { assert.deepStrictEqual((target.args[1][0]).removed.map(folder_2 => folder_2.uri.toString()), [joinPath(ROOT, 'a').toString()]); assert.deepStrictEqual((target.args[1][0]).changed, []); - }); + })); - (isMacintosh ? test.skip : test)('initialize a folder workspace from a folder workspace with configuration changes', async () => { + (isMacintosh ? test.skip : test)('initialize a folder workspace from a folder workspace with configuration changes', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await testObject.initialize(convertToWorkspacePayload(joinPath(ROOT, 'a'))); const target = sinon.spy(); @@ -665,9 +665,9 @@ suite('WorkspaceService - Initialization', () => { assert.deepStrictEqual((target.args[2][0]).removed.map(folder_2 => folder_2.uri.toString()), [joinPath(ROOT, 'a').toString()]); assert.deepStrictEqual((target.args[2][0]).changed, []); - }); + })); - (isMacintosh ? test.skip : test)('initialize a multi folder workspace from a folder workspacce triggers change events in the right order', async () => { + (isMacintosh ? test.skip : test)('initialize a multi folder workspace from a folder workspacce triggers change events in the right order', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await testObject.initialize(convertToWorkspacePayload(joinPath(ROOT, 'a'))); const target = sinon.spy(); testObject.onDidChangeWorkbenchState(target); @@ -686,7 +686,7 @@ suite('WorkspaceService - Initialization', () => { assert.deepStrictEqual((target.args[4][0]).added.map(folder_1 => folder_1.uri.toString()), [joinPath(ROOT, 'b').toString()]); assert.deepStrictEqual((target.args[4][0]).removed, []); assert.deepStrictEqual((target.args[4][0]).changed, []); - }); + })); }); @@ -788,39 +788,39 @@ suite('WorkspaceConfigurationService - Folder', () => { assert.deepStrictEqual(testObject.getValue('configurationService'), { 'folder': { 'applicationSetting': 'isSet', 'machineSetting': 'isSet', 'machineOverridableSetting': 'isSet', 'testSetting': 'isSet', 'languageSetting': 'isSet', 'restrictedSetting': 'isSet', 'policySetting': 'isSet' } }); }); - test('globals override defaults', async () => { + test('globals override defaults', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.testSetting'), 'userValue'); - }); + })); - test('globals', async () => { + test('globals', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('testworkbench.editor.tabs'), true); - }); + })); - test('workspace settings', async () => { + test('workspace settings', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "testworkbench.editor.icons": true }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('testworkbench.editor.icons'), true); - }); + })); - test('workspace settings override user settings', async () => { + test('workspace settings override user settings', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.testSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.testSetting'), 'workspaceValue'); - }); + })); - test('machine overridable settings override user Settings', async () => { + test('machine overridable settings override user Settings', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineOverridableSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.machineOverridableSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.machineOverridableSetting'), 'workspaceValue'); - }); + })); - test('workspace settings override user settings after defaults are registered ', async () => { + test('workspace settings override user settings after defaults are registered ', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.newSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.newSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -835,9 +835,9 @@ suite('WorkspaceConfigurationService - Folder', () => { } }); assert.strictEqual(testObject.getValue('configurationService.folder.newSetting'), 'workspaceValue'); - }); + })); - test('machine overridable settings override user settings after defaults are registered ', async () => { + test('machine overridable settings override user settings after defaults are registered ', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.newMachineOverridableSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.newMachineOverridableSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -853,45 +853,45 @@ suite('WorkspaceConfigurationService - Folder', () => { } }); assert.strictEqual(testObject.getValue('configurationService.folder.newMachineOverridableSetting'), 'workspaceValue'); - }); + })); - test('application settings are not read from workspace', async () => { + test('application settings are not read from workspace', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.applicationSetting'), 'userValue'); - }); + })); - test('application settings are not read from workspace when workspace folder uri is passed', async () => { + test('application settings are not read from workspace when workspace folder uri is passed', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.applicationSetting', { resource: workspaceService.getWorkspace().folders[0].uri }), 'userValue'); - }); + })); - test('machine settings are not read from workspace', async () => { + test('machine settings are not read from workspace', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.machineSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.machineSetting', { resource: workspaceService.getWorkspace().folders[0].uri }), 'userValue'); - }); + })); - test('machine settings are not read from workspace when workspace folder uri is passed', async () => { + test('machine settings are not read from workspace when workspace folder uri is passed', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.machineSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.machineSetting', { resource: workspaceService.getWorkspace().folders[0].uri }), 'userValue'); - }); + })); - test('get application scope settings are not loaded after defaults are registered', async () => { + test('get application scope settings are not loaded after defaults are registered', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting-2": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.applicationSetting-2": "workspaceValue" }')); @@ -914,9 +914,9 @@ suite('WorkspaceConfigurationService - Folder', () => { await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.applicationSetting-2'), 'userValue'); - }); + })); - test('get application scope settings are not loaded after defaults are registered when workspace folder uri is passed', async () => { + test('get application scope settings are not loaded after defaults are registered when workspace folder uri is passed', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting-3": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.applicationSetting-3": "workspaceValue" }')); @@ -939,9 +939,9 @@ suite('WorkspaceConfigurationService - Folder', () => { await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.applicationSetting-3', { resource: workspaceService.getWorkspace().folders[0].uri }), 'userValue'); - }); + })); - test('get machine scope settings are not loaded after defaults are registered', async () => { + test('get machine scope settings are not loaded after defaults are registered', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting-2": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.machineSetting-2": "workspaceValue" }')); @@ -964,9 +964,9 @@ suite('WorkspaceConfigurationService - Folder', () => { await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.machineSetting-2'), 'userValue'); - }); + })); - test('get machine scope settings are not loaded after defaults are registered when workspace folder uri is passed', async () => { + test('get machine scope settings are not loaded after defaults are registered when workspace folder uri is passed', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting-3": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.machineSetting-3": "workspaceValue" }')); @@ -989,9 +989,9 @@ suite('WorkspaceConfigurationService - Folder', () => { await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.machineSetting-3', { resource: workspaceService.getWorkspace().folders[0].uri }), 'userValue'); - }); + })); - test('policy value override all', async () => { + test('policy value override all', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const result = await runWithFakedTimers({ useFakeTimers: true }, async () => { const promise = Event.toPromise(testObject.onDidChangeConfiguration); await fileService.writeFile(environmentService.policyFile!, VSBuffer.fromString('{ "configurationService.folder.policySetting": "policyValue" }')); @@ -1000,33 +1000,33 @@ suite('WorkspaceConfigurationService - Folder', () => { assert.deepStrictEqual(result.affectedKeys, ['configurationService.folder.policySetting']); assert.strictEqual(testObject.getValue('configurationService.folder.policySetting'), 'policyValue'); assert.strictEqual(testObject.inspect('configurationService.folder.policySetting').policyValue, 'policyValue'); - }); + })); - test('policy settings when policy value is not set', async () => { + test('policy settings when policy value is not set', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.policySetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.policySetting": "workspaceValue" }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.policySetting'), 'workspaceValue'); assert.strictEqual(testObject.inspect('configurationService.folder.policySetting').policyValue, undefined); - }); + })); - test('reload configuration emits events after global configuraiton changes', async () => { + test('reload configuration emits events after global configuraiton changes', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); const target = sinon.spy(); testObject.onDidChangeConfiguration(target); await testObject.reloadConfiguration(); assert.ok(target.called); - }); + })); - test('reload configuration emits events after workspace configuraiton changes', async () => { + test('reload configuration emits events after workspace configuraiton changes', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.testSetting": "workspaceValue" }')); const target = sinon.spy(); testObject.onDidChangeConfiguration(target); await testObject.reloadConfiguration(); assert.ok(target.called); - }); + })); - test('reload configuration should not emit event if no changes', async () => { + test('reload configuration should not emit event if no changes', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.testSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1034,9 +1034,9 @@ suite('WorkspaceConfigurationService - Folder', () => { testObject.onDidChangeConfiguration(() => { target(); }); await testObject.reloadConfiguration(); assert.ok(!target.called); - }); + })); - test('inspect', async () => { + test('inspect', () => runWithFakedTimers({ useFakeTimers: true }, async () => { let actual = testObject.inspect('something.missing'); assert.strictEqual(actual.defaultValue, undefined); assert.strictEqual(actual.application, undefined); @@ -1094,9 +1094,9 @@ suite('WorkspaceConfigurationService - Folder', () => { } } }); - }); + })); - test('inspect restricted settings', async () => { + test('inspect restricted settings', () => runWithFakedTimers({ useFakeTimers: true }, async () => { testObject.updateWorkspaceTrust(false); await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userRestrictedValue" }')); await testObject.reloadConfiguration(); @@ -1182,9 +1182,28 @@ suite('WorkspaceConfigurationService - Folder', () => { } } }); - }); + })); - test('keys', async () => { + test('inspect restricted settings after change', () => runWithFakedTimers({ useFakeTimers: true }, async () => { + testObject.updateWorkspaceTrust(false); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userRestrictedValue" }')); + await testObject.reloadConfiguration(); + + const promise = Event.toPromise(testObject.onDidChangeConfiguration); + await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "workspaceRestrictedValue" }')); + const event = await promise; + + const actual = testObject.inspect('configurationService.folder.restrictedSetting'); + assert.strictEqual(actual.defaultValue, 'isSet'); + assert.strictEqual(actual.application, undefined); + assert.strictEqual(actual.userValue, 'userRestrictedValue'); + assert.strictEqual(actual.workspaceValue, 'workspaceRestrictedValue'); + assert.strictEqual(actual.workspaceFolderValue, undefined); + assert.strictEqual(actual.value, 'userRestrictedValue'); + assert.strictEqual(event.affectsConfiguration('configurationService.folder.restrictedSetting'), true); + })); + + test('keys', () => runWithFakedTimers({ useFakeTimers: true }, async () => { let actual = testObject.keys(); assert.ok(actual.default.indexOf('configurationService.folder.testSetting') !== -1); assert.deepStrictEqual(actual.user, []); @@ -1206,7 +1225,7 @@ suite('WorkspaceConfigurationService - Folder', () => { assert.deepStrictEqual(actual.user, ['configurationService.folder.testSetting']); assert.deepStrictEqual(actual.workspace, ['configurationService.folder.testSetting']); assert.deepStrictEqual(actual.workspaceFolder, []); - }); + })); test('update user configuration', () => { return testObject.updateValue('configurationService.folder.testSetting', 'value', ConfigurationTarget.USER) @@ -1223,39 +1242,39 @@ suite('WorkspaceConfigurationService - Folder', () => { .then(() => assert.strictEqual(testObject.getValue('configurationService.folder.testSetting'), 'value')); }); - test('update language configuration using configuration overrides', async () => { + test('update language configuration using configuration overrides', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await testObject.updateValue('configurationService.folder.languageSetting', 'abcLangValue', { overrideIdentifier: 'abclang' }); assert.strictEqual(testObject.getValue('configurationService.folder.languageSetting', { overrideIdentifier: 'abclang' }), 'abcLangValue'); - }); + })); - test('update language configuration using configuration update overrides', async () => { + test('update language configuration using configuration update overrides', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await testObject.updateValue('configurationService.folder.languageSetting', 'abcLangValue', { overrideIdentifiers: ['abclang'] }); assert.strictEqual(testObject.getValue('configurationService.folder.languageSetting', { overrideIdentifier: 'abclang' }), 'abcLangValue'); - }); + })); - test('update language configuration for multiple languages', async () => { + test('update language configuration for multiple languages', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await testObject.updateValue('configurationService.folder.languageSetting', 'multiLangValue', { overrideIdentifiers: ['deflang', 'xyzlang'] }, ConfigurationTarget.USER); assert.strictEqual(testObject.getValue('configurationService.folder.languageSetting', { overrideIdentifier: 'deflang' }), 'multiLangValue'); assert.strictEqual(testObject.getValue('configurationService.folder.languageSetting', { overrideIdentifier: 'xyzlang' }), 'multiLangValue'); assert.deepStrictEqual(testObject.getValue(keyFromOverrideIdentifiers(['deflang', 'xyzlang'])), { 'configurationService.folder.languageSetting': 'multiLangValue' }); - }); + })); - test('update resource language configuration', async () => { + test('update resource language configuration', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await testObject.updateValue('configurationService.folder.languageSetting', 'value', { resource: workspaceService.getWorkspace().folders[0].uri }, ConfigurationTarget.WORKSPACE_FOLDER); assert.strictEqual(testObject.getValue('configurationService.folder.languageSetting'), 'value'); - }); + })); - test('update resource language configuration for a language using configuration overrides', async () => { + test('update resource language configuration for a language using configuration overrides', () => runWithFakedTimers({ useFakeTimers: true }, async () => { assert.strictEqual(testObject.getValue('configurationService.folder.languageSetting', { resource: workspaceService.getWorkspace().folders[0].uri, overrideIdentifier: 'jsonc' }), 'languageValue'); await testObject.updateValue('configurationService.folder.languageSetting', 'languageValueUpdated', { resource: workspaceService.getWorkspace().folders[0].uri, overrideIdentifier: 'jsonc' }, ConfigurationTarget.WORKSPACE_FOLDER); assert.strictEqual(testObject.getValue('configurationService.folder.languageSetting', { resource: workspaceService.getWorkspace().folders[0].uri, overrideIdentifier: 'jsonc' }), 'languageValueUpdated'); - }); + })); - test('update resource language configuration for a language using configuration update overrides', async () => { + test('update resource language configuration for a language using configuration update overrides', () => runWithFakedTimers({ useFakeTimers: true }, async () => { assert.strictEqual(testObject.getValue('configurationService.folder.languageSetting', { resource: workspaceService.getWorkspace().folders[0].uri, overrideIdentifier: 'jsonc' }), 'languageValue'); await testObject.updateValue('configurationService.folder.languageSetting', 'languageValueUpdated', { resource: workspaceService.getWorkspace().folders[0].uri, overrideIdentifiers: ['jsonc'] }, ConfigurationTarget.WORKSPACE_FOLDER); assert.strictEqual(testObject.getValue('configurationService.folder.languageSetting', { resource: workspaceService.getWorkspace().folders[0].uri, overrideIdentifier: 'jsonc' }), 'languageValueUpdated'); - }); + })); test('update application setting into workspace configuration in a workspace is not supported', () => { return testObject.updateValue('configurationService.folder.applicationSetting', 'workspaceValue', {}, ConfigurationTarget.WORKSPACE, true) @@ -1298,7 +1317,7 @@ suite('WorkspaceConfigurationService - Folder', () => { .then(() => assert.ok(target.called)); }); - test('remove setting from all targets', async () => { + test('remove setting from all targets', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const key = 'configurationService.folder.testSetting'; await testObject.updateValue(key, 'workspaceValue', ConfigurationTarget.WORKSPACE); await testObject.updateValue(key, 'userValue', ConfigurationTarget.USER); @@ -1310,19 +1329,19 @@ suite('WorkspaceConfigurationService - Folder', () => { assert.strictEqual(actual.userValue, undefined); assert.strictEqual(actual.workspaceValue, undefined); assert.strictEqual(actual.workspaceFolderValue, undefined); - }); + })); - test('update user configuration to default value when target is not passed', async () => { + test('update user configuration to default value when target is not passed', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await testObject.updateValue('configurationService.folder.testSetting', 'value', ConfigurationTarget.USER); await testObject.updateValue('configurationService.folder.testSetting', 'isSet'); assert.strictEqual(testObject.inspect('configurationService.folder.testSetting').userValue, undefined); - }); + })); - test('update user configuration to default value when target is passed', async () => { + test('update user configuration to default value when target is passed', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await testObject.updateValue('configurationService.folder.testSetting', 'value', ConfigurationTarget.USER); await testObject.updateValue('configurationService.folder.testSetting', 'isSet', ConfigurationTarget.USER); assert.strictEqual(testObject.inspect('configurationService.folder.testSetting').userValue, 'isSet'); - }); + })); test('update task configuration should trigger change event before promise is resolve', () => { const target = sinon.spy(); @@ -1331,19 +1350,19 @@ suite('WorkspaceConfigurationService - Folder', () => { .then(() => assert.ok(target.called)); }); - test('no change event when there are no global tasks', async () => { + test('no change event when there are no global tasks', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const target = sinon.spy(); testObject.onDidChangeConfiguration(target); await timeout(5); assert.ok(target.notCalled); - }); + })); - test('change event when there are global tasks', async () => { + test('change event when there are global tasks', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(joinPath(environmentService.userRoamingDataHome, 'tasks.json'), VSBuffer.fromString('{ "version": "1.0.0", "tasks": [{ "taskName": "myTask" }')); const promise = Event.toPromise(testObject.onDidChangeConfiguration); await testObject.reloadLocalUserConfiguration(); await promise; - }); + })); test('creating workspace settings', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); @@ -1372,7 +1391,7 @@ suite('WorkspaceConfigurationService - Folder', () => { assert.strictEqual(testObject.getValue('configurationService.folder.testSetting'), 'userValue'); })); - test('restricted setting is read from workspace when workspace is trusted', async () => { + test('restricted setting is read from workspace when workspace is trusted', () => runWithFakedTimers({ useFakeTimers: true }, async () => { testObject.updateWorkspaceTrust(true); await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); @@ -1386,9 +1405,9 @@ suite('WorkspaceConfigurationService - Folder', () => { assert.deepStrictEqual(testObject.restrictedSettings.workspace, ['configurationService.folder.restrictedSetting']); assert.strictEqual(testObject.restrictedSettings.workspaceFolder?.size, 1); assert.deepStrictEqual(testObject.restrictedSettings.workspaceFolder?.get(workspaceService.getWorkspace().folders[0].uri), ['configurationService.folder.restrictedSetting']); - }); + })); - test('restricted setting is not read from workspace when workspace is changed to trusted', async () => { + test('restricted setting is not read from workspace when workspace is changed to trusted', () => runWithFakedTimers({ useFakeTimers: true }, async () => { testObject.updateWorkspaceTrust(true); await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); @@ -1404,9 +1423,9 @@ suite('WorkspaceConfigurationService - Folder', () => { assert.deepStrictEqual(testObject.restrictedSettings.workspace, ['configurationService.folder.restrictedSetting']); assert.strictEqual(testObject.restrictedSettings.workspaceFolder?.size, 1); assert.deepStrictEqual(testObject.restrictedSettings.workspaceFolder?.get(workspaceService.getWorkspace().folders[0].uri), ['configurationService.folder.restrictedSetting']); - }); + })); - test('change event is triggered when workspace is changed to untrusted', async () => { + test('change event is triggered when workspace is changed to untrusted', () => runWithFakedTimers({ useFakeTimers: true }, async () => { testObject.updateWorkspaceTrust(true); await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); @@ -1419,9 +1438,9 @@ suite('WorkspaceConfigurationService - Folder', () => { const event = await promise; assert.ok(event.affectedKeys.includes('configurationService.folder.restrictedSetting')); assert.ok(event.affectsConfiguration('configurationService.folder.restrictedSetting')); - }); + })); - test('restricted setting is not read from workspace when workspace is not trusted', async () => { + test('restricted setting is not read from workspace when workspace is not trusted', () => runWithFakedTimers({ useFakeTimers: true }, async () => { testObject.updateWorkspaceTrust(false); await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); @@ -1435,9 +1454,9 @@ suite('WorkspaceConfigurationService - Folder', () => { assert.deepStrictEqual(testObject.restrictedSettings.workspace, ['configurationService.folder.restrictedSetting']); assert.strictEqual(testObject.restrictedSettings.workspaceFolder?.size, 1); assert.deepStrictEqual(testObject.restrictedSettings.workspaceFolder?.get(workspaceService.getWorkspace().folders[0].uri), ['configurationService.folder.restrictedSetting']); - }); + })); - test('restricted setting is read when workspace is changed to trusted', async () => { + test('restricted setting is read when workspace is changed to trusted', () => runWithFakedTimers({ useFakeTimers: true }, async () => { testObject.updateWorkspaceTrust(false); await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); @@ -1453,9 +1472,9 @@ suite('WorkspaceConfigurationService - Folder', () => { assert.deepStrictEqual(testObject.restrictedSettings.workspace, ['configurationService.folder.restrictedSetting']); assert.strictEqual(testObject.restrictedSettings.workspaceFolder?.size, 1); assert.deepStrictEqual(testObject.restrictedSettings.workspaceFolder?.get(workspaceService.getWorkspace().folders[0].uri), ['configurationService.folder.restrictedSetting']); - }); + })); - test('change event is triggered when workspace is changed to trusted', async () => { + test('change event is triggered when workspace is changed to trusted', () => runWithFakedTimers({ useFakeTimers: true }, async () => { testObject.updateWorkspaceTrust(false); await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); @@ -1468,7 +1487,7 @@ suite('WorkspaceConfigurationService - Folder', () => { const event = await promise; assert.ok(event.affectedKeys.includes('configurationService.folder.restrictedSetting')); assert.ok(event.affectsConfiguration('configurationService.folder.restrictedSetting')); - }); + })); test('adding an restricted setting triggers change event', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); @@ -1480,7 +1499,7 @@ suite('WorkspaceConfigurationService - Folder', () => { return promise; })); - test('remove an unregistered setting', async () => { + test('remove an unregistered setting', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const key = 'configurationService.folder.unknownSetting'; await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.unknownSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.unknownSetting": "workspaceValue" }')); @@ -1492,7 +1511,7 @@ suite('WorkspaceConfigurationService - Folder', () => { assert.strictEqual(actual.userValue, undefined); assert.strictEqual(actual.workspaceValue, undefined); assert.strictEqual(actual.workspaceFolderValue, undefined); - }); + })); }); suite('WorkspaceConfigurationService - Profiles', () => { @@ -1563,12 +1582,12 @@ suite('WorkspaceConfigurationService - Profiles', () => { teardown(() => disposables.clear()); - test('initialize', async () => { + test('initialize', () => runWithFakedTimers({ useFakeTimers: true }, async () => { assert.strictEqual(testObject.getValue('configurationService.profiles.applicationSetting2'), 'applicationValue'); assert.strictEqual(testObject.getValue('configurationService.profiles.testSetting2'), 'profileValue'); - }); + })); - test('inspect', async () => { + test('inspect', () => runWithFakedTimers({ useFakeTimers: true }, async () => { let actual = testObject.inspect('something.missing'); assert.strictEqual(actual.defaultValue, undefined); assert.strictEqual(actual.application, undefined); @@ -1606,23 +1625,23 @@ suite('WorkspaceConfigurationService - Profiles', () => { assert.strictEqual(actual.workspaceValue, undefined); assert.strictEqual(actual.workspaceFolderValue, undefined); assert.strictEqual(actual.value, 'profileValue'); - }); + })); - test('update application scope setting', async () => { + test('update application scope setting', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await testObject.updateValue('configurationService.profiles.applicationSetting', 'applicationValue'); assert.deepStrictEqual(JSON.parse((await fileService.readFile(instantiationService.get(IUserDataProfilesService).defaultProfile.settingsResource)).value.toString()), { 'configurationService.profiles.applicationSetting': 'applicationValue', 'configurationService.profiles.applicationSetting2': 'applicationValue', 'configurationService.profiles.testSetting2': 'userValue' }); assert.strictEqual(testObject.getValue('configurationService.profiles.applicationSetting'), 'applicationValue'); - }); + })); - test('update normal setting', async () => { + test('update normal setting', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await testObject.updateValue('configurationService.profiles.testSetting', 'profileValue'); assert.deepStrictEqual(JSON.parse((await fileService.readFile(userDataProfileService.currentProfile.settingsResource)).value.toString()), { 'configurationService.profiles.testSetting': 'profileValue', 'configurationService.profiles.testSetting2': 'profileValue', 'configurationService.profiles.applicationSetting2': 'profileValue' }); assert.strictEqual(testObject.getValue('configurationService.profiles.testSetting'), 'profileValue'); - }); + })); - test('switch to default profile', async () => { + test('switch to default profile', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(instantiationService.get(IUserDataProfilesService).defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.profiles.applicationSetting": "applicationValue", "configurationService.profiles.testSetting": "userValue" }')); await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.profiles.applicationSetting": "profileValue", "configurationService.profiles.testSetting": "profileValue" }')); await testObject.reloadConfiguration(); @@ -1634,9 +1653,9 @@ suite('WorkspaceConfigurationService - Profiles', () => { assert.deepStrictEqual(changeEvent.affectedKeys, ['configurationService.profiles.testSetting']); assert.strictEqual(testObject.getValue('configurationService.profiles.applicationSetting'), 'applicationValue'); assert.strictEqual(testObject.getValue('configurationService.profiles.testSetting'), 'userValue'); - }); + })); - test('switch to non default profile', async () => { + test('switch to non default profile', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(instantiationService.get(IUserDataProfilesService).defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.profiles.applicationSetting": "applicationValue", "configurationService.profiles.testSetting": "userValue" }')); await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.profiles.applicationSetting": "profileValue", "configurationService.profiles.testSetting": "profileValue" }')); await testObject.reloadConfiguration(); @@ -1650,7 +1669,7 @@ suite('WorkspaceConfigurationService - Profiles', () => { assert.deepStrictEqual(changeEvent.affectedKeys, ['configurationService.profiles.testSetting']); assert.strictEqual(testObject.getValue('configurationService.profiles.applicationSetting'), 'applicationValue'); assert.strictEqual(testObject.getValue('configurationService.profiles.testSetting'), 'profileValue2'); - }); + })); }); @@ -1757,43 +1776,43 @@ suite('WorkspaceConfigurationService-Multiroot', () => { teardown(() => disposables.clear()); - test('application settings are not read from workspace', async () => { + test('application settings are not read from workspace', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.applicationSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.applicationSetting'), 'userValue'); - }); + })); - test('application settings are not read from workspace when folder is passed', async () => { + test('application settings are not read from workspace when folder is passed', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.applicationSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.applicationSetting', { resource: workspaceContextService.getWorkspace().folders[0].uri }), 'userValue'); - }); + })); - test('machine settings are not read from workspace', async () => { + test('machine settings are not read from workspace', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.machineSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.machineSetting'), 'userValue'); - }); + })); - test('machine settings are not read from workspace when folder is passed', async () => { + test('machine settings are not read from workspace when folder is passed', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.machineSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.machineSetting', { resource: workspaceContextService.getWorkspace().folders[0].uri }), 'userValue'); - }); + })); - test('get application scope settings are not loaded after defaults are registered', async () => { + test('get application scope settings are not loaded after defaults are registered', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.newSetting': 'workspaceValue' } }], true); @@ -1816,9 +1835,9 @@ suite('WorkspaceConfigurationService-Multiroot', () => { await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.workspace.newSetting'), 'userValue'); - }); + })); - test('get application scope settings are not loaded after defaults are registered when workspace folder is passed', async () => { + test('get application scope settings are not loaded after defaults are registered when workspace folder is passed', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newSetting-2": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.newSetting-2': 'workspaceValue' } }], true); @@ -1841,9 +1860,9 @@ suite('WorkspaceConfigurationService-Multiroot', () => { await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.workspace.newSetting-2', { resource: workspaceContextService.getWorkspace().folders[0].uri }), 'userValue'); - }); + })); - test('workspace settings override user settings after defaults are registered for machine overridable settings ', async () => { + test('workspace settings override user settings after defaults are registered for machine overridable settings ', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newMachineOverridableSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.newMachineOverridableSetting': 'workspaceValue' } }], true); @@ -1867,45 +1886,45 @@ suite('WorkspaceConfigurationService-Multiroot', () => { await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.workspace.newMachineOverridableSetting'), 'workspaceValue'); - }); + })); - test('application settings are not read from workspace folder', async () => { + test('application settings are not read from workspace folder', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.workspace.applicationSetting'), 'userValue'); - }); + })); - test('application settings are not read from workspace folder when workspace folder is passed', async () => { + test('application settings are not read from workspace folder when workspace folder is passed', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.workspace.applicationSetting', { resource: workspaceContextService.getWorkspace().folders[0].uri }), 'userValue'); - }); + })); - test('machine settings are not read from workspace folder', async () => { + test('machine settings are not read from workspace folder', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.workspace.machineSetting'), 'userValue'); - }); + })); - test('machine settings are not read from workspace folder when workspace folder is passed', async () => { + test('machine settings are not read from workspace folder when workspace folder is passed', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.workspace.machineSetting', { resource: workspaceContextService.getWorkspace().folders[0].uri }), 'userValue'); - }); + })); - test('application settings are not read from workspace folder after defaults are registered', async () => { + test('application settings are not read from workspace folder after defaults are registered', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testNewApplicationSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.testNewApplicationSetting": "workspaceFolderValue" }')); @@ -1928,9 +1947,9 @@ suite('WorkspaceConfigurationService-Multiroot', () => { await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.workspace.testNewApplicationSetting', { resource: workspaceContextService.getWorkspace().folders[0].uri }), 'userValue'); - }); + })); - test('application settings are not read from workspace folder after defaults are registered', async () => { + test('application settings are not read from workspace folder after defaults are registered', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testNewMachineSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.testNewMachineSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); @@ -1953,9 +1972,9 @@ suite('WorkspaceConfigurationService-Multiroot', () => { await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.workspace.testNewMachineSetting', { resource: workspaceContextService.getWorkspace().folders[0].uri }), 'userValue'); - }); + })); - test('resource setting in folder is read after it is registered later', async () => { + test('resource setting in folder is read after it is registered later', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.testNewResourceSetting2": "workspaceFolderValue" }')); await jsonEditingServce.write((workspaceContextService.getWorkspace().configuration!), [{ path: ['settings'], value: { 'configurationService.workspace.testNewResourceSetting2': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1971,9 +1990,9 @@ suite('WorkspaceConfigurationService-Multiroot', () => { } }); assert.strictEqual(testObject.getValue('configurationService.workspace.testNewResourceSetting2', { resource: workspaceContextService.getWorkspace().folders[0].uri }), 'workspaceFolderValue'); - }); + })); - test('resource language setting in folder is read after it is registered later', async () => { + test('resource language setting in folder is read after it is registered later', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.testNewResourceLanguageSetting2": "workspaceFolderValue" }')); await jsonEditingServce.write((workspaceContextService.getWorkspace().configuration!), [{ path: ['settings'], value: { 'configurationService.workspace.testNewResourceLanguageSetting2': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1989,9 +2008,9 @@ suite('WorkspaceConfigurationService-Multiroot', () => { } }); assert.strictEqual(testObject.getValue('configurationService.workspace.testNewResourceLanguageSetting2', { resource: workspaceContextService.getWorkspace().folders[0].uri }), 'workspaceFolderValue'); - }); + })); - test('machine overridable setting in folder is read after it is registered later', async () => { + test('machine overridable setting in folder is read after it is registered later', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.testNewMachineOverridableSetting2": "workspaceFolderValue" }')); await jsonEditingServce.write((workspaceContextService.getWorkspace().configuration!), [{ path: ['settings'], value: { 'configurationService.workspace.testNewMachineOverridableSetting2': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -2007,9 +2026,9 @@ suite('WorkspaceConfigurationService-Multiroot', () => { } }); assert.strictEqual(testObject.getValue('configurationService.workspace.testNewMachineOverridableSetting2', { resource: workspaceContextService.getWorkspace().folders[0].uri }), 'workspaceFolderValue'); - }); + })); - test('inspect', async () => { + test('inspect', () => runWithFakedTimers({ useFakeTimers: true }, async () => { let actual = testObject.inspect('something.missing'); assert.strictEqual(actual.defaultValue, undefined); assert.strictEqual(actual.userValue, undefined); @@ -2050,9 +2069,9 @@ suite('WorkspaceConfigurationService-Multiroot', () => { assert.strictEqual(actual.workspaceValue, 'workspaceValue'); assert.strictEqual(actual.workspaceFolderValue, 'workspaceFolderValue'); assert.strictEqual(actual.value, 'workspaceFolderValue'); - }); + })); - test('inspect restricted settings', async () => { + test('inspect restricted settings', () => runWithFakedTimers({ useFakeTimers: true }, async () => { testObject.updateWorkspaceTrust(false); await jsonEditingServce.write((workspaceContextService.getWorkspace().configuration!), [{ path: ['settings'], value: { 'configurationService.workspace.testRestrictedSetting1': 'workspaceRestrictedValue' } }], true); await testObject.reloadConfiguration(); @@ -2094,9 +2113,41 @@ suite('WorkspaceConfigurationService-Multiroot', () => { assert.strictEqual(actual.workspaceValue, 'workspaceRestrictedValue'); assert.strictEqual(actual.workspaceFolderValue, 'workspaceFolderRestrictedValue'); assert.strictEqual(actual.value, 'workspaceFolderRestrictedValue'); - }); + })); - test('get launch configuration', async () => { + test('inspect restricted settings after change', () => runWithFakedTimers({ useFakeTimers: true }, async () => { + testObject.updateWorkspaceTrust(false); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting1": "userRestrictedValue" }')); + await testObject.reloadConfiguration(); + + let promise = Event.toPromise(testObject.onDidChangeConfiguration); + await jsonEditingServce.write((workspaceContextService.getWorkspace().configuration!), [{ path: ['settings'], value: { 'configurationService.workspace.testRestrictedSetting1': 'workspaceRestrictedValue' } }], true); + let event = await promise; + + let actual = testObject.inspect('configurationService.workspace.testRestrictedSetting1', { resource: workspaceContextService.getWorkspace().folders[0].uri }); + assert.strictEqual(actual.defaultValue, 'isSet'); + assert.strictEqual(actual.application, undefined); + assert.strictEqual(actual.userValue, 'userRestrictedValue'); + assert.strictEqual(actual.workspaceValue, 'workspaceRestrictedValue'); + assert.strictEqual(actual.workspaceFolderValue, undefined); + assert.strictEqual(actual.value, 'userRestrictedValue'); + assert.strictEqual(event.affectsConfiguration('configurationService.workspace.testRestrictedSetting1'), true); + + promise = Event.toPromise(testObject.onDidChangeConfiguration); + await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting1": "workspaceFolderRestrictedValue" }')); + event = await promise; + + actual = testObject.inspect('configurationService.workspace.testRestrictedSetting1', { resource: workspaceContextService.getWorkspace().folders[0].uri }); + assert.strictEqual(actual.defaultValue, 'isSet'); + assert.strictEqual(actual.application, undefined); + assert.strictEqual(actual.userValue, 'userRestrictedValue'); + assert.strictEqual(actual.workspaceValue, 'workspaceRestrictedValue'); + assert.strictEqual(actual.workspaceFolderValue, 'workspaceFolderRestrictedValue'); + assert.strictEqual(actual.value, 'userRestrictedValue'); + assert.strictEqual(event.affectsConfiguration('configurationService.workspace.testRestrictedSetting1'), true); + })); + + test('get launch configuration', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const expectedLaunchConfiguration = { 'version': '0.1.0', 'configurations': [ @@ -2117,9 +2168,9 @@ suite('WorkspaceConfigurationService-Multiroot', () => { await testObject.reloadConfiguration(); const actual = testObject.getValue('launch'); assert.deepStrictEqual(actual, expectedLaunchConfiguration); - }); + })); - test('inspect launch configuration', async () => { + test('inspect launch configuration', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const expectedLaunchConfiguration = { 'version': '0.1.0', 'configurations': [ @@ -2140,10 +2191,10 @@ suite('WorkspaceConfigurationService-Multiroot', () => { await testObject.reloadConfiguration(); const actual = testObject.inspect('launch').workspaceValue; assert.deepStrictEqual(actual, expectedLaunchConfiguration); - }); + })); - test('get tasks configuration', async () => { + test('get tasks configuration', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const expectedTasksConfiguration = { 'version': '2.0.0', 'tasks': [ @@ -2162,9 +2213,9 @@ suite('WorkspaceConfigurationService-Multiroot', () => { await testObject.reloadConfiguration(); const actual = testObject.getValue(TasksSchemaProperties.Tasks); assert.deepStrictEqual(actual, expectedTasksConfiguration); - }); + })); - test('inspect tasks configuration', async () => { + test('inspect tasks configuration', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const expectedTasksConfiguration = { 'version': '2.0.0', 'tasks': [ @@ -2183,31 +2234,31 @@ suite('WorkspaceConfigurationService-Multiroot', () => { await testObject.reloadConfiguration(); const actual = testObject.inspect('tasks').workspaceValue; assert.deepStrictEqual(actual, expectedTasksConfiguration); - }); + })); - test('update user configuration', async () => { + test('update user configuration', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await testObject.updateValue('configurationService.workspace.testSetting', 'userValue', ConfigurationTarget.USER); assert.strictEqual(testObject.getValue('configurationService.workspace.testSetting'), 'userValue'); - }); + })); - test('update user configuration should trigger change event before promise is resolve', async () => { + test('update user configuration should trigger change event before promise is resolve', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const target = sinon.spy(); testObject.onDidChangeConfiguration(target); await testObject.updateValue('configurationService.workspace.testSetting', 'userValue', ConfigurationTarget.USER); assert.ok(target.called); - }); + })); - test('update workspace configuration', async () => { + test('update workspace configuration', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await testObject.updateValue('configurationService.workspace.testSetting', 'workspaceValue', ConfigurationTarget.WORKSPACE); assert.strictEqual(testObject.getValue('configurationService.workspace.testSetting'), 'workspaceValue'); - }); + })); - test('update workspace configuration should trigger change event before promise is resolve', async () => { + test('update workspace configuration should trigger change event before promise is resolve', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const target = sinon.spy(); testObject.onDidChangeConfiguration(target); await testObject.updateValue('configurationService.workspace.testSetting', 'workspaceValue', ConfigurationTarget.WORKSPACE); assert.ok(target.called); - }); + })); test('update application setting into workspace configuration in a workspace is not supported', () => { return testObject.updateValue('configurationService.workspace.applicationSetting', 'workspaceValue', {}, ConfigurationTarget.WORKSPACE, true) @@ -2225,48 +2276,48 @@ suite('WorkspaceConfigurationService-Multiroot', () => { .then(() => assert.strictEqual(testObject.getValue('configurationService.workspace.testResourceSetting', { resource: workspace.folders[0].uri }), 'workspaceFolderValue')); }); - test('update resource language configuration in workspace folder', async () => { + test('update resource language configuration in workspace folder', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const workspace = workspaceContextService.getWorkspace(); await testObject.updateValue('configurationService.workspace.testLanguageSetting', 'workspaceFolderValue', { resource: workspace.folders[0].uri }, ConfigurationTarget.WORKSPACE_FOLDER); assert.strictEqual(testObject.getValue('configurationService.workspace.testLanguageSetting', { resource: workspace.folders[0].uri }), 'workspaceFolderValue'); - }); + })); - test('update workspace folder configuration should trigger change event before promise is resolve', async () => { + test('update workspace folder configuration should trigger change event before promise is resolve', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const workspace = workspaceContextService.getWorkspace(); const target = sinon.spy(); testObject.onDidChangeConfiguration(target); await testObject.updateValue('configurationService.workspace.testResourceSetting', 'workspaceFolderValue', { resource: workspace.folders[0].uri }, ConfigurationTarget.WORKSPACE_FOLDER); assert.ok(target.called); - }); + })); - test('update workspace folder configuration second time should trigger change event before promise is resolve', async () => { + test('update workspace folder configuration second time should trigger change event before promise is resolve', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const workspace = workspaceContextService.getWorkspace(); await testObject.updateValue('configurationService.workspace.testResourceSetting', 'workspaceFolderValue', { resource: workspace.folders[0].uri }, ConfigurationTarget.WORKSPACE_FOLDER); const target = sinon.spy(); testObject.onDidChangeConfiguration(target); await testObject.updateValue('configurationService.workspace.testResourceSetting', 'workspaceFolderValue2', { resource: workspace.folders[0].uri }, ConfigurationTarget.WORKSPACE_FOLDER); assert.ok(target.called); - }); + })); - test('update machine overridable setting in folder', async () => { + test('update machine overridable setting in folder', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const workspace = workspaceContextService.getWorkspace(); await testObject.updateValue('configurationService.workspace.machineOverridableSetting', 'workspaceFolderValue', { resource: workspace.folders[0].uri }, ConfigurationTarget.WORKSPACE_FOLDER); assert.strictEqual(testObject.getValue('configurationService.workspace.machineOverridableSetting', { resource: workspace.folders[0].uri }), 'workspaceFolderValue'); - }); + })); - test('update memory configuration', async () => { + test('update memory configuration', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await testObject.updateValue('configurationService.workspace.testSetting', 'memoryValue', ConfigurationTarget.MEMORY); assert.strictEqual(testObject.getValue('configurationService.workspace.testSetting'), 'memoryValue'); - }); + })); - test('update memory configuration should trigger change event before promise is resolve', async () => { + test('update memory configuration should trigger change event before promise is resolve', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const target = sinon.spy(); testObject.onDidChangeConfiguration(target); await testObject.updateValue('configurationService.workspace.testSetting', 'memoryValue', ConfigurationTarget.MEMORY); assert.ok(target.called); - }); + })); - test('remove setting from all targets', async () => { + test('remove setting from all targets', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const workspace = workspaceContextService.getWorkspace(); const key = 'configurationService.workspace.testResourceSetting'; await testObject.updateValue(key, 'workspaceFolderValue', { resource: workspace.folders[0].uri }, ConfigurationTarget.WORKSPACE_FOLDER); @@ -2280,28 +2331,28 @@ suite('WorkspaceConfigurationService-Multiroot', () => { assert.strictEqual(actual.userValue, undefined); assert.strictEqual(actual.workspaceValue, undefined); assert.strictEqual(actual.workspaceFolderValue, undefined); - }); + })); - test('update tasks configuration in a folder', async () => { + test('update tasks configuration in a folder', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const workspace = workspaceContextService.getWorkspace(); await testObject.updateValue('tasks', { 'version': '1.0.0', tasks: [{ 'taskName': 'myTask' }] }, { resource: workspace.folders[0].uri }, ConfigurationTarget.WORKSPACE_FOLDER); assert.deepStrictEqual(testObject.getValue(TasksSchemaProperties.Tasks, { resource: workspace.folders[0].uri }), { 'version': '1.0.0', tasks: [{ 'taskName': 'myTask' }] }); - }); + })); - test('update launch configuration in a workspace', async () => { + test('update launch configuration in a workspace', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const workspace = workspaceContextService.getWorkspace(); await testObject.updateValue('launch', { 'version': '1.0.0', configurations: [{ 'name': 'myLaunch' }] }, { resource: workspace.folders[0].uri }, ConfigurationTarget.WORKSPACE, true); assert.deepStrictEqual(testObject.getValue('launch'), { 'version': '1.0.0', configurations: [{ 'name': 'myLaunch' }] }); - }); + })); - test('update tasks configuration in a workspace', async () => { + test('update tasks configuration in a workspace', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const workspace = workspaceContextService.getWorkspace(); const tasks = { 'version': '2.0.0', tasks: [{ 'label': 'myTask' }] }; await testObject.updateValue('tasks', tasks, { resource: workspace.folders[0].uri }, ConfigurationTarget.WORKSPACE, true); assert.deepStrictEqual(testObject.getValue(TasksSchemaProperties.Tasks), tasks); - }); + })); - test('configuration of newly added folder is available on configuration change event', async () => { + test('configuration of newly added folder is available on configuration change event', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const workspaceService = testObject; const uri = workspaceService.getWorkspace().folders[1].uri; await workspaceService.removeFolders([uri]); @@ -2318,9 +2369,9 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); workspaceService.addFolders([{ uri }]); }); - }); + })); - test('restricted setting is read from workspace folders when workspace is trusted', async () => { + test('restricted setting is read from workspace folders when workspace is trusted', () => runWithFakedTimers({ useFakeTimers: true }, async () => { testObject.updateWorkspaceTrust(true); await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting1": "userValue", "configurationService.workspace.testRestrictedSetting2": "userValue" }')); @@ -2338,9 +2389,9 @@ suite('WorkspaceConfigurationService-Multiroot', () => { assert.strictEqual(testObject.restrictedSettings.workspaceFolder?.size, 1); assert.strictEqual(testObject.restrictedSettings.workspaceFolder?.get(testObject.getWorkspace().folders[0].uri), undefined); assert.deepStrictEqual(testObject.restrictedSettings.workspaceFolder?.get(testObject.getWorkspace().folders[1].uri), ['configurationService.workspace.testRestrictedSetting2']); - }); + })); - test('restricted setting is not read from workspace when workspace is not trusted', async () => { + test('restricted setting is not read from workspace when workspace is not trusted', () => runWithFakedTimers({ useFakeTimers: true }, async () => { testObject.updateWorkspaceTrust(false); await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting1": "userValue", "configurationService.workspace.testRestrictedSetting2": "userValue" }')); @@ -2358,9 +2409,9 @@ suite('WorkspaceConfigurationService-Multiroot', () => { assert.strictEqual(testObject.restrictedSettings.workspaceFolder?.size, 1); assert.strictEqual(testObject.restrictedSettings.workspaceFolder?.get(testObject.getWorkspace().folders[0].uri), undefined); assert.deepStrictEqual(testObject.restrictedSettings.workspaceFolder?.get(testObject.getWorkspace().folders[1].uri), ['configurationService.workspace.testRestrictedSetting2']); - }); + })); - test('remove an unregistered setting', async () => { + test('remove an unregistered setting', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const key = 'configurationService.workspace.unknownSetting'; await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.unknownSetting": "userValue" }')); await jsonEditingServce.write((workspaceContextService.getWorkspace().configuration!), [{ path: ['settings'], value: { 'configurationService.workspace.unknownSetting': 'workspaceValue' } }], true); @@ -2380,7 +2431,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { assert.strictEqual(actual.userValue, undefined); assert.strictEqual(actual.workspaceValue, undefined); assert.strictEqual(actual.workspaceFolderValue, undefined); - }); + })); }); @@ -2474,23 +2525,23 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { teardown(() => disposables.clear()); - test('remote settings override globals', async () => { + test('remote settings override globals', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(machineSettingsResource, VSBuffer.fromString('{ "configurationService.remote.machineSetting": "remoteValue" }')); registerRemoteFileSystemProvider(); resolveRemoteEnvironment(); await initialize(); assert.strictEqual(testObject.getValue('configurationService.remote.machineSetting'), 'remoteValue'); - }); + })); - test('remote settings override globals after remote provider is registered on activation', async () => { + test('remote settings override globals after remote provider is registered on activation', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(machineSettingsResource, VSBuffer.fromString('{ "configurationService.remote.machineSetting": "remoteValue" }')); resolveRemoteEnvironment(); registerRemoteFileSystemProviderOnActivation(); await initialize(); assert.strictEqual(testObject.getValue('configurationService.remote.machineSetting'), 'remoteValue'); - }); + })); - test('remote settings override globals after remote environment is resolved', async () => { + test('remote settings override globals after remote environment is resolved', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(machineSettingsResource, VSBuffer.fromString('{ "configurationService.remote.machineSetting": "remoteValue" }')); registerRemoteFileSystemProvider(); await initialize(); @@ -2508,9 +2559,9 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { }); resolveRemoteEnvironment(); return promise; - }); + })); - test('remote settings override globals after remote provider is registered on activation and remote environment is resolved', async () => { + test('remote settings override globals after remote provider is registered on activation and remote environment is resolved', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(machineSettingsResource, VSBuffer.fromString('{ "configurationService.remote.machineSetting": "remoteValue" }')); registerRemoteFileSystemProviderOnActivation(); await initialize(); @@ -2528,52 +2579,52 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { }); resolveRemoteEnvironment(); return promise; - }); + })); - test('machine settings in local user settings does not override defaults', async () => { + test('machine settings in local user settings does not override defaults', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.machineSetting": "globalValue" }')); registerRemoteFileSystemProvider(); resolveRemoteEnvironment(); await initialize(); assert.strictEqual(testObject.getValue('configurationService.remote.machineSetting'), 'isSet'); - }); + })); - test('machine overridable settings in local user settings does not override defaults', async () => { + test('machine overridable settings in local user settings does not override defaults', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.machineOverridableSetting": "globalValue" }')); registerRemoteFileSystemProvider(); resolveRemoteEnvironment(); await initialize(); assert.strictEqual(testObject.getValue('configurationService.remote.machineOverridableSetting'), 'isSet'); - }); + })); - test('non machine setting is written in local settings', async () => { + test('non machine setting is written in local settings', () => runWithFakedTimers({ useFakeTimers: true }, async () => { registerRemoteFileSystemProvider(); resolveRemoteEnvironment(); await initialize(); await testObject.updateValue('configurationService.remote.applicationSetting', 'applicationValue'); await testObject.reloadConfiguration(); assert.strictEqual(testObject.inspect('configurationService.remote.applicationSetting').userLocalValue, 'applicationValue'); - }); + })); - test('machine setting is written in remote settings', async () => { + test('machine setting is written in remote settings', () => runWithFakedTimers({ useFakeTimers: true }, async () => { registerRemoteFileSystemProvider(); resolveRemoteEnvironment(); await initialize(); await testObject.updateValue('configurationService.remote.machineSetting', 'machineValue'); await testObject.reloadConfiguration(); assert.strictEqual(testObject.inspect('configurationService.remote.machineSetting').userRemoteValue, 'machineValue'); - }); + })); - test('machine overridable setting is written in remote settings', async () => { + test('machine overridable setting is written in remote settings', () => runWithFakedTimers({ useFakeTimers: true }, async () => { registerRemoteFileSystemProvider(); resolveRemoteEnvironment(); await initialize(); await testObject.updateValue('configurationService.remote.machineOverridableSetting', 'machineValue'); await testObject.reloadConfiguration(); assert.strictEqual(testObject.inspect('configurationService.remote.machineOverridableSetting').userRemoteValue, 'machineValue'); - }); + })); - test('machine settings in local user settings does not override defaults after defalts are registered ', async () => { + test('machine settings in local user settings does not override defaults after defalts are registered ', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.newMachineSetting": "userValue" }')); registerRemoteFileSystemProvider(); resolveRemoteEnvironment(); @@ -2590,9 +2641,9 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { } }); assert.strictEqual(testObject.getValue('configurationService.remote.newMachineSetting'), 'isSet'); - }); + })); - test('machine overridable settings in local user settings does not override defaults after defaults are registered ', async () => { + test('machine overridable settings in local user settings does not override defaults after defaults are registered ', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.newMachineOverridableSetting": "userValue" }')); registerRemoteFileSystemProvider(); resolveRemoteEnvironment(); @@ -2609,7 +2660,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { } }); assert.strictEqual(testObject.getValue('configurationService.remote.newMachineOverridableSetting'), 'isSet'); - }); + })); }); diff --git a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts index 4a7d00a5b4d..70f5d1cd368 100644 --- a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts @@ -5,7 +5,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILabelService } from 'vs/platform/label/common/label'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -33,4 +33,4 @@ export class ConfigurationResolverService extends BaseConfigurationResolverServi } } -registerSingleton(IConfigurationResolverService, ConfigurationResolverService, true); +registerSingleton(IConfigurationResolverService, ConfigurationResolverService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/configurationResolver/electron-sandbox/configurationResolverService.ts b/src/vs/workbench/services/configurationResolver/electron-sandbox/configurationResolverService.ts index 14a385e3e06..bb1a8751e6d 100644 --- a/src/vs/workbench/services/configurationResolver/electron-sandbox/configurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/electron-sandbox/configurationResolverService.ts @@ -10,7 +10,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { BaseConfigurationResolverService } from 'vs/workbench/services/configurationResolver/browser/baseConfigurationResolverService'; import { ILabelService } from 'vs/platform/label/common/label'; import { IShellEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/shellEnvironmentService'; @@ -43,4 +43,4 @@ export class ConfigurationResolverService extends BaseConfigurationResolverServi } } -registerSingleton(IConfigurationResolverService, ConfigurationResolverService, true); +registerSingleton(IConfigurationResolverService, ConfigurationResolverService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts b/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts index fa2034e1ee7..4433a8b533f 100644 --- a/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts +++ b/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts @@ -5,7 +5,7 @@ import { IAction, IActionRunner, ActionRunner, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification, Separator, SubmenuAction } from 'vs/base/common/actions'; import * as dom from 'vs/base/browser/dom'; -import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IContextMenuMenuDelegate, IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { getZoomFactor } from 'vs/base/browser/browser'; @@ -13,25 +13,27 @@ import { unmnemonicLabel } from 'vs/base/common/labels'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IContextMenuDelegate, IContextMenuEvent } from 'vs/base/browser/contextmenu'; import { once } from 'vs/base/common/functional'; -import { Disposable } from 'vs/base/common/lifecycle'; import { IContextMenuItem } from 'vs/base/parts/contextmenu/common/contextmenu'; import { popup } from 'vs/base/parts/contextmenu/electron-sandbox/contextmenu'; import { getTitleBarStyle } from 'vs/platform/window/common/window'; import { isMacintosh, isWindows } from 'vs/base/common/platform'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ContextMenuService as HTMLContextMenuService } from 'vs/platform/contextview/browser/contextMenuService'; +import { ContextMenuMenuDelegate, ContextMenuService as HTMLContextMenuService } from 'vs/platform/contextview/browser/contextMenuService'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { stripIcons } from 'vs/base/common/iconLabels'; import { coalesce } from 'vs/base/common/arrays'; import { Event, Emitter } from 'vs/base/common/event'; import { AnchorAlignment, AnchorAxisAlignment } from 'vs/base/browser/ui/contextview/contextview'; +import { IMenuService } from 'vs/platform/actions/common/actions'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { Disposable } from 'vs/base/common/lifecycle'; -export class ContextMenuService extends Disposable implements IContextMenuService { +export class ContextMenuService implements IContextMenuService { declare readonly _serviceBrand: undefined; - private impl: IContextMenuService; + private impl: HTMLContextMenuService | NativeContextMenuService; get onDidShowContextMenu(): Event { return this.impl.onDidShowContextMenu; } get onDidHideContextMenu(): Event { return this.impl.onDidHideContextMenu; } @@ -42,22 +44,27 @@ export class ContextMenuService extends Disposable implements IContextMenuServic @IKeybindingService keybindingService: IKeybindingService, @IConfigurationService configurationService: IConfigurationService, @IContextViewService contextViewService: IContextViewService, - @IThemeService themeService: IThemeService + @IThemeService themeService: IThemeService, + @IMenuService menuService: IMenuService, + @IContextKeyService contextKeyService: IContextKeyService, ) { - super(); // Custom context menu: Linux/Windows if custom title is enabled if (!isMacintosh && getTitleBarStyle(configurationService) === 'custom') { - this.impl = new HTMLContextMenuService(telemetryService, notificationService, contextViewService, keybindingService, themeService); + this.impl = new HTMLContextMenuService(telemetryService, notificationService, contextViewService, keybindingService, themeService, menuService, contextKeyService); } // Native context menu: otherwise else { - this.impl = new NativeContextMenuService(notificationService, telemetryService, keybindingService); + this.impl = new NativeContextMenuService(notificationService, telemetryService, keybindingService, menuService, contextKeyService); } } - showContextMenu(delegate: IContextMenuDelegate): void { + dispose(): void { + this.impl.dispose(); + } + + showContextMenu(delegate: IContextMenuDelegate | IContextMenuMenuDelegate): void { this.impl.showContextMenu(delegate); } } @@ -66,21 +73,26 @@ class NativeContextMenuService extends Disposable implements IContextMenuService declare readonly _serviceBrand: undefined; - private readonly _onDidShowContextMenu = new Emitter(); + private readonly _onDidShowContextMenu = this._store.add(new Emitter()); readonly onDidShowContextMenu = this._onDidShowContextMenu.event; - private readonly _onDidHideContextMenu = new Emitter(); + private readonly _onDidHideContextMenu = this._store.add(new Emitter()); readonly onDidHideContextMenu = this._onDidHideContextMenu.event; constructor( @INotificationService private readonly notificationService: INotificationService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IKeybindingService private readonly keybindingService: IKeybindingService + @IKeybindingService private readonly keybindingService: IKeybindingService, + @IMenuService private readonly menuService: IMenuService, + @IContextKeyService private readonly contextKeyService: IContextKeyService, ) { super(); } - showContextMenu(delegate: IContextMenuDelegate): void { + showContextMenu(delegate: IContextMenuDelegate | IContextMenuMenuDelegate): void { + + delegate = ContextMenuMenuDelegate.transform(delegate, this.menuService, this.contextKeyService); + const actions = delegate.getActions(); if (actions.length) { const onHide = once(() => { @@ -247,4 +259,4 @@ class NativeContextMenuService extends Disposable implements IContextMenuService } } -registerSingleton(IContextMenuService, ContextMenuService, true); +registerSingleton(IContextMenuService, ContextMenuService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/decorations/browser/decorationsService.ts b/src/vs/workbench/services/decorations/browser/decorationsService.ts index 42a7850a675..b8d5d9491ad 100644 --- a/src/vs/workbench/services/decorations/browser/decorationsService.ts +++ b/src/vs/workbench/services/decorations/browser/decorationsService.ts @@ -16,7 +16,7 @@ import { isFalsyOrWhitespace } from 'vs/base/common/strings'; import { localize } from 'vs/nls'; import { isCancellationError } from 'vs/base/common/errors'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { hash } from 'vs/base/common/hash'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { asArray, distinct } from 'vs/base/common/arrays'; @@ -408,4 +408,4 @@ export class DecorationsService implements IDecorationsService { } } -registerSingleton(IDecorationsService, DecorationsService, true); +registerSingleton(IDecorationsService, DecorationsService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts b/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts index fabbda76cb7..2282c22ec86 100644 --- a/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts +++ b/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts @@ -323,12 +323,16 @@ export abstract class AbstractFileDialogService implements IFileDialogService { const filter: IFilter = { name: languageName, extensions: distinct(extensions).slice(0, 10).map(e => trim(e, '.')) }; - if (!matchingFilter && extensions.includes(ext || PLAINTEXT_EXTENSION /* https://github.com/microsoft/vscode/issues/115860 */)) { + // https://github.com/microsoft/vscode/issues/115860 + const extOrPlaintext = ext || PLAINTEXT_EXTENSION; + if (!matchingFilter && extensions.includes(extOrPlaintext)) { matchingFilter = filter; - const trimmedExt = trim(ext || PLAINTEXT_EXTENSION, '.'); + // The selected extension must be in the set of extensions that are in the filter list that is sent to the save dialog. + // If it isn't, add it manually. https://github.com/microsoft/vscode/issues/147657 + const trimmedExt = trim(extOrPlaintext, '.'); if (!filter.extensions.includes(trimmedExt)) { - filter.extensions.push(trimmedExt); + filter.extensions.unshift(trimmedExt); } return null; // first matching filter will be added to the top diff --git a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts index 87f6e2ca052..68aab5fc304 100644 --- a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts +++ b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts @@ -5,7 +5,7 @@ import { IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, IFileDialogService, FileFilter } from 'vs/platform/dialogs/common/dialogs'; import { URI } from 'vs/base/common/uri'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { AbstractFileDialogService } from 'vs/workbench/services/dialogs/browser/abstractFileDialogService'; import { Schemas } from 'vs/base/common/network'; import { memoize } from 'vs/base/common/decorators'; @@ -270,4 +270,4 @@ export class FileDialogService extends AbstractFileDialogService implements IFil } } -registerSingleton(IFileDialogService, FileDialogService, true); +registerSingleton(IFileDialogService, FileDialogService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/dialogs/common/dialogService.ts b/src/vs/workbench/services/dialogs/common/dialogService.ts index 7360fe7d875..13da7563bae 100644 --- a/src/vs/workbench/services/dialogs/common/dialogService.ts +++ b/src/vs/workbench/services/dialogs/common/dialogService.ts @@ -7,7 +7,7 @@ import Severity from 'vs/base/common/severity'; import { Disposable } from 'vs/base/common/lifecycle'; import { IConfirmation, IConfirmationResult, IDialogOptions, IDialogService, IInput, IInputResult, IShowResult } from 'vs/platform/dialogs/common/dialogs'; import { DialogsModel } from 'vs/workbench/common/dialogs'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { ILogService } from 'vs/platform/log/common/log'; @@ -78,4 +78,4 @@ export class DialogService extends Disposable implements IDialogService { } } -registerSingleton(IDialogService, DialogService, true); +registerSingleton(IDialogService, DialogService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/dialogs/electron-sandbox/fileDialogService.ts b/src/vs/workbench/services/dialogs/electron-sandbox/fileDialogService.ts index 36f7e3e76a1..bfbc3211a7f 100644 --- a/src/vs/workbench/services/dialogs/electron-sandbox/fileDialogService.ts +++ b/src/vs/workbench/services/dialogs/electron-sandbox/fileDialogService.ts @@ -12,7 +12,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { URI } from 'vs/base/common/uri'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; @@ -200,4 +200,4 @@ export class FileDialogService extends AbstractFileDialogService implements IFil } } -registerSingleton(IFileDialogService, FileDialogService, true); +registerSingleton(IFileDialogService, FileDialogService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/editor/browser/codeEditorService.ts b/src/vs/workbench/services/editor/browser/codeEditorService.ts index 492e5077def..657f203312c 100644 --- a/src/vs/workbench/services/editor/browser/codeEditorService.ts +++ b/src/vs/workbench/services/editor/browser/codeEditorService.ts @@ -11,7 +11,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor'; import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { isEqual } from 'vs/base/common/resources'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { applyTextEditorOptions } from 'vs/workbench/common/editor/editorOptions'; @@ -113,4 +113,4 @@ export class CodeEditorService extends AbstractCodeEditorService { } } -registerSingleton(ICodeEditorService, CodeEditorService, true); +registerSingleton(ICodeEditorService, CodeEditorService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/editor/browser/editorResolverService.ts b/src/vs/workbench/services/editor/browser/editorResolverService.ts index b35aabc0711..6731ee8717c 100644 --- a/src/vs/workbench/services/editor/browser/editorResolverService.ts +++ b/src/vs/workbench/services/editor/browser/editorResolverService.ts @@ -19,7 +19,7 @@ import { QuickPickItem, IKeyMods, IQuickInputService, IQuickPickItem, IQuickPick import { localize } from 'vs/nls'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; @@ -829,4 +829,4 @@ export class EditorResolverService extends Disposable implements IEditorResolver } } -registerSingleton(IEditorResolverService, EditorResolverService, false); +registerSingleton(IEditorResolverService, EditorResolverService, InstantiationType.Eager); diff --git a/src/vs/workbench/services/encryption/browser/encryptionService.ts b/src/vs/workbench/services/encryption/browser/encryptionService.ts index e204f45bd49..8e745e85578 100644 --- a/src/vs/workbench/services/encryption/browser/encryptionService.ts +++ b/src/vs/workbench/services/encryption/browser/encryptionService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { IEncryptionService } from 'vs/workbench/services/encryption/common/encryptionService'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; @@ -35,4 +35,4 @@ export class EncryptionService implements IEncryptionService { } } -registerSingleton(IEncryptionService, EncryptionService, true); +registerSingleton(IEncryptionService, EncryptionService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index 372ac7bb167..0f3e507ebb0 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -18,6 +18,7 @@ import { LogLevelToString } from 'vs/platform/log/common/log'; import { isUndefined } from 'vs/base/common/types'; import { refineServiceDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; +import { EXTENSION_IDENTIFIER_WITH_LOG_REGEX } from 'vs/platform/environment/common/environmentService'; export const IBrowserWorkbenchEnvironmentService = refineServiceDecorator(IEnvironmentService); @@ -47,10 +48,34 @@ export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvi get logsPath(): string { return this.logsHome.path; } @memoize - get logLevel(): string | undefined { return this.payload?.get('logLevel') || (this.options.developmentOptions?.logLevel !== undefined ? LogLevelToString(this.options.developmentOptions?.logLevel) : undefined); } + get logLevel(): string | undefined { + const logLevelFromPayload = this.payload?.get('logLevel'); + if (logLevelFromPayload) { + return logLevelFromPayload.split(',').find(entry => !EXTENSION_IDENTIFIER_WITH_LOG_REGEX.test(entry)); + } + return this.options.developmentOptions?.logLevel !== undefined ? LogLevelToString(this.options.developmentOptions?.logLevel) : undefined; + } + + get extensionLogLevel(): [string, string][] | undefined { + const logLevelFromPayload = this.payload?.get('logLevel'); + if (logLevelFromPayload) { + const result: [string, string][] = []; + for (const entry of logLevelFromPayload.split(',')) { + const matches = EXTENSION_IDENTIFIER_WITH_LOG_REGEX.exec(entry); + if (matches && matches[1] && matches[2]) { + result.push([matches[1], matches[2]]); + } + } + return result.length ? result : undefined; + } + return this.options.developmentOptions?.extensionLogLevel !== undefined ? this.options.developmentOptions?.extensionLogLevel.map(([extension, logLevel]) => ([extension, LogLevelToString(logLevel)])) : undefined; + } @memoize - get logFile(): URI { return joinPath(this.logsHome, 'window.log'); } + get windowLogsPath(): URI { return this.logsHome; } + + @memoize + get logFile(): URI { return joinPath(this.windowLogsPath, 'window.log'); } @memoize get userRoamingDataHome(): URI { return URI.file('/User').with({ scheme: Schemas.vscodeUserData }); } @@ -101,6 +126,11 @@ export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvi @memoize get extHostLogsPath(): URI { return joinPath(this.logsHome, 'exthost'); } + @memoize + get extHostTelemetryLogFile(): URI { + return joinPath(this.extHostLogsPath, 'telemetry.log'); + } + private extensionHostDebugEnvironment: IExtensionHostDebugEnvironment | undefined = undefined; @memoize diff --git a/src/vs/workbench/services/environment/common/environmentService.ts b/src/vs/workbench/services/environment/common/environmentService.ts index de37a985f17..a1b4d8341d1 100644 --- a/src/vs/workbench/services/environment/common/environmentService.ts +++ b/src/vs/workbench/services/environment/common/environmentService.ts @@ -24,7 +24,9 @@ export interface IWorkbenchEnvironmentService extends IEnvironmentService { // --- Paths readonly logFile: URI; + readonly windowLogsPath: URI; readonly extHostLogsPath: URI; + readonly extHostTelemetryLogFile: URI; // --- Extensions readonly extensionEnabledProposedApi?: string[]; diff --git a/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts b/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts index a18fa598d41..72ee95090af 100644 --- a/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts +++ b/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts @@ -14,6 +14,7 @@ import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { join } from 'vs/base/common/path'; import { IProductService } from 'vs/platform/product/common/productService'; +import { joinPath } from 'vs/base/common/resources'; export const INativeWorkbenchEnvironmentService = refineServiceDecorator(IEnvironmentService); @@ -85,10 +86,18 @@ export class NativeWorkbenchEnvironmentService extends AbstractNativeEnvironment override get userRoamingDataHome(): URI { return this.appSettingsHome.with({ scheme: Schemas.vscodeUserData }); } @memoize - get logFile(): URI { return URI.file(join(this.logsPath, `renderer${this.configuration.windowId}.log`)); } + get windowLogsPath(): URI { return URI.file(join(this.logsPath, `window${this.configuration.windowId}`)); } @memoize - get extHostLogsPath(): URI { return URI.file(join(this.logsPath, `exthost${this.configuration.windowId}`)); } + get logFile(): URI { return joinPath(this.windowLogsPath, `renderer.log`); } + + @memoize + get extHostLogsPath(): URI { return joinPath(this.windowLogsPath, 'exthost'); } + + @memoize + get extHostTelemetryLogFile(): URI { + return joinPath(this.extHostLogsPath, 'telemetry.log'); + } @memoize get webviewExternalEndpoint(): string { return `${Schemas.vscodeWebview}://{{uuid}}`; } diff --git a/src/vs/workbench/services/environment/electron-sandbox/shellEnvironmentService.ts b/src/vs/workbench/services/environment/electron-sandbox/shellEnvironmentService.ts index cee48fc8657..71ac3a7fbe6 100644 --- a/src/vs/workbench/services/environment/electron-sandbox/shellEnvironmentService.ts +++ b/src/vs/workbench/services/environment/electron-sandbox/shellEnvironmentService.ts @@ -6,7 +6,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IProcessEnvironment } from 'vs/base/common/platform'; import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; export const IShellEnvironmentService = createDecorator('shellEnvironmentService'); @@ -26,4 +26,4 @@ export class ShellEnvironmentService implements IShellEnvironmentService { } } -registerSingleton(IShellEnvironmentService, ShellEnvironmentService, true); +registerSingleton(IShellEnvironmentService, ShellEnvironmentService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/extensionManagement/browser/builtinExtensionsScannerService.ts b/src/vs/workbench/services/extensionManagement/browser/builtinExtensionsScannerService.ts index 7fd4e622342..41a586dc3f1 100644 --- a/src/vs/workbench/services/extensionManagement/browser/builtinExtensionsScannerService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/builtinExtensionsScannerService.ts @@ -7,11 +7,11 @@ import { IBuiltinExtensionsScannerService, ExtensionType, IExtensionManifest, Ta import { isWeb, Language } from 'vs/base/common/platform'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { FileAccess } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; -import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; +import { IExtensionResourceLoaderService } from 'vs/platform/extensionResourceLoader/common/extensionResourceLoader'; import { IProductService } from 'vs/platform/product/common/productService'; import { ITranslations, localizeManifest } from 'vs/platform/extensionManagement/common/extensionNls'; import { ILogService } from 'vs/platform/log/common/log'; @@ -113,4 +113,4 @@ export class BuiltinExtensionsScannerService implements IBuiltinExtensionsScanne } } -registerSingleton(IBuiltinExtensionsScannerService, BuiltinExtensionsScannerService, true); +registerSingleton(IBuiltinExtensionsScannerService, BuiltinExtensionsScannerService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/extensionManagement/browser/extensionBisect.ts b/src/vs/workbench/services/extensionManagement/browser/extensionBisect.ts index 2a099ed043a..c9d26ad3b32 100644 --- a/src/vs/workbench/services/extensionManagement/browser/extensionBisect.ts +++ b/src/vs/workbench/services/extensionManagement/browser/extensionBisect.ts @@ -7,7 +7,7 @@ import { localize } from 'vs/nls'; import { IExtensionManagementService, IGlobalExtensionEnablementService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ExtensionType, IExtension, isResolverExtension } from 'vs/platform/extensions/common/extensions'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { createDecorator, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; @@ -23,7 +23,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; // --- bisect service @@ -161,7 +161,7 @@ class ExtensionBisectService implements IExtensionBisectService { } } -registerSingleton(IExtensionBisectService, ExtensionBisectService, true); +registerSingleton(IExtensionBisectService, ExtensionBisectService, InstantiationType.Delayed); // --- bisect UI @@ -211,7 +211,6 @@ class ExtensionBisectUi { Registry.as(Extensions.Workbench).registerWorkbenchContribution( ExtensionBisectUi, - 'ExtensionBisectUi', LifecyclePhase.Restored ); @@ -220,7 +219,7 @@ registerAction2(class extends Action2 { super({ id: 'extension.bisect.start', title: { value: localize('title.start', "Start Extension Bisect"), original: 'Start Extension Bisect' }, - category: CATEGORIES.Help, + category: Categories.Help, f1: true, precondition: ExtensionBisectUi.ctxIsBisectActive.negate(), menu: { @@ -260,7 +259,7 @@ registerAction2(class extends Action2 { super({ id: 'extension.bisect.next', title: { value: localize('title.isBad', "Continue Extension Bisect"), original: 'Continue Extension Bisect' }, - category: localize('help', "Help"), + category: Categories.Help, f1: true, precondition: ExtensionBisectUi.ctxIsBisectActive }); @@ -347,7 +346,7 @@ registerAction2(class extends Action2 { super({ id: 'extension.bisect.stop', title: { value: localize('title.stop', "Stop Extension Bisect"), original: 'Stop Extension Bisect' }, - category: localize('help', "Help"), + category: Categories.Help, f1: true, precondition: ExtensionBisectUi.ctxIsBisectActive }); diff --git a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts index 3c350eae2d8..94369a63ef3 100644 --- a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts @@ -14,7 +14,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IExtension, isAuthenticationProviderExtension, isLanguagePackExtension, isResolverExtension } from 'vs/platform/extensions/common/extensions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { StorageManager } from 'vs/platform/extensionManagement/common/extensionEnablementService'; import { webWorkerExtHostConfig, WebWorkerExtHostConfigValue } from 'vs/workbench/services/extensions/common/extensions'; import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; @@ -236,7 +236,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench checked.push(extension); } - const extensionsToDisable: IExtension[] = []; + const extensionsToEnable: IExtension[] = []; for (const extension of allExtensions) { // Extension is already checked if (checked.some(e => areSameExtensions(e.identifier, extension.identifier))) { @@ -244,8 +244,13 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench } const enablementStateOfExtension = this.getEnablementState(extension); - // Extension enablement state is same as the end enablement state - if (enablementStateOfExtension === enablementState) { + // Extension is enabled + if (this.isEnabledEnablementState(enablementStateOfExtension)) { + continue; + } + + // Skip if dependency extension is disabled by extension kind + if (enablementStateOfExtension === EnablementState.DisabledByExtensionKind) { continue; } @@ -254,11 +259,11 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench (options.dependencies && e.manifest.extensionDependencies?.some(id => areSameExtensions({ id }, extension.identifier))) || (options.pack && e.manifest.extensionPack?.some(id => areSameExtensions({ id }, extension.identifier))))) { - const index = extensionsToDisable.findIndex(e => areSameExtensions(e.identifier, extension.identifier)); + const index = extensionsToEnable.findIndex(e => areSameExtensions(e.identifier, extension.identifier)); // Extension is not aded to the disablement list so add it if (index === -1) { - extensionsToDisable.push(extension); + extensionsToEnable.push(extension); } // Extension is there already in the disablement list. @@ -266,17 +271,17 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench try { // Replace only if the enablement state can be changed this.throwErrorIfEnablementStateCannotBeChanged(extension, enablementStateOfExtension, true); - extensionsToDisable.splice(index, 1, extension); + extensionsToEnable.splice(index, 1, extension); } catch (error) { /*Do not add*/ } } } } - if (extensionsToDisable.length) { - extensionsToDisable.push(...this.getExtensionsToEnableRecursively(extensionsToDisable, allExtensions, enablementState, options, checked)); + if (extensionsToEnable.length) { + extensionsToEnable.push(...this.getExtensionsToEnableRecursively(extensionsToEnable, allExtensions, enablementState, options, checked)); } - return extensionsToDisable; + return extensionsToEnable; } private _setUserEnablementState(extension: IExtension, newState: EnablementState): Promise { @@ -711,4 +716,4 @@ class ExtensionsManager extends Disposable { } } -registerSingleton(IWorkbenchExtensionEnablementService, ExtensionEnablementService, true); +registerSingleton(IWorkbenchExtensionEnablementService, ExtensionEnablementService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/extensionManagement/browser/extensionUrlTrustService.ts b/src/vs/workbench/services/extensionManagement/browser/extensionUrlTrustService.ts index 9df7560ac34..31127de9b07 100644 --- a/src/vs/workbench/services/extensionManagement/browser/extensionUrlTrustService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/extensionUrlTrustService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; class ExtensionUrlTrustService implements IExtensionUrlTrustService { @@ -15,4 +15,4 @@ class ExtensionUrlTrustService implements IExtensionUrlTrustService { } } -registerSingleton(IExtensionUrlTrustService, ExtensionUrlTrustService, false); +registerSingleton(IExtensionUrlTrustService, ExtensionUrlTrustService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts b/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts index 34432033ea5..313e55a10b5 100644 --- a/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts @@ -7,7 +7,7 @@ import { IBuiltinExtensionsScannerService, ExtensionType, IExtensionIdentifier, import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { IScannedExtension, IWebExtensionsScannerService, ScanOptions } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { isWeb, Language } from 'vs/base/common/platform'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { joinPath } from 'vs/base/common/resources'; import { URI, UriComponents } from 'vs/base/common/uri'; import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files'; @@ -25,9 +25,9 @@ import { isString } from 'vs/base/common/types'; import { getErrorMessage } from 'vs/base/common/errors'; import { ResourceMap } from 'vs/base/common/map'; import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; -import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; +import { IExtensionResourceLoaderService } from 'vs/platform/extensionResourceLoader/common/extensionResourceLoader'; import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; @@ -137,6 +137,12 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten if (extensions.length) { extensions = await this.checkAdditionalBuiltinExtensions(extensions); } + if (extensions.length) { + this.logService.info('Found additional builtin gallery extensions in env', extensions); + } + if (extensionLocations.length) { + this.logService.info('Found additional builtin location extensions in env', extensionLocations.map(e => e.toString())); + } return { extensions, extensionsToMigrate, extensionLocations }; })(); } @@ -209,6 +215,8 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten const extension = await this.toScannedExtension(webExtension, true); if (extension.isValid || !scanOptions?.skipInvalidExtensions) { result.push(extension); + } else { + this.logService.info(`Skipping invalid additional builtin extension ${webExtension.identifier.id}`); } } catch (error) { this.logService.info(`Error while fetching the additional builtin extension ${location.toString()}.`, getErrorMessage(error)); @@ -218,15 +226,12 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten } private async getCustomBuiltinExtensionsFromGallery(scanOptions?: ScanOptions): Promise { - const { extensions } = await this.readCustomBuiltinExtensionsInfoFromEnv(); - if (!extensions.length) { - return []; - } if (!this.galleryService.isEnabled()) { this.logService.info('Ignoring fetching additional builtin extensions from gallery as it is disabled.'); return []; } const result: IScannedExtension[] = []; + const { extensions } = await this.readCustomBuiltinExtensionsInfoFromEnv(); try { const useCache = this.storageService.get('additionalBuiltinExtensions', StorageScope.APPLICATION, '[]') === JSON.stringify(extensions); const webExtensions = await (useCache ? this.getCustomBuiltinExtensionsFromCache() : this.updateCustomBuiltinExtensionsCache()); @@ -236,6 +241,8 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten const extension = await this.toScannedExtension(webExtension, true); if (extension.isValid || !scanOptions?.skipInvalidExtensions) { result.push(extension); + } else { + this.logService.info(`Skipping invalid additional builtin gallery extension ${webExtension.identifier.id}`); } } catch (error) { this.logService.info(`Ignoring additional builtin extension ${webExtension.identifier.id} because there is an error while converting it into scanned extension`, getErrorMessage(error)); @@ -318,31 +325,23 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten private async updateCustomBuiltinExtensionsCache(): Promise { if (!this._updateCustomBuiltinExtensionsCachePromise) { this._updateCustomBuiltinExtensionsCachePromise = (async () => { - // Clear Cache - await this.writeCustomBuiltinExtensionsCache(() => []); - - const { extensions } = await this.readCustomBuiltinExtensionsInfoFromEnv(); - - if (!extensions.length) { - return []; - } - - const galleryExtensionsMap = await this.getExtensionsWithDependenciesAndPackedExtensions(extensions); - - const missingExtensions = extensions.filter(({ id }) => !galleryExtensionsMap.has(id.toLowerCase())); - if (missingExtensions.length) { - this.logService.info('Skipping the additional builtin extensions because their compatible versions are not found.', missingExtensions); - } - + this.logService.info('Updating additional builtin extensions cache'); const webExtensions: IWebExtension[] = []; - await Promise.all([...galleryExtensionsMap.values()].map(async gallery => { - try { - webExtensions.push(await this.toWebExtensionFromGallery(gallery, { isPreReleaseVersion: gallery.properties.isPreReleaseVersion, preRelease: gallery.properties.isPreReleaseVersion, isBuiltin: true })); - } catch (error) { - this.logService.info(`Ignoring additional builtin extension ${gallery.identifier.id} because there is an error while converting it into web extension`, getErrorMessage(error)); + const { extensions } = await this.readCustomBuiltinExtensionsInfoFromEnv(); + if (extensions.length) { + const galleryExtensionsMap = await this.getExtensionsWithDependenciesAndPackedExtensions(extensions); + const missingExtensions = extensions.filter(({ id }) => !galleryExtensionsMap.has(id.toLowerCase())); + if (missingExtensions.length) { + this.logService.info('Skipping the additional builtin extensions because their compatible versions are not found.', missingExtensions); } - })); - + await Promise.all([...galleryExtensionsMap.values()].map(async gallery => { + try { + webExtensions.push(await this.toWebExtensionFromGallery(gallery, { isPreReleaseVersion: gallery.properties.isPreReleaseVersion, preRelease: gallery.properties.isPreReleaseVersion, isBuiltin: true })); + } catch (error) { + this.logService.info(`Ignoring additional builtin extension ${gallery.identifier.id} because there is an error while converting it into web extension`, getErrorMessage(error)); + } + })); + } await this.writeCustomBuiltinExtensionsCache(() => webExtensions); return webExtensions; })(); @@ -534,6 +533,8 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten const extension = await this.toScannedExtension(webExtension, false); if (extension.isValid || !scanOptions?.skipInvalidExtensions) { result.set(extension.identifier.id.toLowerCase(), extension); + } else { + this.logService.info(`Skipping invalid installed extension ${webExtension.identifier.id}`); } } return [...result.values()]; @@ -874,7 +875,7 @@ if (isWeb) { super({ id: 'workbench.extensions.action.openInstalledWebExtensionsResource', title: { value: localize('openInstalledWebExtensionsResource', "Open Installed Web Extensions Resource"), original: 'Open Installed Web Extensions Resource' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true, precondition: IsWebContext }); @@ -887,4 +888,4 @@ if (isWeb) { }); } -registerSingleton(IWebExtensionsScannerService, WebExtensionsScannerService, true); +registerSingleton(IWebExtensionsScannerService, WebExtensionsScannerService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts index 4db3f9b85cc..c56e6177daf 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts @@ -4,18 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { ExtensionInstallLocation, IExtensionManagementServer, IExtensionManagementServerService, IProfileAwareExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { ExtensionInstallLocation, IExtensionManagementServer, IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { Schemas } from 'vs/base/common/network'; -import { Event } from 'vs/base/common/event'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILabelService } from 'vs/platform/label/common/label'; import { isWeb } from 'vs/base/common/platform'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { WebExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/webExtensionManagementService'; import { IExtension } from 'vs/platform/extensions/common/extensions'; -import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; +import { RemoteExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/remoteExtensionManagementService'; export class ExtensionManagementServerService implements IExtensionManagementServerService { @@ -32,13 +31,7 @@ export class ExtensionManagementServerService implements IExtensionManagementSer ) { const remoteAgentConnection = remoteAgentService.getConnection(); if (remoteAgentConnection) { - const extensionManagementService = new class extends ExtensionManagementChannelClient implements IProfileAwareExtensionManagementService { - get onProfileAwareInstallExtension() { return super.onInstallExtension; } - get onProfileAwareDidInstallExtensions() { return super.onDidInstallExtensions; } - get onProfileAwareUninstallExtension() { return super.onUninstallExtension; } - get onProfileAwareDidUninstallExtension() { return super.onDidUninstallExtension; } - readonly onDidChangeProfile = Event.None; - }(remoteAgentConnection.getChannel('extensions')); + const extensionManagementService = instantiationService.createInstance(RemoteExtensionManagementService, remoteAgentConnection.getChannel('extensions')); this.remoteExtensionManagementServer = { id: 'remote', extensionManagementService, @@ -71,4 +64,4 @@ export class ExtensionManagementServerService implements IExtensionManagementSer } } -registerSingleton(IExtensionManagementServerService, ExtensionManagementServerService, true); +registerSingleton(IExtensionManagementServerService, ExtensionManagementServerService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts index e4955cb1fa3..a8d11483188 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts @@ -84,8 +84,8 @@ export class ExtensionManagementService extends Disposable implements IWorkbench this.onDidChangeProfile = this._register(this.servers.reduce((emitter: EventMultiplexer, server) => { emitter.add(Event.map(server.extensionManagementService.onDidChangeProfile, e => ({ ...e, server }))); return emitter; }, new EventMultiplexer())).event; } - async getInstalled(type?: ExtensionType): Promise { - const result = await Promise.all(this.servers.map(({ extensionManagementService }) => extensionManagementService.getInstalled(type))); + async getInstalled(type?: ExtensionType, profileLocation?: URI): Promise { + const result = await Promise.all(this.servers.map(({ extensionManagementService }) => extensionManagementService.getInstalled(type, profileLocation))); return flatten(result); } @@ -96,26 +96,26 @@ export class ExtensionManagementService extends Disposable implements IWorkbench } if (this.servers.length > 1) { if (isLanguagePackExtension(extension.manifest)) { - return this.uninstallEverywhere(extension); + return this.uninstallEverywhere(extension, options); } return this.uninstallInServer(extension, server, options); } - return server.extensionManagementService.uninstall(extension); + return server.extensionManagementService.uninstall(extension, options); } - private async uninstallEverywhere(extension: ILocalExtension): Promise { + private async uninstallEverywhere(extension: ILocalExtension, options?: UninstallOptions): Promise { const server = this.getServer(extension); if (!server) { return Promise.reject(`Invalid location ${extension.location.toString()}`); } - const promise = server.extensionManagementService.uninstall(extension); + const promise = server.extensionManagementService.uninstall(extension, options); const otherServers: IExtensionManagementServer[] = this.servers.filter(s => s !== server); if (otherServers.length) { for (const otherServer of otherServers) { const installed = await otherServer.extensionManagementService.getInstalled(); extension = installed.filter(i => !i.isBuiltin && areSameExtensions(i.identifier, extension.identifier))[0]; if (extension) { - await otherServer.extensionManagementService.uninstall(extension); + await otherServer.extensionManagementService.uninstall(extension, options); } } } diff --git a/src/vs/workbench/services/extensionManagement/common/remoteExtensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/remoteExtensionManagementService.ts new file mode 100644 index 00000000000..0eb60966707 --- /dev/null +++ b/src/vs/workbench/services/extensionManagement/common/remoteExtensionManagementService.ts @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { Event } from 'vs/base/common/event'; +import { ILocalExtension, IGalleryExtension, InstallOptions, InstallVSIXOptions, UninstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { URI } from 'vs/base/common/uri'; +import { ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { IProfileAwareExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; +import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; + +export class RemoteExtensionManagementService extends ExtensionManagementChannelClient implements IProfileAwareExtensionManagementService { + + readonly onDidChangeProfile = Event.None; + get onProfileAwareInstallExtension() { return super.onInstallExtension; } + get onProfileAwareDidInstallExtensions() { return super.onDidInstallExtensions; } + get onProfileAwareUninstallExtension() { return super.onUninstallExtension; } + get onProfileAwareDidUninstallExtension() { return super.onDidUninstallExtension; } + + constructor( + channel: IChannel, + @IUserDataProfilesService private readonly userDataProfileService: IUserDataProfilesService, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, + ) { + super(channel); + } + + override getInstalled(type: ExtensionType | null = null, profileLocation?: URI): Promise { + this.validateProfileLocation({ profileLocation }); + return super.getInstalled(type); + } + + override uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise { + options = this.validateProfileLocation(options); + return super.uninstall(extension, options); + } + + override async install(vsix: URI, options?: InstallVSIXOptions): Promise { + options = this.validateProfileLocation(options); + return super.install(vsix, options); + } + + override async installFromGallery(extension: IGalleryExtension, options?: InstallOptions): Promise { + options = this.validateProfileLocation(options); + return super.installFromGallery(extension, options); + } + + private validateProfileLocation(options?: T): T | undefined { + if (options?.profileLocation) { + if (!this.uriIdentityService.extUri.isEqual(options?.profileLocation, this.userDataProfileService.defaultProfile.extensionsResource)) { + throw new Error('This opertaion is not supported in remote scenario'); + } + options = { ...options, profileLocation: undefined }; + } + return options; + } + +} diff --git a/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts index a19ff1b42f9..47c2436709c 100644 --- a/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts @@ -6,7 +6,7 @@ import { ExtensionIdentifier, ExtensionType, IExtension, IExtensionIdentifier, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions'; import { ILocalExtension, IGalleryExtension, IGalleryMetadata, InstallOperation, IExtensionGalleryService, Metadata, InstallOptions, UninstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; import { URI } from 'vs/base/common/uri'; -import { Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { areSameExtensions, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IProfileAwareExtensionManagementService, IScannedExtension, IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ILogService } from 'vs/platform/log/common/log'; @@ -20,15 +20,31 @@ import { DidChangeUserDataProfileEvent, IUserDataProfileService } from 'vs/workb import { delta } from 'vs/base/common/arrays'; import { compare } from 'vs/base/common/strings'; import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { DisposableStore } from 'vs/base/common/lifecycle'; export class WebExtensionManagementService extends AbstractExtensionManagementService implements IProfileAwareExtensionManagementService { declare readonly _serviceBrand: undefined; + private readonly disposables = this._register(new DisposableStore()); + get onProfileAwareInstallExtension() { return super.onInstallExtension; } + override get onInstallExtension() { return Event.filter(this.onProfileAwareInstallExtension, e => this.filterEvent(e), this.disposables); } + get onProfileAwareDidInstallExtensions() { return super.onDidInstallExtensions; } + override get onDidInstallExtensions() { + return Event.filter( + Event.map(this.onProfileAwareDidInstallExtensions, results => results.filter(e => this.filterEvent(e)), this.disposables), + results => results.length > 0, this.disposables); + } + get onProfileAwareUninstallExtension() { return super.onUninstallExtension; } + override get onUninstallExtension() { return Event.filter(this.onProfileAwareUninstallExtension, e => this.filterEvent(e), this.disposables); } + get onProfileAwareDidUninstallExtension() { return super.onDidUninstallExtension; } + override get onDidUninstallExtension() { return Event.filter(this.onProfileAwareDidUninstallExtension, e => this.filterEvent(e), this.disposables); } + private readonly _onDidChangeProfile = this._register(new Emitter<{ readonly added: ILocalExtension[]; readonly removed: ILocalExtension[] }>()); readonly onDidChangeProfile = this._onDidChangeProfile.event; @@ -42,11 +58,17 @@ export class WebExtensionManagementService extends AbstractExtensionManagementSe @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, @IProductService productService: IProductService, @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, ) { super(extensionGalleryService, telemetryService, logService, productService, userDataProfilesService); this._register(userDataProfileService.onDidChangeCurrentProfile(e => e.join(this.whenProfileChanged(e)))); } + private filterEvent({ profileLocation, applicationScoped }: { profileLocation?: URI; applicationScoped?: boolean }): boolean { + profileLocation = profileLocation ?? this.userDataProfileService.currentProfile.extensionsResource; + return applicationScoped || this.uriIdentityService.extUri.isEqual(this.userDataProfileService.currentProfile.extensionsResource, profileLocation); + } + async getTargetPlatform(): Promise { return TargetPlatform.WEB; } @@ -61,17 +83,17 @@ export class WebExtensionManagementService extends AbstractExtensionManagementSe return false; } - async getInstalled(type?: ExtensionType): Promise { + async getInstalled(type?: ExtensionType, profileLocation?: URI): Promise { const extensions = []; if (type === undefined || type === ExtensionType.System) { const systemExtensions = await this.webExtensionsScannerService.scanSystemExtensions(); extensions.push(...systemExtensions); } if (type === undefined || type === ExtensionType.User) { - const userExtensions = await this.webExtensionsScannerService.scanUserExtensions(this.userDataProfileService.currentProfile.extensionsResource); + const userExtensions = await this.webExtensionsScannerService.scanUserExtensions(profileLocation ?? this.userDataProfileService.currentProfile.extensionsResource); extensions.push(...userExtensions); } - return Promise.all(extensions.map(e => toLocalExtension(e))); + return extensions.map(e => toLocalExtension(e)); } async install(location: URI, options: InstallOptions = {}): Promise { diff --git a/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService.ts b/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService.ts index 9c920ab4057..d539186ddb6 100644 --- a/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService.ts @@ -9,7 +9,7 @@ import { ExtensionInstallLocation, IExtensionManagementServer, IExtensionManagem import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { NativeRemoteExtensionManagementService } from 'vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService'; import { ILabelService } from 'vs/platform/label/common/label'; import { IExtension } from 'vs/platform/extensions/common/extensions'; @@ -66,4 +66,4 @@ export class ExtensionManagementServerService extends Disposable implements IExt } } -registerSingleton(IExtensionManagementServerService, ExtensionManagementServerService, true); +registerSingleton(IExtensionManagementServerService, ExtensionManagementServerService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementService.ts index 54ad833ec8b..21033abfbe8 100644 --- a/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementService.ts @@ -7,7 +7,7 @@ import { generateUuid } from 'vs/base/common/uuid'; import { ILocalExtension, IExtensionGalleryService, InstallVSIXOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; import { URI } from 'vs/base/common/uri'; import { ExtensionManagementService as BaseExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagementService'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IExtensionManagementServer, IExtensionManagementServerService, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { Schemas } from 'vs/base/common/network'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -53,4 +53,4 @@ export class ExtensionManagementService extends BaseExtensionManagementService { } } -registerSingleton(IWorkbenchExtensionManagementService, ExtensionManagementService, true); +registerSingleton(IWorkbenchExtensionManagementService, ExtensionManagementService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionTipsService.ts b/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionTipsService.ts index e99f8822f0a..fa12c6b9d13 100644 --- a/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionTipsService.ts +++ b/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionTipsService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IExtensionTipsService, IExecutableBasedExtensionTip, IWorkspaceTips, IConfigBasedExtensionTip } from 'vs/platform/extensionManagement/common/extensionManagement'; @@ -53,4 +53,4 @@ class NativeExtensionTipsService extends ExtensionTipsService implements IExtens } -registerSingleton(IExtensionTipsService, NativeExtensionTipsService, true); +registerSingleton(IExtensionTipsService, NativeExtensionTipsService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts b/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts index c2e3673df9c..6320906637a 100644 --- a/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts @@ -5,7 +5,7 @@ import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event } from 'vs/base/common/event'; -import { ILocalExtension, IGalleryExtension, IExtensionGalleryService, InstallOperation, InstallOptions, InstallVSIXOptions, ExtensionManagementError, ExtensionManagementErrorCode } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { ILocalExtension, IGalleryExtension, IExtensionGalleryService, InstallOperation, InstallOptions, InstallVSIXOptions, ExtensionManagementError, ExtensionManagementErrorCode, UninstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; import { URI } from 'vs/base/common/uri'; import { ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; @@ -47,13 +47,33 @@ export class NativeRemoteExtensionManagementService extends ExtensionManagementC super(channel); } + override getInstalled(type: ExtensionType | null = null, profileLocation?: URI): Promise { + if (profileLocation) { + throw new Error('Installing extensions to a specific profile is not supported in remote scenario'); + } + return super.getInstalled(type); + } + + override uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise { + if (options?.profileLocation) { + throw new Error('Installing extensions to a specific profile is not supported in remote scenario'); + } + return super.uninstall(extension, options); + } + override async install(vsix: URI, options?: InstallVSIXOptions): Promise { + if (options?.profileLocation) { + throw new Error('Installing extensions to a specific profile is not supported in remote scenario'); + } const local = await super.install(vsix, options); await this.installUIDependenciesAndPackedExtensions(local); return local; } override async installFromGallery(extension: IGalleryExtension, installOptions?: InstallOptions): Promise { + if (installOptions?.profileLocation) { + throw new Error('Installing extensions to a specific profile is not supported in remote scenario'); + } const local = await this.doInstallFromGallery(extension, installOptions); await this.installUIDependenciesAndPackedExtensions(local); return local; diff --git a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts index 8e8c6678cfc..3bbb6fe1ebe 100644 --- a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts +++ b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts @@ -402,6 +402,32 @@ suite('ExtensionEnablementService Test', () => { assert.strictEqual(testObject.getEnablementState(dep), EnablementState.EnabledGlobally); }); + test('test enable an extension in workspace with a dependency extension that has auth providers', async () => { + installed.push(...[aLocalExtension2('pub.a', { extensionDependencies: ['pub.b'] }), aLocalExtension('pub.b', { authentication: [{ id: 'a', label: 'a' }] })]); + const target = installed[0]; + await (testObject).waitUntilInitialized(); + await testObject.setEnablement([target], EnablementState.DisabledWorkspace); + await testObject.setEnablement([target], EnablementState.EnabledWorkspace); + assert.ok(testObject.isEnabled(target)); + assert.strictEqual(testObject.getEnablementState(target), EnablementState.EnabledWorkspace); + }); + + test('test enable an extension with a dependency extension that cannot be enabled', async () => { + instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(anExtensionManagementServer('vscode-local', instantiationService), anExtensionManagementServer('vscode-remote', instantiationService), null)); + const localWorkspaceDepExtension = aLocalExtension2('pub.b', { extensionKind: ['workspace'] }, { location: URI.file(`pub.b`) }); + const remoteWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['workspace'], extensionDependencies: ['pub.b'] }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); + const remoteWorkspaceDepExtension = aLocalExtension2('pub.b', { extensionKind: ['workspace'] }, { location: URI.file(`pub.b`).with({ scheme: Schemas.vscodeRemote }) }); + installed.push(localWorkspaceDepExtension, remoteWorkspaceExtension, remoteWorkspaceDepExtension); + + testObject = new TestExtensionEnablementService(instantiationService); + await (testObject).waitUntilInitialized(); + + await testObject.setEnablement([remoteWorkspaceExtension], EnablementState.DisabledGlobally); + await testObject.setEnablement([remoteWorkspaceExtension], EnablementState.EnabledGlobally); + assert.ok(testObject.isEnabled(remoteWorkspaceExtension)); + assert.strictEqual(testObject.getEnablementState(remoteWorkspaceExtension), EnablementState.EnabledGlobally); + }); + test('test enable an extension also enables packed extensions', async () => { installed.push(...[aLocalExtension2('pub.a', { extensionPack: ['pub.b'] }), aLocalExtension('pub.b')]); const target = installed[0]; diff --git a/src/vs/workbench/services/extensionRecommendations/common/extensionIgnoredRecommendationsService.ts b/src/vs/workbench/services/extensionRecommendations/common/extensionIgnoredRecommendationsService.ts index bed9c346aee..1c4b365f2d2 100644 --- a/src/vs/workbench/services/extensionRecommendations/common/extensionIgnoredRecommendationsService.ts +++ b/src/vs/workbench/services/extensionRecommendations/common/extensionIgnoredRecommendationsService.ts @@ -6,7 +6,7 @@ import { distinct } from 'vs/base/common/arrays'; import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IStorageService, IStorageValueChangeEvent, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IExtensionIgnoredRecommendationsService, IgnoredRecommendationChangeNotification } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IWorkspaceExtensionsConfigService } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; @@ -108,4 +108,4 @@ export class ExtensionIgnoredRecommendationsService extends Disposable implement } -registerSingleton(IExtensionIgnoredRecommendationsService, ExtensionIgnoredRecommendationsService, true); +registerSingleton(IExtensionIgnoredRecommendationsService, ExtensionIgnoredRecommendationsService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts b/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts index e2495875344..4fe17405e22 100644 --- a/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts +++ b/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts @@ -9,7 +9,7 @@ import { parse } from 'vs/base/common/json'; import { Disposable } from 'vs/base/common/lifecycle'; import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; import { FileKind, IFileService } from 'vs/platform/files/common/files'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { isWorkspace, IWorkspace, IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; @@ -266,4 +266,4 @@ export class WorkspaceExtensionsConfigService extends Disposable implements IWor } -registerSingleton(IWorkspaceExtensionsConfigService, WorkspaceExtensionsConfigService, true); +registerSingleton(IWorkspaceExtensionsConfigService, WorkspaceExtensionsConfigService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts b/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts index e147dac6cc2..2ecae8e653c 100644 --- a/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts +++ b/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts @@ -18,7 +18,7 @@ import { IURLHandler, IURLService, IOpenURLOptions } from 'vs/platform/url/commo import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IExtensionService, toExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContribution, Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; @@ -382,7 +382,7 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { } } -registerSingleton(IExtensionUrlHandler, ExtensionUrlHandler, false); +registerSingleton(IExtensionUrlHandler, ExtensionUrlHandler, InstantiationType.Eager); /** * This class handles URLs before `ExtensionUrlHandler` is instantiated. @@ -416,7 +416,7 @@ class ExtensionUrlBootstrapHandler implements IWorkbenchContribution, IURLHandle } const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(ExtensionUrlBootstrapHandler, 'ExtensionUrlBootstrapHandler', LifecyclePhase.Ready); +workbenchRegistry.registerWorkbenchContribution(ExtensionUrlBootstrapHandler, LifecyclePhase.Ready); class ManageAuthorizedExtensionURIsAction extends Action2 { diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts index 2c35ebcfaa6..b78a21d763e 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts @@ -16,7 +16,7 @@ import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensio import * as platform from 'vs/base/common/platform'; import * as dom from 'vs/base/browser/dom'; import { URI } from 'vs/base/common/uri'; -import { IExtensionHost, ExtensionHostLogFileName, LocalWebWorkerRunningLocation, ExtensionHostExtensions } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionHost, ExtensionHostLogFileName, LocalWebWorkerRunningLocation, ExtensionHostExtensions, webWorkerExtHostLog } from 'vs/workbench/services/extensions/common/extensions'; import { IProductService } from 'vs/platform/product/common/productService'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { joinPath } from 'vs/base/common/resources'; @@ -251,7 +251,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost } // Register log channel for web worker exthost log - Registry.as(Extensions.OutputChannels).registerChannel({ id: 'webWorkerExtHostLog', label: localize('name', "Worker Extension Host"), file: this._extensionHostLogFile, log: true }); + Registry.as(Extensions.OutputChannels).registerChannel({ id: webWorkerExtHostLog, label: localize('name', "Worker Extension Host"), file: this._extensionHostLogFile, log: true }); return protocol; } @@ -293,10 +293,12 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost appHost: this._productService.embedderIdentifier ?? (platform.isWeb ? 'web' : 'desktop'), appUriScheme: this._productService.urlProtocol, appLanguage: platform.language, + extensionTelemetryLogResource: this._environmentService.extHostTelemetryLogFile, extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, globalStorageHome: this._userDataProfilesService.defaultProfile.globalStorageHome, workspaceStorageHome: this._environmentService.workspaceStorageHome, + extensionLogLevel: this._environmentService.extensionLogLevel }, workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? undefined : { configuration: workspace.configuration || undefined, diff --git a/src/vs/workbench/services/extensions/common/extensionHostManager.ts b/src/vs/workbench/services/extensions/common/extensionHostManager.ts index 2aceff7689b..41e7ef11f1d 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostManager.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostManager.ts @@ -20,7 +20,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { StopWatch } from 'vs/base/common/stopwatch'; import { VSBuffer } from 'vs/base/common/buffer'; import { IExtensionHost, ExtensionHostKind, ActivationKind, extensionHostKindToString, ExtensionActivationReason, IInternalExtensionService, ExtensionRunningLocation, ExtensionHostExtensions } from 'vs/workbench/services/extensions/common/extensions'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { Barrier } from 'vs/base/common/async'; import { URI } from 'vs/base/common/uri'; import { ILogService } from 'vs/platform/log/common/log'; @@ -708,7 +708,7 @@ registerAction2(class MeasureExtHostLatencyAction extends Action2 { value: nls.localize('measureExtHostLatency', "Measure Extension Host Latency"), original: 'Measure Extension Host Latency' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true }); } diff --git a/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts b/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts index 0f5865d1100..eb9db688f2f 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts @@ -46,6 +46,7 @@ export interface IEnvironment { appHost: string; appRoot?: URI; appLanguage: string; + extensionTelemetryLogResource: URI; appUriScheme: string; extensionDevelopmentLocationURI?: URI[]; extensionTestsLocationURI?: URI; @@ -53,6 +54,7 @@ export interface IEnvironment { workspaceStorageHome: URI; useHostProxy?: boolean; skipWorkspaceStorageLock?: boolean; + extensionLogLevel?: [string, string][]; } export interface IStaticWorkspaceData { diff --git a/src/vs/workbench/services/extensions/common/extensionManifestPropertiesService.ts b/src/vs/workbench/services/extensions/common/extensionManifestPropertiesService.ts index efaeb062e84..63c348cc399 100644 --- a/src/vs/workbench/services/extensions/common/extensionManifestPropertiesService.ts +++ b/src/vs/workbench/services/extensions/common/extensionManifestPropertiesService.ts @@ -11,7 +11,7 @@ import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/ex import { isNonEmptyArray } from 'vs/base/common/arrays'; import { IProductService } from 'vs/platform/product/common/productService'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ExtensionUntrustedWorkspaceSupport } from 'vs/base/common/product'; import { Disposable } from 'vs/base/common/lifecycle'; import { WORKSPACE_TRUST_EXTENSION_SUPPORT } from 'vs/workbench/services/workspaces/common/workspaceTrust'; @@ -371,4 +371,4 @@ export class ExtensionManifestPropertiesService extends Disposable implements IE } } -registerSingleton(IExtensionManifestPropertiesService, ExtensionManifestPropertiesService, true); +registerSingleton(IExtensionManifestPropertiesService, ExtensionManifestPropertiesService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index 269ad124f08..2bcb79c3e67 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -404,6 +404,9 @@ export class ExtensionPointContribution { } export const ExtensionHostLogFileName = 'exthost'; +export const localExtHostLog = 'extHostLog'; +export const remoteExtHostLog = 'remoteExtHostLog'; +export const webWorkerExtHostLog = 'webWorkerExtHostLog'; export interface IWillActivateEvent { readonly event: string; diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 280b0f3fef6..f461c58e37e 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -14,6 +14,7 @@ export const allApiProposals = Object.freeze({ contribLabelFormatterWorkspaceTooltip: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribLabelFormatterWorkspaceTooltip.d.ts', contribMenuBarHome: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribMenuBarHome.d.ts', contribMergeEditorMenus: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribMergeEditorMenus.d.ts', + contribNotebookStaticPreloads: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribNotebookStaticPreloads.d.ts', contribRemoteHelp: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribRemoteHelp.d.ts', contribShareMenu: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribShareMenu.d.ts', contribViewsRemote: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribViewsRemote.d.ts', @@ -38,12 +39,10 @@ export const allApiProposals = Object.freeze({ inlineCompletionsNew: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.inlineCompletionsNew.d.ts', interactiveWindow: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.interactiveWindow.d.ts', ipc: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.ipc.d.ts', - localization: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.localization.d.ts', notebookCellExecutionState: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookCellExecutionState.d.ts', notebookContentProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookContentProvider.d.ts', notebookControllerAffinityHidden: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookControllerAffinityHidden.d.ts', notebookControllerKind: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookControllerKind.d.ts', - notebookDebugOptions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookDebugOptions.d.ts', notebookDeprecated: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookDeprecated.d.ts', notebookEditor: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookEditor.d.ts', notebookKernelSource: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookKernelSource.d.ts', @@ -59,7 +58,7 @@ export const allApiProposals = Object.freeze({ tabInputTextMerge: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.tabInputTextMerge.d.ts', taskPresentationGroup: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.taskPresentationGroup.d.ts', telemetry: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.telemetry.d.ts', - telemetryLog: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.telemetryLog.d.ts', + telemetryLogger: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.telemetryLogger.d.ts', terminalDataWriteEvent: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalDataWriteEvent.d.ts', terminalDimensions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalDimensions.d.ts', testCoverage: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.testCoverage.d.ts', @@ -70,7 +69,6 @@ export const allApiProposals = Object.freeze({ treeItemCheckbox: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.treeItemCheckbox.d.ts', treeViewReveal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.treeViewReveal.d.ts', tunnels: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.tunnels.d.ts', - workspaceEditIsRefactoring: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.workspaceEditIsRefactoring.d.ts', workspaceTrust: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.workspaceTrust.d.ts' }); export type ApiProposalName = keyof typeof allApiProposals; diff --git a/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts b/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts index 62eea683c18..1cf2e6448eb 100644 --- a/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts +++ b/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts @@ -27,7 +27,7 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions'; import { createMessageOfType, isMessageOfType, MessageType, IExtensionHostInitData, UIKind } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; -import { ExtensionHostExtensions, ExtensionHostLogFileName, IExtensionHost, RemoteRunningLocation } from 'vs/workbench/services/extensions/common/extensions'; +import { ExtensionHostExtensions, ExtensionHostLogFileName, IExtensionHost, remoteExtHostLog, RemoteRunningLocation } from 'vs/workbench/services/extensions/common/extensions'; import { Extensions, IOutputChannelRegistry } from 'vs/workbench/services/output/common/output'; export interface IRemoteExtensionHostInitData { @@ -171,7 +171,7 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost { disposable.dispose(); // Register log channel for remote exthost log - Registry.as(Extensions.OutputChannels).registerChannel({ id: 'remoteExtHostLog', label: localize('remote extension host Log', "Remote Extension Host"), file: logFile, log: true }); + Registry.as(Extensions.OutputChannels).registerChannel({ id: remoteExtHostLog, label: localize('remote extension host Log', "Remote Extension Host"), file: logFile, log: true }); // release this promise this._protocol = protocol; @@ -221,11 +221,13 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost { appName: this._productService.nameLong, appHost: this._productService.embedderIdentifier || 'desktop', appUriScheme: this._productService.urlProtocol, + extensionTelemetryLogResource: this._environmentService.extHostTelemetryLogFile, appLanguage: platform.language, extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, globalStorageHome: remoteInitData.globalStorageHome, - workspaceStorageHome: remoteInitData.workspaceStorageHome + workspaceStorageHome: remoteInitData.workspaceStorageHome, + extensionLogLevel: this._environmentService.extensionLogLevel }, workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : { configuration: workspace.configuration, diff --git a/src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts b/src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts index 4077c1661e4..71edeaee506 100644 --- a/src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts @@ -34,7 +34,7 @@ import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEn import { IWebWorkerExtensionHostDataProvider, IWebWorkerExtensionHostInitData, WebWorkerExtensionHost } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHost'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { ILogService } from 'vs/platform/log/common/log'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { Schemas } from 'vs/base/common/network'; import { ExtensionHostExitCode } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { updateProxyConfigurationsScope } from 'vs/platform/request/common/request'; @@ -704,7 +704,7 @@ class RestartExtensionHostAction extends Action2 { super({ id: 'workbench.action.restartExtensionHost', title: { value: nls.localize('restartExtensionHost', "Restart Extension Host"), original: 'Restart Extension Host' }, - category: CATEGORIES.Developer, + category: Categories.Developer, f1: true }); } diff --git a/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts index 8d240be7670..08247f4396b 100644 --- a/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts @@ -28,7 +28,7 @@ import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensio import { parseExtensionDevOptions } from '../common/extensionDevOptions'; import { VSBuffer } from 'vs/base/common/buffer'; import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug'; -import { IExtensionHost, ExtensionHostLogFileName, LocalProcessRunningLocation, ExtensionHostExtensions } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionHost, ExtensionHostLogFileName, LocalProcessRunningLocation, ExtensionHostExtensions, localExtHostLog } from 'vs/workbench/services/extensions/common/extensions'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { joinPath } from 'vs/base/common/resources'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -417,7 +417,7 @@ export class SandboxLocalProcessExtensionHost implements IExtensionHost { disposable.dispose(); // Register log channel for exthost log - Registry.as(Extensions.OutputChannels).registerChannel({ id: 'extHostLog', label: nls.localize('extension host Log', "Extension Host"), file: this._extensionHostLogFile, log: true }); + Registry.as(Extensions.OutputChannels).registerChannel({ id: localExtHostLog, label: nls.localize('extension host Log', "Extension Host"), file: this._extensionHostLogFile, log: true }); // release this promise resolve(); @@ -444,11 +444,13 @@ export class SandboxLocalProcessExtensionHost implements IExtensionHost { appName: this._productService.nameLong, appHost: this._productService.embedderIdentifier || 'desktop', appUriScheme: this._productService.urlProtocol, + extensionTelemetryLogResource: this._environmentService.extHostTelemetryLogFile, appLanguage: platform.language, extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, globalStorageHome: this._userDataProfilesService.defaultProfile.globalStorageHome, workspaceStorageHome: this._environmentService.workspaceStorageHome, + extensionLogLevel: this._environmentService.extensionLogLevel }, workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? undefined : { configuration: withNullAsUndefined(workspace.configuration), diff --git a/src/vs/workbench/services/files/browser/elevatedFileService.ts b/src/vs/workbench/services/files/browser/elevatedFileService.ts index 7c2eef326fc..3e950a0f417 100644 --- a/src/vs/workbench/services/files/browser/elevatedFileService.ts +++ b/src/vs/workbench/services/files/browser/elevatedFileService.ts @@ -6,7 +6,7 @@ import { VSBuffer, VSBufferReadable, VSBufferReadableStream } from 'vs/base/common/buffer'; import { URI } from 'vs/base/common/uri'; import { IFileStatWithMetadata, IWriteFileOptions } from 'vs/platform/files/common/files'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IElevatedFileService } from 'vs/workbench/services/files/common/elevatedFileService'; export class BrowserElevatedFileService implements IElevatedFileService { @@ -25,4 +25,4 @@ export class BrowserElevatedFileService implements IElevatedFileService { } } -registerSingleton(IElevatedFileService, BrowserElevatedFileService, true); +registerSingleton(IElevatedFileService, BrowserElevatedFileService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/files/electron-sandbox/elevatedFileService.ts b/src/vs/workbench/services/files/electron-sandbox/elevatedFileService.ts index 3f2ff21e4aa..189612f7115 100644 --- a/src/vs/workbench/services/files/electron-sandbox/elevatedFileService.ts +++ b/src/vs/workbench/services/files/electron-sandbox/elevatedFileService.ts @@ -8,7 +8,7 @@ import { randomPath } from 'vs/base/common/extpath'; import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { IFileService, IFileStatWithMetadata, IWriteFileOptions } from 'vs/platform/files/common/files'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { IElevatedFileService } from 'vs/workbench/services/files/common/elevatedFileService'; @@ -49,4 +49,4 @@ export class NativeElevatedFileService implements IElevatedFileService { } } -registerSingleton(IElevatedFileService, NativeElevatedFileService, true); +registerSingleton(IElevatedFileService, NativeElevatedFileService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/host/browser/browserHostService.ts b/src/vs/workbench/services/host/browser/browserHostService.ts index 63ddc762602..f17e7a256a0 100644 --- a/src/vs/workbench/services/host/browser/browserHostService.ts +++ b/src/vs/workbench/services/host/browser/browserHostService.ts @@ -5,7 +5,7 @@ import { Event } from 'vs/base/common/event'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -531,4 +531,4 @@ export class BrowserHostService extends Disposable implements IHostService { //#endregion } -registerSingleton(IHostService, BrowserHostService, true); +registerSingleton(IHostService, BrowserHostService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts b/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts index 6f3f1ab1a80..f9181a1a376 100644 --- a/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts +++ b/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts @@ -6,7 +6,7 @@ import { Event } from 'vs/base/common/event'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILabelService } from 'vs/platform/label/common/label'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IWindowOpenable, IOpenWindowOptions, isFolderToOpen, isWorkspaceToOpen, IOpenEmptyWindowOptions } from 'vs/platform/window/common/window'; @@ -138,5 +138,5 @@ class WorkbenchHostService extends Disposable implements IHostService { //#endregion } -registerSingleton(IHostService, WorkbenchHostService, true); -registerSingleton(INativeHostService, WorkbenchNativeHostService, true); +registerSingleton(IHostService, WorkbenchHostService, InstantiationType.Delayed); +registerSingleton(INativeHostService, WorkbenchNativeHostService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/hover/browser/hoverService.ts b/src/vs/workbench/services/hover/browser/hoverService.ts index 69f7457daa6..3a33a5ed48e 100644 --- a/src/vs/workbench/services/hover/browser/hoverService.ts +++ b/src/vs/workbench/services/hover/browser/hoverService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/hover'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { editorHoverBackground, editorHoverBorder, textLinkForeground, editorHoverForeground, editorHoverStatusBarBackground, textCodeBlockBackground, widgetShadow, textLinkActiveForeground, focusBorder, toolbarHoverBackground } from 'vs/platform/theme/common/colorRegistry'; import { IHoverService, IHoverOptions, IHoverWidget } from 'vs/workbench/services/hover/browser/hover'; @@ -156,7 +156,7 @@ class HoverContextViewDelegate implements IDelegate { } } -registerSingleton(IHoverService, HoverService, true); +registerSingleton(IHoverService, HoverService, InstantiationType.Delayed); registerThemingParticipant((theme, collector) => { const hoverBackground = theme.getColor(editorHoverBackground); diff --git a/src/vs/workbench/services/integrity/browser/integrityService.ts b/src/vs/workbench/services/integrity/browser/integrityService.ts index f29a3dfe552..e2285f53c04 100644 --- a/src/vs/workbench/services/integrity/browser/integrityService.ts +++ b/src/vs/workbench/services/integrity/browser/integrityService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IIntegrityService, IntegrityTestResult } from 'vs/workbench/services/integrity/common/integrity'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class IntegrityService implements IIntegrityService { @@ -15,4 +15,4 @@ export class IntegrityService implements IIntegrityService { } } -registerSingleton(IIntegrityService, IntegrityService, true); +registerSingleton(IIntegrityService, IntegrityService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/integrity/electron-sandbox/integrityService.ts b/src/vs/workbench/services/integrity/electron-sandbox/integrityService.ts index 7125f82440e..ad51e6ef946 100644 --- a/src/vs/workbench/services/integrity/electron-sandbox/integrityService.ts +++ b/src/vs/workbench/services/integrity/electron-sandbox/integrityService.ts @@ -11,7 +11,7 @@ import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecyc import { IProductService } from 'vs/platform/product/common/productService'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { FileAccess } from 'vs/base/common/network'; import { IChecksumService } from 'vs/platform/checksum/common/checksumService'; @@ -163,4 +163,4 @@ export class IntegrityService implements IIntegrityService { } } -registerSingleton(IIntegrityService, IntegrityService, true); +registerSingleton(IIntegrityService, IntegrityService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayoutService.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayoutService.ts index 0e630dc4ce8..c83b9e0a72c 100644 --- a/src/vs/workbench/services/keybinding/browser/keyboardLayoutService.ts +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayoutService.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { KeymapInfo, IRawMixedKeyboardMapping, IKeymapInfo } from 'vs/workbench/services/keybinding/common/keymapInfo'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { DispatchConfig } from 'vs/platform/keyboardLayout/common/dispatchConfig'; import { IKeyboardMapper, CachedKeyboardMapper } from 'vs/platform/keyboardLayout/common/keyboardMapper'; import { OS, OperatingSystem, isMacintosh, isWindows } from 'vs/base/common/platform'; @@ -618,7 +618,7 @@ export class BrowserKeyboardLayoutService extends Disposable implements IKeyboar } } -registerSingleton(IKeyboardLayoutService, BrowserKeyboardLayoutService, true); +registerSingleton(IKeyboardLayoutService, BrowserKeyboardLayoutService, InstantiationType.Delayed); // Configuration const configurationRegistry = Registry.as(ConfigExtensions.Configuration); diff --git a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts index a064d35bee6..4ddcceda908 100644 --- a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts +++ b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts @@ -22,7 +22,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; export const IKeybindingEditingService = createDecorator('keybindingEditingService'); @@ -293,4 +293,4 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding } } -registerSingleton(IKeybindingEditingService, KeybindingsEditingService, true); +registerSingleton(IKeybindingEditingService, KeybindingsEditingService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts index bf0c0c1f5b2..5e3e0114503 100644 --- a/src/vs/workbench/services/label/common/labelService.ts +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -18,7 +18,7 @@ import { ILabelService, ResourceLabelFormatter, ResourceLabelFormatting, IFormat import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { match } from 'vs/base/common/glob'; import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import { OperatingSystem, OS } from 'vs/base/common/platform'; @@ -103,7 +103,7 @@ class ResourceLabelFormattersHandler implements IWorkbenchContribution { }); } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ResourceLabelFormattersHandler, 'ResourceLabelFormattersHandler', LifecyclePhase.Restored); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ResourceLabelFormattersHandler, LifecyclePhase.Restored); const FORMATTER_CACHE_SIZE = 50; @@ -455,4 +455,4 @@ export class LabelService extends Disposable implements ILabelService { } } -registerSingleton(ILabelService, LabelService, true); +registerSingleton(ILabelService, LabelService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/languageStatus/common/languageStatusService.ts b/src/vs/workbench/services/languageStatus/common/languageStatusService.ts index 7f8bbc8ae19..a7a49dad0be 100644 --- a/src/vs/workbench/services/languageStatus/common/languageStatusService.ts +++ b/src/vs/workbench/services/languageStatus/common/languageStatusService.ts @@ -13,7 +13,7 @@ import { Command } from 'vs/editor/common/languages'; import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry'; import { LanguageSelector } from 'vs/editor/common/languageSelector'; import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export interface ILanguageStatus { @@ -73,4 +73,4 @@ class LanguageStatusServiceImpl implements ILanguageStatusService { } } -registerSingleton(ILanguageStatusService, LanguageStatusServiceImpl, true); +registerSingleton(ILanguageStatusService, LanguageStatusServiceImpl, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/model/common/modelService.ts b/src/vs/workbench/services/model/common/modelService.ts index e72f0272a98..7765c84da55 100644 --- a/src/vs/workbench/services/model/common/modelService.ts +++ b/src/vs/workbench/services/model/common/modelService.ts @@ -10,7 +10,7 @@ import { ModelService } from 'vs/editor/common/services/modelService'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfiguration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; @@ -42,4 +42,4 @@ export class WorkbenchModelService extends ModelService { } } -registerSingleton(IModelService, WorkbenchModelService, true); +registerSingleton(IModelService, WorkbenchModelService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/notification/common/notificationService.ts b/src/vs/workbench/services/notification/common/notificationService.ts index 1a768532b79..545f9104c05 100644 --- a/src/vs/workbench/services/notification/common/notificationService.ts +++ b/src/vs/workbench/services/notification/common/notificationService.ts @@ -8,7 +8,7 @@ import { INotificationService, INotification, INotificationHandle, Severity, Not import { NotificationsModel, ChoiceAction, NotificationChangeType } from 'vs/workbench/common/notifications'; import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IAction, Action } from 'vs/base/common/actions'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; @@ -271,4 +271,4 @@ export class NotificationService extends Disposable implements INotificationServ } } -registerSingleton(INotificationService, NotificationService, true); +registerSingleton(INotificationService, NotificationService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/outline/browser/outline.ts b/src/vs/workbench/services/outline/browser/outline.ts index 022a6004f26..be7a8ecb9f6 100644 --- a/src/vs/workbench/services/outline/browser/outline.ts +++ b/src/vs/workbench/services/outline/browser/outline.ts @@ -92,6 +92,7 @@ export interface IOutline { export const enum OutlineConfigKeys { 'icons' = 'outline.icons', + 'collapseItems' = 'outline.collapseItems', 'problemsEnabled' = 'outline.problems.enabled', 'problemsColors' = 'outline.problems.colors', 'problemsBadges' = 'outline.problems.badges' diff --git a/src/vs/workbench/services/outline/browser/outlineService.ts b/src/vs/workbench/services/outline/browser/outlineService.ts index fac1d91dd08..f4707432371 100644 --- a/src/vs/workbench/services/outline/browser/outlineService.ts +++ b/src/vs/workbench/services/outline/browser/outlineService.ts @@ -6,7 +6,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { LinkedList } from 'vs/base/common/linkedList'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IEditorPane } from 'vs/workbench/common/editor'; import { IOutline, IOutlineCreator, IOutlineService, OutlineTarget } from 'vs/workbench/services/outline/browser/outline'; import { Event, Emitter } from 'vs/base/common/event'; @@ -49,4 +49,4 @@ class OutlineService implements IOutlineService { } -registerSingleton(IOutlineService, OutlineService, true); +registerSingleton(IOutlineService, OutlineService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/output/common/output.ts b/src/vs/workbench/services/output/common/output.ts index 93698acaa1d..5adde39b333 100644 --- a/src/vs/workbench/services/output/common/output.ts +++ b/src/vs/workbench/services/output/common/output.ts @@ -257,3 +257,5 @@ async function whenFileExists(file: URI, trial: number, fileService: IFileServic await timeout(1000, token); await whenFileExists(file, trial + 1, fileService, logService, token); } + +export const ACTIVE_OUTPUT_CHANNEL_CONTEXT = new RawContextKey('activeOutputChannel', ''); diff --git a/src/vs/workbench/services/path/browser/pathService.ts b/src/vs/workbench/services/path/browser/pathService.ts index 215f1ec36ad..664884d60ba 100644 --- a/src/vs/workbench/services/path/browser/pathService.ts +++ b/src/vs/workbench/services/path/browser/pathService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IPathService, AbstractPathService } from 'vs/workbench/services/path/common/pathService'; import { URI } from 'vs/base/common/uri'; @@ -57,4 +57,4 @@ function guessLocalUserHome(environmentService: IWorkbenchEnvironmentService, co }); } -registerSingleton(IPathService, BrowserPathService, true); +registerSingleton(IPathService, BrowserPathService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/path/electron-sandbox/pathService.ts b/src/vs/workbench/services/path/electron-sandbox/pathService.ts index 07159a128c5..7888fc6ab71 100644 --- a/src/vs/workbench/services/path/electron-sandbox/pathService.ts +++ b/src/vs/workbench/services/path/electron-sandbox/pathService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { IPathService, AbstractPathService } from 'vs/workbench/services/path/common/pathService'; @@ -20,4 +20,4 @@ export class NativePathService extends AbstractPathService { } } -registerSingleton(IPathService, NativePathService, true); +registerSingleton(IPathService, NativePathService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/preferences/browser/keybindingsEditorInput.ts b/src/vs/workbench/services/preferences/browser/keybindingsEditorInput.ts index 0e2f929af32..8987b464b18 100644 --- a/src/vs/workbench/services/preferences/browser/keybindingsEditorInput.ts +++ b/src/vs/workbench/services/preferences/browser/keybindingsEditorInput.ts @@ -44,7 +44,7 @@ export class KeybindingsEditorInput extends EditorInput { } override matches(otherInput: EditorInput | IUntypedEditorInput): boolean { - return super.matches(otherInput) || otherInput instanceof KeybindingsEditorInput; + return otherInput instanceof KeybindingsEditorInput; } override dispose(): void { diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index 1759898bf46..473e718663b 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -20,7 +20,7 @@ import * as nls from 'vs/nls'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Extensions, getDefaultValue, IConfigurationRegistry, OVERRIDE_PROPERTY_REGEX } from 'vs/platform/configuration/common/configurationRegistry'; import { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -605,4 +605,4 @@ export class PreferencesService extends Disposable implements IPreferencesServic } } -registerSingleton(IPreferencesService, PreferencesService, true); +registerSingleton(IPreferencesService, PreferencesService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/preferences/test/browser/keybindingsEditorModel.test.ts b/src/vs/workbench/services/preferences/test/browser/keybindingsEditorModel.test.ts index 341f95fd936..aa3fcee7718 100644 --- a/src/vs/workbench/services/preferences/test/browser/keybindingsEditorModel.test.ts +++ b/src/vs/workbench/services/preferences/test/browser/keybindingsEditorModel.test.ts @@ -6,13 +6,9 @@ import * as assert from 'assert'; import * as uuid from 'vs/base/common/uuid'; import { OS, OperatingSystem } from 'vs/base/common/platform'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { Action } from 'vs/base/common/actions'; import { KeyCode } from 'vs/base/common/keyCodes'; import { SimpleKeybinding, ChordKeybinding } from 'vs/base/common/keybindings'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -22,6 +18,7 @@ import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayo import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { IKeybindingItemEntry } from 'vs/workbench/services/preferences/common/preferences'; +import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; interface Modifiers { metaKey?: boolean; @@ -30,12 +27,6 @@ interface Modifiers { shiftKey?: boolean; } -class AnAction extends Action { - constructor(id: string) { - super(id); - } -} - suite('KeybindingsEditorModel', () => { let instantiationService: TestInstantiationService; @@ -677,8 +668,16 @@ suite('KeybindingsEditorModel', () => { } function registerCommandWithTitle(command: string, title: string): void { - const registry = Registry.as(ActionExtensions.WorkbenchActions); - registry.registerWorkbenchAction(SyncActionDescriptor.create(AnAction, command, title, { primary: 0 }), ''); + registerAction2(class extends Action2 { + constructor() { + super({ + id: command, + title: { value: title, original: title }, + f1: true + }); + } + async run(): Promise { } + }); } function assertKeybindingItems(actual: ResolvedKeybindingItem[], expected: ResolvedKeybindingItem[]) { diff --git a/src/vs/workbench/services/progress/browser/progressService.ts b/src/vs/workbench/services/progress/browser/progressService.ts index 5d95e1984f7..c3a0416f735 100644 --- a/src/vs/workbench/services/progress/browser/progressService.ts +++ b/src/vs/workbench/services/progress/browser/progressService.ts @@ -14,7 +14,7 @@ import { ProgressBadge, IActivityService } from 'vs/workbench/services/activity/ import { INotificationService, Severity, INotificationHandle } from 'vs/platform/notification/common/notification'; import { Action } from 'vs/base/common/actions'; import { Event, Emitter } from 'vs/base/common/event'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { Dialog } from 'vs/base/browser/ui/dialog/dialog'; import { attachDialogStyler } from 'vs/platform/theme/common/styler'; @@ -622,4 +622,4 @@ export class ProgressService extends Disposable implements IProgressService { } } -registerSingleton(IProgressService, ProgressService, true); +registerSingleton(IProgressService, ProgressService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/quickinput/browser/quickInputService.ts b/src/vs/workbench/services/quickinput/browser/quickInputService.ts index 891e6b6fe1b..34572df63e0 100644 --- a/src/vs/workbench/services/quickinput/browser/quickInputService.ts +++ b/src/vs/workbench/services/quickinput/browser/quickInputService.ts @@ -12,7 +12,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { QuickInputController } from 'vs/base/parts/quickinput/browser/quickInput'; import { QuickInputService as BaseQuickInputService } from 'vs/platform/quickinput/browser/quickInput'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { InQuickPickContextKey } from 'vs/workbench/browser/quickaccess'; @@ -47,4 +47,4 @@ export class QuickInputService extends BaseQuickInputService { } } -registerSingleton(IQuickInputService, QuickInputService, true); +registerSingleton(IQuickInputService, QuickInputService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/remote/browser/remoteAgentService.ts b/src/vs/workbench/services/remote/browser/remoteAgentService.ts index 69a816ad6b9..dc897db8241 100644 --- a/src/vs/workbench/services/remote/browser/remoteAgentService.ts +++ b/src/vs/workbench/services/remote/browser/remoteAgentService.ts @@ -69,4 +69,4 @@ class RemoteConnectionFailureNotificationContribution implements IWorkbenchContr } const workbenchRegistry = Registry.as(Extensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(RemoteConnectionFailureNotificationContribution, 'RemoteConnectionFailureNotificationContribution', LifecyclePhase.Ready); +workbenchRegistry.registerWorkbenchContribution(RemoteConnectionFailureNotificationContribution, LifecyclePhase.Ready); diff --git a/src/vs/workbench/services/remote/common/remoteExplorerService.ts b/src/vs/workbench/services/remote/common/remoteExplorerService.ts index ff29e0375ca..234f9eb50c0 100644 --- a/src/vs/workbench/services/remote/common/remoteExplorerService.ts +++ b/src/vs/workbench/services/remote/common/remoteExplorerService.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { Event, Emitter } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ALL_INTERFACES_ADDRESSES, isAllInterfaces, isLocalhost, ITunnelService, LOCALHOST_ADDRESSES, PortAttributesProvider, ProvidedOnAutoForward, ProvidedPortAttributes, RemoteTunnel, TunnelPrivacyId, TunnelProtocol } from 'vs/platform/tunnel/common/tunnel'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; @@ -705,6 +705,7 @@ export class TunnelModel extends Disposable { description: nls.localize('tunnel.staticallyForwarded', "Statically Forwarded") } }); + this.tunnelService.setEnvironmentTunnel(tunnel.remoteAddress.host, tunnel.remoteAddress.port, localAddress, TunnelPrivacyId.ConstantPrivate, TunnelProtocol.Http); } } this._environmentTunnelsSet = true; @@ -1015,4 +1016,4 @@ class RemoteExplorerService implements IRemoteExplorerService { } } -registerSingleton(IRemoteExplorerService, RemoteExplorerService, true); +registerSingleton(IRemoteExplorerService, RemoteExplorerService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/remote/electron-sandbox/remoteAgentService.ts b/src/vs/workbench/services/remote/electron-sandbox/remoteAgentService.ts index 38556bbcb9a..997cfc7cda5 100644 --- a/src/vs/workbench/services/remote/electron-sandbox/remoteAgentService.ts +++ b/src/vs/workbench/services/remote/electron-sandbox/remoteAgentService.ts @@ -90,4 +90,4 @@ class RemoteConnectionFailureNotificationContribution implements IWorkbenchContr } const workbenchRegistry = Registry.as(Extensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(RemoteConnectionFailureNotificationContribution, 'RemoteConnectionFailureNotificationContribution', LifecyclePhase.Ready); +workbenchRegistry.registerWorkbenchContribution(RemoteConnectionFailureNotificationContribution, LifecyclePhase.Ready); diff --git a/src/vs/workbench/services/request/electron-sandbox/requestService.ts b/src/vs/workbench/services/request/electron-sandbox/requestService.ts index 4fb44ec4e00..2ff418a52e1 100644 --- a/src/vs/workbench/services/request/electron-sandbox/requestService.ts +++ b/src/vs/workbench/services/request/electron-sandbox/requestService.ts @@ -6,7 +6,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService } from 'vs/platform/log/common/log'; import { RequestService } from 'vs/platform/request/browser/requestService'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IRequestService } from 'vs/platform/request/common/request'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; @@ -25,4 +25,4 @@ export class NativeRequestService extends RequestService { } } -registerSingleton(IRequestService, NativeRequestService, true); +registerSingleton(IRequestService, NativeRequestService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/search/browser/searchService.ts b/src/vs/workbench/services/search/browser/searchService.ts index 21bb410c03e..bea85f71a43 100644 --- a/src/vs/workbench/services/search/browser/searchService.ts +++ b/src/vs/workbench/services/search/browser/searchService.ts @@ -17,7 +17,7 @@ import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity' import { IWorkerClient, logOnceWebWorkerWarning, SimpleWorkerClient } from 'vs/base/common/worker/simpleWorker'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { DefaultWorkerFactory } from 'vs/base/browser/defaultWorkerFactory'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILocalFileSearchSimpleWorker, ILocalFileSearchSimpleWorkerHost } from 'vs/workbench/services/search/common/localFileSearchWorkerTypes'; import { memoize } from 'vs/base/common/decorators'; import { HTMLFileSystemProvider } from 'vs/platform/files/browser/htmlFileSystemProvider'; @@ -196,4 +196,4 @@ export class LocalFileSearchWorkerClient extends Disposable implements ISearchRe } } -registerSingleton(ISearchService, RemoteSearchService, true); +registerSingleton(ISearchService, RemoteSearchService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/search/electron-sandbox/searchService.ts b/src/vs/workbench/services/search/electron-sandbox/searchService.ts index 1bb5a83da06..cf9c6c6fd91 100644 --- a/src/vs/workbench/services/search/electron-sandbox/searchService.ts +++ b/src/vs/workbench/services/search/electron-sandbox/searchService.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ISearchService } from 'vs/workbench/services/search/common/search'; import { SearchService } from 'vs/workbench/services/search/common/searchService'; -registerSingleton(ISearchService, SearchService, true); +registerSingleton(ISearchService, SearchService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/telemetry/browser/telemetryService.ts b/src/vs/workbench/services/telemetry/browser/telemetryService.ts index dbf075cf7d2..6d5739aed97 100644 --- a/src/vs/workbench/services/telemetry/browser/telemetryService.ts +++ b/src/vs/workbench/services/telemetry/browser/telemetryService.ts @@ -6,7 +6,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IObservableValue } from 'vs/base/common/observableValue'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILoggerService } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -108,4 +108,4 @@ export class TelemetryService extends Disposable implements ITelemetryService { } } -registerSingleton(ITelemetryService, TelemetryService, false); +registerSingleton(ITelemetryService, TelemetryService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/telemetry/electron-sandbox/telemetryService.ts b/src/vs/workbench/services/telemetry/electron-sandbox/telemetryService.ts index 4e2103332d1..7a1e6498df9 100644 --- a/src/vs/workbench/services/telemetry/electron-sandbox/telemetryService.ts +++ b/src/vs/workbench/services/telemetry/electron-sandbox/telemetryService.ts @@ -14,7 +14,7 @@ import { TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryI import { IStorageService } from 'vs/platform/storage/common/storage'; import { resolveWorkbenchCommonProperties } from 'vs/workbench/services/telemetry/electron-sandbox/workbenchCommonProperties'; import { TelemetryService as BaseTelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ClassifiedEvent, StrictPropertyCheck, OmitMetadata, IGDPRProperty } from 'vs/platform/telemetry/common/gdprTypings'; import { IFileService } from 'vs/platform/files/common/files'; import { IObservableValue } from 'vs/base/common/observableValue'; @@ -84,4 +84,4 @@ export class TelemetryService extends Disposable implements ITelemetryService { } } -registerSingleton(ITelemetryService, TelemetryService, false); +registerSingleton(ITelemetryService, TelemetryService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts b/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts index 8a59203a4df..2f45c4699cf 100644 --- a/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts +++ b/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts @@ -28,7 +28,7 @@ import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IValidGrammarDefinition, IValidEmbeddedLanguagesMap, IValidTokenTypeMap } from 'vs/workbench/services/textMate/common/TMScopeRegistry'; import { missingTMGrammarErrorMessage, TMGrammarFactory } from 'vs/workbench/services/textMate/common/TMGrammarFactory'; -import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; +import { IExtensionResourceLoaderService } from 'vs/platform/extensionResourceLoader/common/extensionResourceLoader'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; import { TMTokenization } from 'vs/workbench/services/textMate/common/TMTokenization'; diff --git a/src/vs/workbench/services/textMate/browser/nativeTextMateService.ts b/src/vs/workbench/services/textMate/browser/nativeTextMateService.ts index 326cb3c09f9..eaed1a95612 100644 --- a/src/vs/workbench/services/textMate/browser/nativeTextMateService.ts +++ b/src/vs/workbench/services/textMate/browser/nativeTextMateService.ts @@ -22,7 +22,7 @@ import { UriComponents, URI } from 'vs/base/common/uri'; import { ContiguousMultilineTokensBuilder } from 'vs/editor/common/tokens/contiguousMultilineTokensBuilder'; import { TMGrammarFactory } from 'vs/workbench/services/textMate/common/TMGrammarFactory'; import { IModelContentChangedEvent } from 'vs/editor/common/textModelEvents'; -import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; +import { IExtensionResourceLoaderService } from 'vs/platform/extensionResourceLoader/common/extensionResourceLoader'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { FileAccess } from 'vs/base/common/network'; diff --git a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts index 0fc86c74e4f..a6fb673f437 100644 --- a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts @@ -21,6 +21,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { SaveReason, SaveSourceRegistry } from 'vs/workbench/common/editor'; import { isEqual } from 'vs/base/common/resources'; import { UTF16be } from 'vs/workbench/services/textfile/common/encoding'; +import { isWeb } from 'vs/base/common/platform'; suite('Files - TextFileEditorModel', () => { @@ -655,8 +656,18 @@ suite('Files - TextFileEditorModel', () => { await accessor.textFileService.save(toResource.call(this, '/path/index_async2.txt')); assert.ok(!accessor.textFileService.isDirty(toResource.call(this, '/path/index_async.txt'))); assert.ok(!accessor.textFileService.isDirty(toResource.call(this, '/path/index_async2.txt'))); - assert.ok(assertIsDefined(getLastResolvedFileStat(model1)).mtime > m1Mtime); - assert.ok(assertIsDefined(getLastResolvedFileStat(model2)).mtime > m2Mtime); + + if (isWeb) { + // web tests does not ensure timeouts are respected at all, so we cannot + // really assert the mtime to be different, only that it is equal or greater. + // https://github.com/microsoft/vscode/issues/161886 + assert.ok(assertIsDefined(getLastResolvedFileStat(model1)).mtime >= m1Mtime); + assert.ok(assertIsDefined(getLastResolvedFileStat(model2)).mtime >= m2Mtime); + } else { + // on desktop we want to assert this condition more strictly though + assert.ok(assertIsDefined(getLastResolvedFileStat(model1)).mtime > m1Mtime); + assert.ok(assertIsDefined(getLastResolvedFileStat(model2)).mtime > m2Mtime); + } model1.dispose(); model2.dispose(); diff --git a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts index 1c0ba078d72..e9a8f68117e 100644 --- a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts +++ b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts @@ -14,7 +14,7 @@ import { Schemas } from 'vs/base/common/network'; import { ITextModelService, ITextModelContentProvider, ITextEditorModel, IResolvedTextEditorModel, isResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { IFileService } from 'vs/platform/files/common/files'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { ModelUndoRedoParticipant } from 'vs/editor/common/services/modelUndoRedoParticipant'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; @@ -246,4 +246,4 @@ export class TextModelResolverService extends Disposable implements ITextModelSe } } -registerSingleton(ITextModelService, TextModelResolverService, true); +registerSingleton(ITextModelService, TextModelResolverService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/textresourceProperties/common/textResourcePropertiesService.ts b/src/vs/workbench/services/textresourceProperties/common/textResourcePropertiesService.ts index 3cdfdc486cf..0118d3d4e27 100644 --- a/src/vs/workbench/services/textresourceProperties/common/textResourcePropertiesService.ts +++ b/src/vs/workbench/services/textresourceProperties/common/textResourcePropertiesService.ts @@ -10,7 +10,7 @@ import { OperatingSystem, OS } from 'vs/base/common/platform'; import { Schemas } from 'vs/base/common/network'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; @@ -54,4 +54,4 @@ export class TextResourcePropertiesService implements ITextResourcePropertiesSer } } -registerSingleton(ITextResourcePropertiesService, TextResourcePropertiesService, true); +registerSingleton(ITextResourcePropertiesService, TextResourcePropertiesService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/themes/browser/browserHostColorSchemeService.ts b/src/vs/workbench/services/themes/browser/browserHostColorSchemeService.ts index 469ada1f878..09cabf53111 100644 --- a/src/vs/workbench/services/themes/browser/browserHostColorSchemeService.ts +++ b/src/vs/workbench/services/themes/browser/browserHostColorSchemeService.ts @@ -5,7 +5,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { addMatchMediaChangeListener } from 'vs/base/browser/browser'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Disposable } from 'vs/base/common/lifecycle'; import { IHostColorSchemeService } from 'vs/workbench/services/themes/common/hostColorSchemeService'; @@ -54,4 +54,4 @@ export class BrowserHostColorSchemeService extends Disposable implements IHostCo } -registerSingleton(IHostColorSchemeService, BrowserHostColorSchemeService, true); +registerSingleton(IHostColorSchemeService, BrowserHostColorSchemeService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/themes/browser/fileIconThemeData.ts b/src/vs/workbench/services/themes/browser/fileIconThemeData.ts index c570a913f61..b65836c2377 100644 --- a/src/vs/workbench/services/themes/browser/fileIconThemeData.ts +++ b/src/vs/workbench/services/themes/browser/fileIconThemeData.ts @@ -12,7 +12,7 @@ import { ExtensionData, IThemeExtensionPoint, IWorkbenchFileIconTheme } from 'vs import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages'; import { asCSSUrl } from 'vs/base/browser/dom'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; +import { IExtensionResourceLoaderService } from 'vs/platform/extensionResourceLoader/common/extensionResourceLoader'; import { ILanguageService } from 'vs/editor/common/languages/language'; export class FileIconThemeData implements IWorkbenchFileIconTheme { diff --git a/src/vs/workbench/services/themes/browser/productIconThemeData.ts b/src/vs/workbench/services/themes/browser/productIconThemeData.ts index 63ca74a35cd..97fff900e80 100644 --- a/src/vs/workbench/services/themes/browser/productIconThemeData.ts +++ b/src/vs/workbench/services/themes/browser/productIconThemeData.ts @@ -17,7 +17,7 @@ import { isObject, isString } from 'vs/base/common/types'; import { ILogService } from 'vs/platform/log/common/log'; import { IconDefinition, getIconRegistry, IconContribution, IconFontDefinition, IconFontSource } from 'vs/platform/theme/common/iconRegistry'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; -import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; +import { IExtensionResourceLoaderService } from 'vs/platform/extensionResourceLoader/common/extensionResourceLoader'; export const DEFAULT_PRODUCT_ICON_THEME_ID = ''; // TODO diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index e164e0ac1f2..9e8bad431cb 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -27,7 +27,7 @@ import { registerColorThemeSchemas } from 'vs/workbench/services/themes/common/c import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; -import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; +import { IExtensionResourceLoaderService } from 'vs/platform/extensionResourceLoader/common/extensionResourceLoader'; import { ThemeRegistry, registerColorThemeExtensionPoint, registerFileIconThemeExtensionPoint, registerProductIconThemeExtensionPoint } from 'vs/workbench/services/themes/common/themeExtensionPoints'; import { updateColorThemeConfigurationSchemas, updateFileIconThemeConfigurationSchemas, ThemeConfiguration, updateProductIconThemeConfigurationSchemas } from 'vs/workbench/services/themes/common/themeConfiguration'; import { ProductIconThemeData, DEFAULT_PRODUCT_ICON_THEME_ID } from 'vs/workbench/services/themes/browser/productIconThemeData'; diff --git a/src/vs/workbench/services/themes/common/colorThemeData.ts b/src/vs/workbench/services/themes/common/colorThemeData.ts index 5ccb9e5f93d..c498d6c4da4 100644 --- a/src/vs/workbench/services/themes/common/colorThemeData.ts +++ b/src/vs/workbench/services/themes/common/colorThemeData.ts @@ -19,7 +19,7 @@ import { URI } from 'vs/base/common/uri'; import { parse as parsePList } from 'vs/workbench/services/themes/common/plistParser'; import { TokenStyle, SemanticTokenRule, ProbeScope, getTokenClassificationRegistry, TokenStyleValue, TokenStyleData, parseClassifierString } from 'vs/platform/theme/common/tokenClassificationRegistry'; import { MatcherWithPriority, Matcher, createMatchers } from 'vs/workbench/services/themes/common/textMateScopeMatcher'; -import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; +import { IExtensionResourceLoaderService } from 'vs/platform/extensionResourceLoader/common/extensionResourceLoader'; import { CharCode } from 'vs/base/common/charCode'; import { StorageScope, IStorageService, StorageTarget } from 'vs/platform/storage/common/storage'; import { ThemeConfiguration } from 'vs/workbench/services/themes/common/themeConfiguration'; diff --git a/src/vs/workbench/services/themes/electron-sandbox/nativeHostColorSchemeService.ts b/src/vs/workbench/services/themes/electron-sandbox/nativeHostColorSchemeService.ts index f9578f6805a..10769a87465 100644 --- a/src/vs/workbench/services/themes/electron-sandbox/nativeHostColorSchemeService.ts +++ b/src/vs/workbench/services/themes/electron-sandbox/nativeHostColorSchemeService.ts @@ -5,7 +5,7 @@ import { Emitter } from 'vs/base/common/event'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Disposable } from 'vs/base/common/lifecycle'; import { IHostColorSchemeService } from 'vs/workbench/services/themes/common/hostColorSchemeService'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; @@ -70,4 +70,4 @@ export class NativeHostColorSchemeService extends Disposable implements IHostCol } -registerSingleton(IHostColorSchemeService, NativeHostColorSchemeService, true); +registerSingleton(IHostColorSchemeService, NativeHostColorSchemeService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts b/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts index 7067dd39095..d93a77294b0 100644 --- a/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts +++ b/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts @@ -13,7 +13,7 @@ import { FileService } from 'vs/platform/files/common/fileService'; import { NullLogService } from 'vs/platform/log/common/log'; import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; import { FileAccess, Schemas } from 'vs/base/common/network'; -import { ExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/electron-sandbox/extensionResourceLoaderService'; +import { ExtensionResourceLoaderService } from 'vs/platform/extensionResourceLoader/electron-sandbox/extensionResourceLoaderService'; import { ITokenStyle } from 'vs/platform/theme/common/themeService'; import { mock, TestProductService } from 'vs/workbench/test/common/workbenchTestServices'; import { IRequestService } from 'vs/platform/request/common/request'; diff --git a/src/vs/workbench/services/timer/electron-sandbox/timerService.ts b/src/vs/workbench/services/timer/electron-sandbox/timerService.ts index 2a778a46ca8..7f54ca6ea75 100644 --- a/src/vs/workbench/services/timer/electron-sandbox/timerService.ts +++ b/src/vs/workbench/services/timer/electron-sandbox/timerService.ts @@ -14,7 +14,7 @@ import { IAccessibilityService } from 'vs/platform/accessibility/common/accessib import { IStartupMetrics, AbstractTimerService, Writeable, ITimerService } from 'vs/workbench/services/timer/browser/timerService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IProductService } from 'vs/platform/product/common/productService'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; @@ -85,7 +85,7 @@ export class TimerService extends AbstractTimerService { } } -registerSingleton(ITimerService, TimerService, true); +registerSingleton(ITimerService, TimerService, InstantiationType.Delayed); //#region cached data logic diff --git a/src/vs/workbench/services/tunnel/browser/tunnelService.ts b/src/vs/workbench/services/tunnel/browser/tunnelService.ts index 622b9c7d62a..179594037d5 100644 --- a/src/vs/workbench/services/tunnel/browser/tunnelService.ts +++ b/src/vs/workbench/services/tunnel/browser/tunnelService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { IAddressProvider } from 'vs/platform/remote/common/remoteAgentConnection'; import { AbstractTunnelService, ITunnelService, RemoteTunnel } from 'vs/platform/tunnel/common/tunnel'; @@ -18,6 +18,10 @@ export class TunnelService extends AbstractTunnelService { super(logService); } + public isPortPrivileged(_port: number): boolean { + return false; + } + protected retainOrCreateTunnel(_addressProvider: IAddressProvider, remoteHost: string, remotePort: number, localPort: number | undefined, elevateIfNeeded: boolean, privacy?: string, protocol?: string): Promise | undefined { const existing = this.getTunnelFromMap(remoteHost, remotePort); if (existing) { @@ -36,4 +40,4 @@ export class TunnelService extends AbstractTunnelService { } } -registerSingleton(ITunnelService, TunnelService, true); +registerSingleton(ITunnelService, TunnelService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/tunnel/electron-sandbox/tunnelService.ts b/src/vs/workbench/services/tunnel/electron-sandbox/tunnelService.ts index eb6835f334d..dcc855a7e9d 100644 --- a/src/vs/workbench/services/tunnel/electron-sandbox/tunnelService.ts +++ b/src/vs/workbench/services/tunnel/electron-sandbox/tunnelService.ts @@ -14,6 +14,8 @@ import { ISharedProcessTunnelService } from 'vs/platform/remote/common/sharedPro import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; +import { isMacintosh, isWindows } from 'vs/base/common/platform'; class SharedProcessTunnel extends Disposable implements RemoteTunnel { @@ -59,6 +61,7 @@ export class TunnelService extends AbstractTunnelService { @ISharedProcessTunnelService private readonly _sharedProcessTunnelService: ISharedProcessTunnelService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @ILifecycleService lifecycleService: ILifecycleService, + @INativeWorkbenchEnvironmentService private readonly _nativeWorkbenchEnvironmentService: INativeWorkbenchEnvironmentService ) { super(logService); @@ -70,6 +73,10 @@ export class TunnelService extends AbstractTunnelService { }); } + public isPortPrivileged(port: number): boolean { + return this.doIsPortPrivileged(port, isWindows, isMacintosh, this._nativeWorkbenchEnvironmentService.os.release); + } + protected retainOrCreateTunnel(addressProvider: IAddressProvider, remoteHost: string, remotePort: number, localPort: number | undefined, elevateIfNeeded: boolean, privacy?: string, protocol?: string): Promise | undefined { const existing = this.getTunnelFromMap(remoteHost, remotePort); if (existing) { diff --git a/src/vs/workbench/services/untitled/common/untitledTextEditorService.ts b/src/vs/workbench/services/untitled/common/untitledTextEditorService.ts index 3b31b992f6f..919d45b23d1 100644 --- a/src/vs/workbench/services/untitled/common/untitledTextEditorService.ts +++ b/src/vs/workbench/services/untitled/common/untitledTextEditorService.ts @@ -12,7 +12,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { ResourceMap } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; export const IUntitledTextEditorService = createDecorator('untitledTextEditorService'); @@ -261,4 +261,4 @@ export class UntitledTextEditorService extends Disposable implements IUntitledTe } } -registerSingleton(IUntitledTextEditorService, UntitledTextEditorService, true); +registerSingleton(IUntitledTextEditorService, UntitledTextEditorService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/update/browser/updateService.ts b/src/vs/workbench/services/update/browser/updateService.ts index 1087a9ce29b..61a3be52e13 100644 --- a/src/vs/workbench/services/update/browser/updateService.ts +++ b/src/vs/workbench/services/update/browser/updateService.ts @@ -5,7 +5,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { IUpdateService, State, UpdateType } from 'vs/platform/update/common/update'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -96,4 +96,4 @@ export class BrowserUpdateService extends Disposable implements IUpdateService { } } -registerSingleton(IUpdateService, BrowserUpdateService, false); +registerSingleton(IUpdateService, BrowserUpdateService, InstantiationType.Eager); diff --git a/src/vs/workbench/services/url/browser/urlService.ts b/src/vs/workbench/services/url/browser/urlService.ts index 7aec4c76552..602d6d32c6b 100644 --- a/src/vs/workbench/services/url/browser/urlService.ts +++ b/src/vs/workbench/services/url/browser/urlService.ts @@ -5,7 +5,7 @@ import { IURLService } from 'vs/platform/url/common/url'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { AbstractURLService } from 'vs/platform/url/common/urlService'; import { Event } from 'vs/base/common/event'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; @@ -91,4 +91,4 @@ export class BrowserURLService extends AbstractURLService { } } -registerSingleton(IURLService, BrowserURLService, true); +registerSingleton(IURLService, BrowserURLService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/url/electron-sandbox/urlService.ts b/src/vs/workbench/services/url/electron-sandbox/urlService.ts index 7bc2e4723c7..2ae2624e9f3 100644 --- a/src/vs/workbench/services/url/electron-sandbox/urlService.ts +++ b/src/vs/workbench/services/url/electron-sandbox/urlService.ts @@ -9,7 +9,7 @@ import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services'; import { URLHandlerChannel } from 'vs/platform/url/common/urlIpc'; import { IOpenerService, IOpener, matchesScheme } from 'vs/platform/opener/common/opener'; import { IProductService } from 'vs/platform/product/common/productService'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { NativeURLService } from 'vs/platform/url/common/urlService'; @@ -73,4 +73,4 @@ export class RelayURLService extends NativeURLService implements IURLHandler, IO } } -registerSingleton(IURLService, RelayURLService, false); +registerSingleton(IURLService, RelayURLService, InstantiationType.Eager); diff --git a/src/vs/workbench/services/userData/browser/userDataInit.ts b/src/vs/workbench/services/userData/browser/userDataInit.ts index 67ab3ee9976..e7cfccfe177 100644 --- a/src/vs/workbench/services/userData/browser/userDataInit.ts +++ b/src/vs/workbench/services/userData/browser/userDataInit.ts @@ -445,5 +445,5 @@ class InitializeOtherResourcesContribution implements IWorkbenchContribution { if (isWeb) { const workbenchRegistry = Registry.as(Extensions.Workbench); - workbenchRegistry.registerWorkbenchContribution(InitializeOtherResourcesContribution, 'InitializeOtherResourcesContribution', LifecyclePhase.Restored); + workbenchRegistry.registerWorkbenchContribution(InitializeOtherResourcesContribution, LifecyclePhase.Restored); } diff --git a/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts b/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts index f45c89a4111..5b202dc50ca 100644 --- a/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts +++ b/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts @@ -8,9 +8,8 @@ import { Event } from 'vs/base/common/event'; import { localize } from 'vs/nls'; import { MenuId } from 'vs/platform/actions/common/actions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IUserDataProfile, IUserDataProfileOptions, IUserDataProfileUpdateOptions, PROFILES_ENABLEMENT_CONFIG } from 'vs/platform/userDataProfile/common/userDataProfile'; -import { ContextKeyDefinedExpr, ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { ProductQualityContext } from 'vs/platform/contextkey/common/contextkeys'; +import { IUserDataProfile, IUserDataProfileOptions, IUserDataProfileUpdateOptions } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; export interface DidChangeUserDataProfileEvent { readonly preserveData: boolean; @@ -26,6 +25,7 @@ export interface IUserDataProfileService { readonly onDidChangeCurrentProfile: Event; readonly currentProfile: IUserDataProfile; updateCurrentProfile(currentProfile: IUserDataProfile, preserveData: boolean): Promise; + getShortName(profile: IUserDataProfile): string; } export const IUserDataProfileManagementService = createDecorator('IUserDataProfileManagementService'); @@ -74,10 +74,10 @@ export interface IResourceProfile { export const ManageProfilesSubMenu = new MenuId('SettingsProfiles'); export const MANAGE_PROFILES_ACTION_ID = 'workbench.profiles.actions.manage'; export const PROFILES_TTILE = { value: localize('settings profiles', "Settings Profiles"), original: 'Settings Profiles' }; -export const PROFILES_CATEGORY = PROFILES_TTILE.value; +export const PROFILES_CATEGORY = { ...PROFILES_TTILE }; export const PROFILE_EXTENSION = 'code-profile'; export const PROFILE_FILTER = [{ name: localize('profile', "Settings Profile"), extensions: [PROFILE_EXTENSION] }]; -export const PROFILES_ENABLEMENT_CONTEXT = ContextKeyExpr.or(ProductQualityContext.notEqualsTo('stable'), ContextKeyDefinedExpr.create(`config.${PROFILES_ENABLEMENT_CONFIG}`)); +export const PROFILES_ENABLEMENT_CONTEXT = new RawContextKey('profiles.enabled', true); export const CURRENT_PROFILE_CONTEXT = new RawContextKey('currentSettingsProfile', ''); export const IS_CURRENT_PROFILE_TRANSIENT_CONTEXT = new RawContextKey('isCurrentSettingsProfileTransient', false); export const HAS_PROFILES_CONTEXT = new RawContextKey('hasSettingsProfiles', false); diff --git a/src/vs/workbench/services/userDataProfile/common/userDataProfileImportExportService.ts b/src/vs/workbench/services/userDataProfile/common/userDataProfileImportExportService.ts index 61da2875775..cd2aa2d8c23 100644 --- a/src/vs/workbench/services/userDataProfile/common/userDataProfileImportExportService.ts +++ b/src/vs/workbench/services/userDataProfile/common/userDataProfileImportExportService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; @@ -56,7 +56,7 @@ export class UserDataProfileImportExportService implements IUserDataProfileImpor await this.progressService.withProgress({ location: ProgressLocation.Notification, - title: localize('profiles.importing', "{0}: Importing...", PROFILES_CATEGORY), + title: localize('profiles.importing', "{0}: Importing...", PROFILES_CATEGORY.value), }, async progress => { await this.userDataProfileManagementService.createAndEnterProfile(name); if (profileTemplate.settings) { @@ -70,13 +70,13 @@ export class UserDataProfileImportExportService implements IUserDataProfileImpor } }); - this.notificationService.info(localize('imported profile', "{0}: Imported successfully.", PROFILES_CATEGORY)); + this.notificationService.info(localize('imported profile', "{0}: Imported successfully.", PROFILES_CATEGORY.value)); } async setProfile(profile: IUserDataProfileTemplate): Promise { await this.progressService.withProgress({ location: ProgressLocation.Notification, - title: localize('profiles.applying', "{0}: Applying...", PROFILES_CATEGORY), + title: localize('profiles.applying', "{0}: Applying...", PROFILES_CATEGORY.value), }, async progress => { if (profile.settings) { await this.settingsProfile.applyProfile(profile.settings); @@ -88,9 +88,9 @@ export class UserDataProfileImportExportService implements IUserDataProfileImpor await this.extensionsProfile.applyProfile(profile.extensions); } }); - this.notificationService.info(localize('applied profile', "{0}: Applied successfully.", PROFILES_CATEGORY)); + this.notificationService.info(localize('applied profile', "{0}: Applied successfully.", PROFILES_CATEGORY.value)); } } -registerSingleton(IUserDataProfileImportExportService, UserDataProfileImportExportService, true); +registerSingleton(IUserDataProfileImportExportService, UserDataProfileImportExportService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/userDataProfile/common/userDataProfileService.ts b/src/vs/workbench/services/userDataProfile/common/userDataProfileService.ts index 77f8c28e6ce..63b78ba06b7 100644 --- a/src/vs/workbench/services/userDataProfile/common/userDataProfileService.ts +++ b/src/vs/workbench/services/userDataProfile/common/userDataProfileService.ts @@ -4,11 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import { Promises } from 'vs/base/common/async'; +import { Codicon } from 'vs/base/common/codicons'; import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; +import { localize } from 'vs/nls'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { DidChangeUserDataProfileEvent, IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; +const defaultUserDataProfileIcon = registerIcon('defaultSettingsProfiles-icon', Codicon.settings, localize('settingsProfilesIcon', 'Icon for Default Settings Profiles.')); + export class UserDataProfileService extends Disposable implements IUserDataProfileService { readonly _serviceBrand: undefined; @@ -64,4 +69,17 @@ export class UserDataProfileService extends Disposable implements IUserDataProfi await Promises.settled(joiners); } + getShortName(profile: IUserDataProfile): string { + if (profile.isDefault) { + return `$(${defaultUserDataProfileIcon.id})`; + } + if (profile.shortName) { + return profile.shortName; + } + if (profile.isTransient) { + return `T${profile.name.charAt(profile.name.length - 1)}`; + } + return profile.name.substring(0, 2).toUpperCase(); + } + } diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncEnablementService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncEnablementService.ts index c5eb692ab0a..a24838309e6 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataSyncEnablementService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncEnablementService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IUserDataSyncEnablementService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncEnablementService as BaseUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSyncEnablementService'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; @@ -18,4 +18,4 @@ export class UserDataSyncEnablementService extends BaseUserDataSyncEnablementSer } -registerSingleton(IUserDataSyncEnablementService, UserDataSyncEnablementService, true); +registerSingleton(IUserDataSyncEnablementService, UserDataSyncEnablementService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts index 8cf00b48ffd..36368bb699e 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts @@ -381,7 +381,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat if (status === SyncStatus.HasConflicts) { progress.report({ message: localize('resolving conflicts', "Resolving conflicts...") }); } else { - progress.report({ message: localize('syncing...', "Turnin on...") }); + progress.report({ message: localize('syncing...', "Turning on...") }); } })); await manualSyncTask.merge(); diff --git a/src/vs/workbench/services/userDataSync/browser/webUserDataSyncEnablementService.ts b/src/vs/workbench/services/userDataSync/browser/webUserDataSyncEnablementService.ts index 896f506c826..a05ae03d23d 100644 --- a/src/vs/workbench/services/userDataSync/browser/webUserDataSyncEnablementService.ts +++ b/src/vs/workbench/services/userDataSync/browser/webUserDataSyncEnablementService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IUserDataSyncEnablementService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncEnablementService } from 'vs/workbench/services/userDataSync/browser/userDataSyncEnablementService'; @@ -51,4 +51,4 @@ export class WebUserDataSyncEnablementService extends UserDataSyncEnablementServ } -registerSingleton(IUserDataSyncEnablementService, WebUserDataSyncEnablementService, true); +registerSingleton(IUserDataSyncEnablementService, WebUserDataSyncEnablementService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/userDataSync/common/userDataSyncUtil.ts b/src/vs/workbench/services/userDataSync/common/userDataSyncUtil.ts index aeafb075e5d..45329df40c4 100644 --- a/src/vs/workbench/services/userDataSync/common/userDataSyncUtil.ts +++ b/src/vs/workbench/services/userDataSync/common/userDataSyncUtil.ts @@ -6,7 +6,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IUserDataSyncUtilService, getDefaultIgnoredSettings } from 'vs/platform/userDataSync/common/userDataSync'; import { IStringDictionary } from 'vs/base/common/collections'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { FormattingOptions } from 'vs/base/common/jsonFormatter'; import { URI } from 'vs/base/common/uri'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; @@ -53,4 +53,4 @@ class UserDataSyncUtilService implements IUserDataSyncUtilService { } -registerSingleton(IUserDataSyncUtilService, UserDataSyncUtilService, true); +registerSingleton(IUserDataSyncUtilService, UserDataSyncUtilService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/userDataSync/electron-sandbox/userDataAutoSyncService.ts b/src/vs/workbench/services/userDataSync/electron-sandbox/userDataAutoSyncService.ts index d373b910b87..62da5d44f2e 100644 --- a/src/vs/workbench/services/userDataSync/electron-sandbox/userDataAutoSyncService.ts +++ b/src/vs/workbench/services/userDataSync/electron-sandbox/userDataAutoSyncService.ts @@ -7,7 +7,7 @@ import { IUserDataAutoSyncService, UserDataSyncError } from 'vs/platform/userDat import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event } from 'vs/base/common/event'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; class UserDataAutoSyncService implements IUserDataAutoSyncService { @@ -36,4 +36,4 @@ class UserDataAutoSyncService implements IUserDataAutoSyncService { } -registerSingleton(IUserDataAutoSyncService, UserDataAutoSyncService, true); +registerSingleton(IUserDataAutoSyncService, UserDataAutoSyncService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/userDataSync/electron-sandbox/userDataSyncAccountService.ts b/src/vs/workbench/services/userDataSync/electron-sandbox/userDataSyncAccountService.ts index c9806977001..134525e6d86 100644 --- a/src/vs/workbench/services/userDataSync/electron-sandbox/userDataSyncAccountService.ts +++ b/src/vs/workbench/services/userDataSync/electron-sandbox/userDataSyncAccountService.ts @@ -5,7 +5,7 @@ import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Disposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { IUserDataSyncAccountService, IUserDataSyncAccount } from 'vs/platform/userDataSync/common/userDataSyncAccount'; @@ -44,4 +44,4 @@ export class UserDataSyncAccountService extends Disposable implements IUserDataS } -registerSingleton(IUserDataSyncAccountService, UserDataSyncAccountService, true); +registerSingleton(IUserDataSyncAccountService, UserDataSyncAccountService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/userDataSync/electron-sandbox/userDataSyncMachinesService.ts b/src/vs/workbench/services/userDataSync/electron-sandbox/userDataSyncMachinesService.ts index 059d4989d7d..766386ba6a9 100644 --- a/src/vs/workbench/services/userDataSync/electron-sandbox/userDataSyncMachinesService.ts +++ b/src/vs/workbench/services/userDataSync/electron-sandbox/userDataSyncMachinesService.ts @@ -6,7 +6,7 @@ import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services'; import { Disposable } from 'vs/base/common/lifecycle'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IUserDataSyncMachinesService, IUserDataSyncMachine } from 'vs/platform/userDataSync/common/userDataSyncMachines'; import { Event } from 'vs/base/common/event'; @@ -47,4 +47,4 @@ class UserDataSyncMachinesService extends Disposable implements IUserDataSyncMac } -registerSingleton(IUserDataSyncMachinesService, UserDataSyncMachinesService, true); +registerSingleton(IUserDataSyncMachinesService, UserDataSyncMachinesService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/userDataSync/electron-sandbox/userDataSyncStoreManagementService.ts b/src/vs/workbench/services/userDataSync/electron-sandbox/userDataSyncStoreManagementService.ts index 0ff3079372c..a85e432cd86 100644 --- a/src/vs/workbench/services/userDataSync/electron-sandbox/userDataSyncStoreManagementService.ts +++ b/src/vs/workbench/services/userDataSync/electron-sandbox/userDataSyncStoreManagementService.ts @@ -9,7 +9,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { AbstractUserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; import { IProductService } from 'vs/platform/product/common/productService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { UserDataSyncStoreManagementServiceChannelClient } from 'vs/platform/userDataSync/common/userDataSyncIpc'; class UserDataSyncStoreManagementService extends AbstractUserDataSyncStoreManagementService implements IUserDataSyncStoreManagementService { @@ -37,4 +37,4 @@ class UserDataSyncStoreManagementService extends AbstractUserDataSyncStoreManage } -registerSingleton(IUserDataSyncStoreManagementService, UserDataSyncStoreManagementService, true); +registerSingleton(IUserDataSyncStoreManagementService, UserDataSyncStoreManagementService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/views/browser/viewDescriptorService.ts b/src/vs/workbench/services/views/browser/viewDescriptorService.ts index de843eacab8..12750975fc0 100644 --- a/src/vs/workbench/services/views/browser/viewDescriptorService.ts +++ b/src/vs/workbench/services/views/browser/viewDescriptorService.ts @@ -11,7 +11,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { toDisposable, DisposableStore, Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { ViewPaneContainer, ViewPaneContainerAction, ViewsSubMenu } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Event, Emitter } from 'vs/base/common/event'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { generateUuid } from 'vs/base/common/uuid'; @@ -924,4 +924,4 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor } } -registerSingleton(IViewDescriptorService, ViewDescriptorService, true); +registerSingleton(IViewDescriptorService, ViewDescriptorService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/views/common/viewContainerModel.ts b/src/vs/workbench/services/views/common/viewContainerModel.ts index 0418f5ee6cd..850c42b99d2 100644 --- a/src/vs/workbench/services/views/common/viewContainerModel.ts +++ b/src/vs/workbench/services/views/common/viewContainerModel.ts @@ -9,15 +9,40 @@ import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget import { Registry } from 'vs/platform/registry/common/platform'; import { Disposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { URI } from 'vs/base/common/uri'; import { coalesce, move } from 'vs/base/common/arrays'; import { isUndefined, isUndefinedOrNull } from 'vs/base/common/types'; -import { isEqual } from 'vs/base/common/resources'; +import { isEqual, joinPath } from 'vs/base/common/resources'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IStringDictionary } from 'vs/base/common/collections'; import { Extensions, IProfileStorageRegistry } from 'vs/workbench/services/userDataProfile/common/userDataProfileStorageRegistry'; import { localize } from 'vs/nls'; +import { ILogger, ILoggerService } from 'vs/platform/log/common/log'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; +import { IOutputChannelRegistry, IOutputService, Extensions as OutputExtensions } from 'vs/workbench/services/output/common/output'; + +function getViewsLogFile(environmentService: IWorkbenchEnvironmentService): URI { return joinPath(environmentService.windowLogsPath, 'views.log'); } + +registerAction2(class extends Action2 { + constructor() { + super({ + id: '_workbench.output.showViewsLog', + title: { value: 'Show Views Log', original: 'Show Views Log' }, + category: Categories.Developer, + f1: true + }); + } + async run(servicesAccessor: ServicesAccessor): Promise { + const outputService = servicesAccessor.get(IOutputService); + const environmentService = servicesAccessor.get(IWorkbenchEnvironmentService); + Registry.as(OutputExtensions.OutputChannels).registerChannel({ id: 'viewsLog', label: localize('views log', "Views"), file: getViewsLogFile(environmentService), log: true }); + outputService.showChannel('viewsLog'); + + } +}); export function getViewsStateStorageId(viewContainerStorageId: string): string { return `${viewContainerStorageId}.hidden`; } @@ -84,13 +109,19 @@ class ViewDescriptorsState extends Disposable { private _onDidChangeStoredState = this._register(new Emitter<{ id: string; visible: boolean }[]>()); readonly onDidChangeStoredState = this._onDidChangeStoredState.event; + private readonly logger: ILogger; + constructor( viewContainerStorageId: string, viewContainerName: string, @IStorageService private readonly storageService: IStorageService, + @ILoggerService loggerService: ILoggerService, + @IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService, ) { super(); + this.logger = loggerService.createLogger(getViewsLogFile(workbenchEnvironmentService)); + this.globalViewsStateStorageId = getViewsStateStorageId(viewContainerStorageId); this.workspaceViewsStateStorageId = viewContainerStorageId; this._register(this.storageService.onDidChangeValue(e => this.onDidStorageChange(e))); @@ -162,6 +193,9 @@ class ViewDescriptorsState extends Disposable { const state = this.get(id); if (state) { if (state.visibleGlobal !== !storedState.isHidden) { + if (!storedState.isHidden) { + this.logger.info(`View visibility state changed: ${id} is now visible`); + } changedStates.push({ id, visible: !storedState.isHidden }); } } else { @@ -344,22 +378,29 @@ export class ViewContainerModel extends Disposable implements IViewContainerMode private _onDidMoveVisibleViewDescriptors = this._register(new Emitter<{ from: IViewDescriptorRef; to: IViewDescriptorRef }>()); readonly onDidMoveVisibleViewDescriptors: Event<{ from: IViewDescriptorRef; to: IViewDescriptorRef }> = this._onDidMoveVisibleViewDescriptors.event; + private readonly logger: ILogger; + constructor( readonly viewContainer: ViewContainer, @IInstantiationService instantiationService: IInstantiationService, @IContextKeyService private readonly contextKeyService: IContextKeyService, + @ILoggerService loggerService: ILoggerService, + @IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService, ) { super(); + this.logger = loggerService.createLogger(getViewsLogFile(workbenchEnvironmentService)); + this._register(Event.filter(contextKeyService.onDidChangeContext, e => e.affectsSome(this.contextKeys))(() => this.onDidChangeContext())); this.viewDescriptorsState = this._register(instantiationService.createInstance(ViewDescriptorsState, viewContainer.storageId || `${viewContainer.id}.state`, typeof viewContainer.title === 'string' ? viewContainer.title : viewContainer.title.original)); this._register(this.viewDescriptorsState.onDidChangeStoredState(items => this.updateVisibility(items))); this._register(Event.any( - this.onDidAddVisibleViewDescriptors, - this.onDidRemoveVisibleViewDescriptors, - this.onDidMoveVisibleViewDescriptors) - (() => { + Event.map(this.onDidAddVisibleViewDescriptors, added => `Added views:${added.map(v => v.viewDescriptor.id).join(',')}`), + Event.map(this.onDidRemoveVisibleViewDescriptors, removed => `Removed views:${removed.map(v => v.viewDescriptor.id).join(',')}`), + Event.map(this.onDidMoveVisibleViewDescriptors, ({ from, to }) => `Moved view ${from.viewDescriptor.id} to ${to.viewDescriptor.id}`)) + (message => { + this.logger.info(message); this.viewDescriptorsState.updateState(this.allViewDescriptors); this.updateContainerInfo(); })); @@ -464,6 +505,9 @@ export class ViewContainerModel extends Disposable implements IViewContainerMode viewDescriptorItem.state.visibleWorkspace = visible; } else { viewDescriptorItem.state.visibleGlobal = visible; + if (visible) { + this.logger.info(`Showing view ${viewDescriptorItem.viewDescriptor.id} in the container ${this.viewContainer.id}`); + } } // return `true` only if visibility is changed @@ -532,7 +576,11 @@ export class ViewContainerModel extends Disposable implements IViewContainerMode if (viewDescriptor.workspace) { state.visibleWorkspace = isUndefinedOrNull(addedViewDescriptorState.visible) ? (isUndefinedOrNull(state.visibleWorkspace) ? !viewDescriptor.hideByDefault : state.visibleWorkspace) : addedViewDescriptorState.visible; } else { + const isVisible = state.visibleGlobal; state.visibleGlobal = isUndefinedOrNull(addedViewDescriptorState.visible) ? (isUndefinedOrNull(state.visibleGlobal) ? !viewDescriptor.hideByDefault : state.visibleGlobal) : addedViewDescriptorState.visible; + if (state.visibleGlobal && !isVisible) { + this.logger.info(`Added view ${viewDescriptor.id} in the container ${this.viewContainer.id} and showing it`); + } } state.collapsed = isUndefinedOrNull(addedViewDescriptorState.collapsed) ? (isUndefinedOrNull(state.collapsed) ? !!viewDescriptor.collapsed : state.collapsed) : addedViewDescriptorState.collapsed; } else { diff --git a/src/vs/workbench/services/views/test/browser/viewContainerModel.test.ts b/src/vs/workbench/services/views/test/browser/viewContainerModel.test.ts index 06e375acf9d..8fea43d8892 100644 --- a/src/vs/workbench/services/views/test/browser/viewContainerModel.test.ts +++ b/src/vs/workbench/services/views/test/browser/viewContainerModel.test.ts @@ -18,6 +18,7 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { Event } from 'vs/base/common/event'; import { getViewsStateStorageId } from 'vs/workbench/services/views/common/viewContainerModel'; +import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; const ViewContainerRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); const ViewsRegistry = Registry.as(ViewContainerExtensions.ViewsRegistry); @@ -95,7 +96,7 @@ suite('ViewContainerModel', () => { assert.strictEqual(target.elements.length, 0); }); - test('when contexts', async function () { + test('when contexts', () => runWithFakedTimers({ useFakeTimers: true }, async () => { container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); @@ -137,9 +138,9 @@ suite('ViewContainerModel', () => { await new Promise(c => setTimeout(c, 30)); assert.strictEqual(testObject.visibleViewDescriptors.length, 0, 'view should not be there anymore'); assert.strictEqual(target.elements.length, 0); - }); + })); - test('when contexts - multiple', async function () { + test('when contexts - multiple', () => runWithFakedTimers({ useFakeTimers: true }, async () => { container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); @@ -160,9 +161,9 @@ suite('ViewContainerModel', () => { assert.deepStrictEqual(target.elements, [view1, view2], 'both views should be visible'); ViewsRegistry.deregisterViews([view1, view2], container); - }); + })); - test('when contexts - multiple 2', async function () { + test('when contexts - multiple 2', () => runWithFakedTimers({ useFakeTimers: true }, async () => { container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); @@ -183,7 +184,7 @@ suite('ViewContainerModel', () => { assert.deepStrictEqual(target.elements, [view1, view2], 'both views should be visible'); ViewsRegistry.deregisterViews([view1, view2], container); - }); + })); test('setVisible', () => { container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); @@ -259,7 +260,7 @@ suite('ViewContainerModel', () => { assert.deepStrictEqual(target.elements, [view1, view2, view3]); }); - test('view states', async function () { + test('view states', () => runWithFakedTimers({ useFakeTimers: true }, async () => { storageService.store(`${container.id}.state.hidden`, JSON.stringify([{ id: 'view1', isHidden: true }]), StorageScope.PROFILE, StorageTarget.MACHINE); container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); @@ -277,9 +278,9 @@ suite('ViewContainerModel', () => { ViewsRegistry.registerViews([viewDescriptor], container); assert.strictEqual(testObject.visibleViewDescriptors.length, 0, 'view should not appear since it was set not visible in view state'); assert.strictEqual(target.elements.length, 0); - }); + })); - test('view states and when contexts', async function () { + test('view states and when contexts', () => runWithFakedTimers({ useFakeTimers: true }, async () => { storageService.store(`${container.id}.state.hidden`, JSON.stringify([{ id: 'view1', isHidden: true }]), StorageScope.PROFILE, StorageTarget.MACHINE); container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); @@ -307,9 +308,9 @@ suite('ViewContainerModel', () => { await new Promise(c => setTimeout(c, 30)); assert.strictEqual(testObject.visibleViewDescriptors.length, 0, 'view should still not appear since it was set not visible in view state'); assert.strictEqual(target.elements.length, 0); - }); + })); - test('view states and when contexts multiple views', async function () { + test('view states and when contexts multiple views', () => runWithFakedTimers({ useFakeTimers: true }, async () => { storageService.store(`${container.id}.state.hidden`, JSON.stringify([{ id: 'view1', isHidden: true }]), StorageScope.PROFILE, StorageTarget.MACHINE); container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); @@ -353,9 +354,9 @@ suite('ViewContainerModel', () => { await new Promise(c => setTimeout(c, 30)); assert.deepStrictEqual(testObject.visibleViewDescriptors, [view2], 'Only view2 should be visible'); assert.deepStrictEqual(target.elements, [view2]); - }); + })); - test('remove event is not triggered if view was hidden and removed', async function () { + test('remove event is not triggered if view was hidden and removed', () => runWithFakedTimers({ useFakeTimers: true }, async () => { container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); @@ -383,9 +384,9 @@ suite('ViewContainerModel', () => { key.set(false); await new Promise(c => setTimeout(c, 30)); assert.ok(!targetEvent.called, 'remove event should not be called since it is already hidden'); - }); + })); - test('add event is not triggered if view was set visible (when visible) and not active', async function () { + test('add event is not triggered if view was set visible (when visible) and not active', () => runWithFakedTimers({ useFakeTimers: true }, async () => { container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); @@ -410,9 +411,9 @@ suite('ViewContainerModel', () => { assert.ok(!targetEvent.called, 'add event should not be called since it is already visible'); assert.strictEqual(testObject.visibleViewDescriptors.length, 0); assert.strictEqual(target.elements.length, 0); - }); + })); - test('remove event is not triggered if view was hidden and not active', async function () { + test('remove event is not triggered if view was hidden and not active', () => runWithFakedTimers({ useFakeTimers: true }, async () => { container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); @@ -437,9 +438,9 @@ suite('ViewContainerModel', () => { assert.ok(!targetEvent.called, 'add event should not be called since it is disabled'); assert.strictEqual(testObject.visibleViewDescriptors.length, 0); assert.strictEqual(target.elements.length, 0); - }); + })); - test('add event is not triggered if view was set visible (when not visible) and not active', async function () { + test('add event is not triggered if view was set visible (when not visible) and not active', () => runWithFakedTimers({ useFakeTimers: true }, async () => { container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); @@ -468,9 +469,9 @@ suite('ViewContainerModel', () => { assert.ok(!targetEvent.called, 'add event should not be called since it is disabled'); assert.strictEqual(testObject.visibleViewDescriptors.length, 0); assert.strictEqual(target.elements.length, 0); - }); + })); - test('added view descriptors are in ascending order in the event', async function () { + test('added view descriptors are in ascending order in the event', () => runWithFakedTimers({ useFakeTimers: true }, async () => { container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); @@ -519,9 +520,9 @@ suite('ViewContainerModel', () => { assert.strictEqual(target.elements[2].id, 'view3'); assert.strictEqual(target.elements[3].id, 'view4'); assert.strictEqual(target.elements[4].id, 'view5'); - }); + })); - test('add event is triggered only once when view is set visible while it is set active', async function () { + test('add event is triggered only once when view is set visible while it is set active', () => runWithFakedTimers({ useFakeTimers: true }, async () => { container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); @@ -550,9 +551,9 @@ suite('ViewContainerModel', () => { assert.strictEqual(testObject.visibleViewDescriptors.length, 1); assert.strictEqual(target.elements.length, 1); assert.strictEqual(target.elements[0].id, 'view1'); - }); + })); - test('add event is not triggered only when view is set hidden while it is set active', async function () { + test('add event is not triggered only when view is set hidden while it is set active', () => runWithFakedTimers({ useFakeTimers: true }, async () => { container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); @@ -579,9 +580,9 @@ suite('ViewContainerModel', () => { assert.strictEqual(targetEvent.callCount, 0); assert.strictEqual(testObject.visibleViewDescriptors.length, 0); assert.strictEqual(target.elements.length, 0); - }); + })); - test('#142087: view descriptor visibility is not reset', async function () { + test('#142087: view descriptor visibility is not reset', () => runWithFakedTimers({ useFakeTimers: true }, async () => { container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const viewDescriptor: IViewDescriptor = { @@ -602,9 +603,9 @@ suite('ViewContainerModel', () => { assert.strictEqual(testObject.isVisible(viewDescriptor.id), false); assert.strictEqual(testObject.activeViewDescriptors[0].id, viewDescriptor.id); assert.strictEqual(testObject.visibleViewDescriptors.length, 0); - }); + })); - test('remove event is triggered properly if mutliple views are hidden at the same time', async function () { + test('remove event is triggered properly if mutliple views are hidden at the same time', () => runWithFakedTimers({ useFakeTimers: true }, async () => { container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); @@ -660,9 +661,9 @@ suite('ViewContainerModel', () => { }]); assert.strictEqual(target.elements.length, 1); assert.strictEqual(target.elements[0].id, viewDescriptor1.id); - }); + })); - test('add event is triggered properly if mutliple views are hidden at the same time', async function () { + test('add event is triggered properly if mutliple views are hidden at the same time', () => runWithFakedTimers({ useFakeTimers: true }, async () => { container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); @@ -728,9 +729,9 @@ suite('ViewContainerModel', () => { assert.strictEqual(target.elements[0].id, viewDescriptor1.id); assert.strictEqual(target.elements[1].id, viewDescriptor2.id); assert.strictEqual(target.elements[2].id, viewDescriptor3.id); - }); + })); - test('add and remove events are triggered properly if mutliple views are hidden and added at the same time', async function () { + test('add and remove events are triggered properly if mutliple views are hidden and added at the same time', () => runWithFakedTimers({ useFakeTimers: true }, async () => { container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); @@ -805,6 +806,6 @@ suite('ViewContainerModel', () => { assert.strictEqual(target.elements.length, 2); assert.strictEqual(target.elements[0].id, viewDescriptor1.id); assert.strictEqual(target.elements[1].id, viewDescriptor3.id); - }); + })); }); diff --git a/src/vs/workbench/services/workingCopy/browser/workingCopyBackupService.ts b/src/vs/workbench/services/workingCopy/browser/workingCopyBackupService.ts index 9b6ac4489eb..14f99f72911 100644 --- a/src/vs/workbench/services/workingCopy/browser/workingCopyBackupService.ts +++ b/src/vs/workbench/services/workingCopy/browser/workingCopyBackupService.ts @@ -32,4 +32,4 @@ export class BrowserWorkingCopyBackupService extends WorkingCopyBackupService { registerSingleton(IWorkingCopyBackupService, BrowserWorkingCopyBackupService, InstantiationType.Eager); // Register Backup Tracker -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(BrowserWorkingCopyBackupTracker, 'BrowserWorkingCopyBackupTracker', LifecyclePhase.Starting); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(BrowserWorkingCopyBackupTracker, LifecyclePhase.Starting); diff --git a/src/vs/workbench/services/workingCopy/browser/workingCopyHistoryService.ts b/src/vs/workbench/services/workingCopy/browser/workingCopyHistoryService.ts index 5b620f0b1e8..8be2e38e3c5 100644 --- a/src/vs/workbench/services/workingCopy/browser/workingCopyHistoryService.ts +++ b/src/vs/workbench/services/workingCopy/browser/workingCopyHistoryService.ts @@ -11,7 +11,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { ILogService } from 'vs/platform/log/common/log'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkingCopyHistoryModelOptions, WorkingCopyHistoryService } from 'vs/workbench/services/workingCopy/common/workingCopyHistoryService'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkingCopyHistoryService } from 'vs/workbench/services/workingCopy/common/workingCopyHistory'; export class BrowserWorkingCopyHistoryService extends WorkingCopyHistoryService { @@ -34,4 +34,4 @@ export class BrowserWorkingCopyHistoryService extends WorkingCopyHistoryService } // Register Service -registerSingleton(IWorkingCopyHistoryService, BrowserWorkingCopyHistoryService, true); +registerSingleton(IWorkingCopyHistoryService, BrowserWorkingCopyHistoryService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyEditorService.ts b/src/vs/workbench/services/workingCopy/common/workingCopyEditorService.ts index e4af0ad9baa..f95cb71ff84 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyEditorService.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyEditorService.ts @@ -5,7 +5,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { EditorsOrder, IEditorIdentifier } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { IWorkingCopy, IWorkingCopyIdentifier } from 'vs/workbench/services/workingCopy/common/workingCopy'; @@ -97,4 +97,4 @@ export class WorkingCopyEditorService extends Disposable implements IWorkingCopy } // Register Service -registerSingleton(IWorkingCopyEditorService, WorkingCopyEditorService, true); +registerSingleton(IWorkingCopyEditorService, WorkingCopyEditorService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyFileService.ts b/src/vs/workbench/services/workingCopy/common/workingCopyFileService.ts index 5e830f345ef..b70d40f4676 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyFileService.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyFileService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Event, AsyncEmitter, IWaitUntil } from 'vs/base/common/event'; import { Promises } from 'vs/base/common/async'; import { insert } from 'vs/base/common/arrays'; @@ -525,4 +525,4 @@ export class WorkingCopyFileService extends Disposable implements IWorkingCopyFi //#endregion } -registerSingleton(IWorkingCopyFileService, WorkingCopyFileService, true); +registerSingleton(IWorkingCopyFileService, WorkingCopyFileService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyHistoryService.ts b/src/vs/workbench/services/workingCopy/common/workingCopyHistoryService.ts index 07c1ae6d65a..1e299593f87 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyHistoryService.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyHistoryService.ts @@ -29,6 +29,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { SaveSource, SaveSourceRegistry } from 'vs/workbench/common/editor'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { lastOrDefault } from 'vs/base/common/arrays'; +import { escapeRegExpCharacters } from 'vs/base/common/strings'; interface ISerializedWorkingCopyHistoryModel { readonly version: number; @@ -103,7 +104,7 @@ export class WorkingCopyHistoryModel { this.workingCopyResource = workingCopyResource; this.workingCopyName = this.labelService.getUriBasenameLabel(workingCopyResource); - this.historyEntriesNameMatcher = new RegExp(`[A-Za-z0-9]{4}${extname(workingCopyResource)}`); + this.historyEntriesNameMatcher = new RegExp(`[A-Za-z0-9]{4}${escapeRegExpCharacters(extname(workingCopyResource))}`); // Update locations this.historyEntriesFolder = this.toHistoryEntriesFolder(this.historyHome, workingCopyResource); @@ -786,4 +787,4 @@ export abstract class WorkingCopyHistoryService extends Disposable implements IW } // Register History Tracker -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(WorkingCopyHistoryTracker, 'WorkingCopyHistoryTracker', LifecyclePhase.Restored); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(WorkingCopyHistoryTracker, LifecyclePhase.Restored); diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyService.ts b/src/vs/workbench/services/workingCopy/common/workingCopyService.ts index a7522cbcb77..e9a5927e310 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyService.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Event, Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { Disposable, IDisposable, toDisposable, DisposableStore, dispose } from 'vs/base/common/lifecycle'; @@ -290,4 +290,4 @@ export class WorkingCopyService extends Disposable implements IWorkingCopyServic //#endregion } -registerSingleton(IWorkingCopyService, WorkingCopyService, true); +registerSingleton(IWorkingCopyService, WorkingCopyService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupService.ts b/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupService.ts index f2c28188a53..1aa006ca6e6 100644 --- a/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupService.ts +++ b/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupService.ts @@ -42,4 +42,4 @@ export class NativeWorkingCopyBackupService extends WorkingCopyBackupService { registerSingleton(IWorkingCopyBackupService, NativeWorkingCopyBackupService, InstantiationType.Eager); // Register Backup Tracker -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(NativeWorkingCopyBackupTracker, 'NativeWorkingCopyBackupTracker', LifecyclePhase.Starting); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(NativeWorkingCopyBackupTracker, LifecyclePhase.Starting); diff --git a/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyHistoryService.ts b/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyHistoryService.ts index 55167103df8..173e036770e 100644 --- a/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyHistoryService.ts +++ b/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyHistoryService.ts @@ -15,7 +15,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { ILogService } from 'vs/platform/log/common/log'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkingCopyHistoryModelOptions, WorkingCopyHistoryService } from 'vs/workbench/services/workingCopy/common/workingCopyHistoryService'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkingCopyHistoryService, MAX_PARALLEL_HISTORY_IO_OPS } from 'vs/workbench/services/workingCopy/common/workingCopyHistory'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; @@ -98,4 +98,4 @@ export class NativeWorkingCopyHistoryService extends WorkingCopyHistoryService { } // Register Service -registerSingleton(IWorkingCopyHistoryService, NativeWorkingCopyHistoryService, true); +registerSingleton(IWorkingCopyHistoryService, NativeWorkingCopyHistoryService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/workspaces/browser/workspaceEditingService.ts b/src/vs/workbench/services/workspaces/browser/workspaceEditingService.ts index 637bf9d0a62..9a90505743f 100644 --- a/src/vs/workbench/services/workspaces/browser/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspaces/browser/workspaceEditingService.ts @@ -16,7 +16,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { IHostService } from 'vs/workbench/services/host/browser/host'; import { AbstractWorkspaceEditingService } from 'vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { URI } from 'vs/base/common/uri'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust'; @@ -57,4 +57,4 @@ export class BrowserWorkspaceEditingService extends AbstractWorkspaceEditingServ } } -registerSingleton(IWorkspaceEditingService, BrowserWorkspaceEditingService, true); +registerSingleton(IWorkspaceEditingService, BrowserWorkspaceEditingService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/workspaces/browser/workspacesService.ts b/src/vs/workbench/services/workspaces/browser/workspacesService.ts index cddb23ae96a..08562f80207 100644 --- a/src/vs/workbench/services/workspaces/browser/workspacesService.ts +++ b/src/vs/workbench/services/workspaces/browser/workspacesService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkspacesService, IWorkspaceFolderCreationData, IEnterWorkspaceResult, IRecentlyOpened, restoreRecentlyOpened, IRecent, isRecentFile, isRecentFolder, toStoreData, IStoredWorkspaceFolder, getStoredWorkspaceFolder, IStoredWorkspace, isRecentWorkspace } from 'vs/platform/workspaces/common/workspaces'; import { URI } from 'vs/base/common/uri'; import { Emitter } from 'vs/base/common/event'; @@ -215,4 +215,4 @@ export class BrowserWorkspacesService extends Disposable implements IWorkspacesS //#endregion } -registerSingleton(IWorkspacesService, BrowserWorkspacesService, true); +registerSingleton(IWorkspacesService, BrowserWorkspacesService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/workspaces/common/editSessionIdentityService.ts b/src/vs/workbench/services/workspaces/common/editSessionIdentityService.ts index a9b4a8cd98e..bf8d83d43ee 100644 --- a/src/vs/workbench/services/workspaces/common/editSessionIdentityService.ts +++ b/src/vs/workbench/services/workspaces/common/editSessionIdentityService.ts @@ -5,7 +5,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { IEditSessionIdentityProvider, IEditSessionIdentityService } from 'vs/platform/workspace/common/editSessions'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; @@ -54,4 +54,4 @@ export class EditSessionIdentityService implements IEditSessionIdentityService { } } -registerSingleton(IEditSessionIdentityService, EditSessionIdentityService, true); +registerSingleton(IEditSessionIdentityService, EditSessionIdentityService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/workspaces/common/workspaceTrust.ts b/src/vs/workbench/services/workspaces/common/workspaceTrust.ts index 2e249f7aa57..d9cdd1ba7ac 100644 --- a/src/vs/workbench/services/workspaces/common/workspaceTrust.ts +++ b/src/vs/workbench/services/workspaces/common/workspaceTrust.ts @@ -10,7 +10,7 @@ import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { IPath } from 'vs/platform/window/common/window'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IRemoteAuthorityResolverService, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; import { isVirtualResource } from 'vs/platform/workspace/common/virtualWorkspace'; @@ -887,4 +887,4 @@ class WorkspaceTrustMemento { } } -registerSingleton(IWorkspaceTrustRequestService, WorkspaceTrustRequestService, false); +registerSingleton(IWorkspaceTrustRequestService, WorkspaceTrustRequestService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts b/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts index 0cf6f3892c2..3d4f731f544 100644 --- a/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts @@ -20,7 +20,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { ILifecycleService, ShutdownReason } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IFileDialogService, IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILabelService } from 'vs/platform/label/common/label'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IHostService } from 'vs/workbench/services/host/browser/host'; @@ -190,4 +190,4 @@ export class NativeWorkspaceEditingService extends AbstractWorkspaceEditingServi } } -registerSingleton(IWorkspaceEditingService, NativeWorkspaceEditingService, true); +registerSingleton(IWorkspaceEditingService, NativeWorkspaceEditingService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/workspaces/electron-sandbox/workspacesService.ts b/src/vs/workbench/services/workspaces/electron-sandbox/workspacesService.ts index 48fbb4e5a1c..e5cb1078e35 100644 --- a/src/vs/workbench/services/workspaces/electron-sandbox/workspacesService.ts +++ b/src/vs/workbench/services/workspaces/electron-sandbox/workspacesService.ts @@ -5,7 +5,7 @@ import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; @@ -22,4 +22,4 @@ export class NativeWorkspacesService implements IWorkspacesService { } } -registerSingleton(IWorkspacesService, NativeWorkspacesService, true); +registerSingleton(IWorkspacesService, NativeWorkspacesService, InstantiationType.Delayed); diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index b955a8c3efb..796e5fc4fe3 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -56,7 +56,7 @@ import { IEditorService, ISaveEditorsOptions, IRevertAllEditorsOptions, Preferre import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IEditorPaneRegistry, EditorPaneDescriptor } from 'vs/workbench/browser/editor'; import { Dimension, IDimension } from 'vs/base/browser/dom'; -import { ILogService, NullLogService } from 'vs/platform/log/common/log'; +import { ILoggerService, ILogService, NullLoggerService, NullLogService } from 'vs/platform/log/common/log'; import { ILabelService } from 'vs/platform/label/common/label'; import { timeout } from 'vs/base/common/async'; import { PaneComposite, PaneCompositeDescriptor } from 'vs/workbench/browser/panecomposite'; @@ -159,7 +159,7 @@ import { ExtensionIdentifier, ExtensionType, IExtension, IExtensionDescription, import { ISocketFactory } from 'vs/platform/remote/common/remoteAgentConnection'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { ILayoutOffsetInfo } from 'vs/platform/layout/browser/layoutService'; -import { IUserDataProfilesService, toUserDataProfile, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile, IUserDataProfilesService, toUserDataProfile, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService'; import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { EnablementState, IExtensionManagementServer, IScannedExtension, IWebExtensionsScannerService, IWorkbenchExtensionEnablementService, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; @@ -302,6 +302,7 @@ export function workbenchInstantiationService( instantiationService.stub(ITextFileService, overrides?.textFileService ? overrides.textFileService(instantiationService) : disposables.add(instantiationService.createInstance(TestTextFileService))); instantiationService.stub(IHostService, instantiationService.createInstance(TestHostService)); instantiationService.stub(ITextModelService, disposables.add(instantiationService.createInstance(TextModelResolverService))); + instantiationService.stub(ILoggerService, new NullLoggerService()); instantiationService.stub(ILogService, new NullLogService()); const editorGroupService = new TestEditorGroupsService([new TestEditorGroupView(0)]); instantiationService.stub(IEditorGroupsService, editorGroupService); @@ -479,7 +480,7 @@ class TestEnvironmentServiceWithArgs extends BrowserWorkbenchEnvironmentService args = []; } -export const TestEnvironmentService = new TestEnvironmentServiceWithArgs('', undefined!, Object.create(null), TestProductService); +export const TestEnvironmentService = new TestEnvironmentServiceWithArgs('', URI.file('tests').with({ scheme: 'vscode-tests' }), Object.create(null), TestProductService); export class TestProgressService implements IProgressService { @@ -1783,7 +1784,7 @@ export class TestTerminalEditorService implements ITerminalEditorService { detachActiveEditorInstance(): ITerminalInstance { throw new Error('Method not implemented.'); } detachInstance(instance: ITerminalInstance): void { throw new Error('Method not implemented.'); } splitInstance(instanceToSplit: ITerminalInstance, shellLaunchConfig?: IShellLaunchConfig): ITerminalInstance { throw new Error('Method not implemented.'); } - revealActiveEditor(preserveFocus?: boolean): void { throw new Error('Method not implemented.'); } + revealActiveEditor(preserveFocus?: boolean): Promise { throw new Error('Method not implemented.'); } resolveResource(instance: ITerminalInstance | URI): URI { throw new Error('Method not implemented.'); } reviveInput(deserializedInput: IDeserializedTerminalEditorInput): TerminalEditorInput { throw new Error('Method not implemented.'); } getInputFromResource(resource: URI): TerminalEditorInput { throw new Error('Method not implemented.'); } @@ -2012,6 +2013,7 @@ export class TestUserDataProfileService implements IUserDataProfileService { readonly onDidChangeCurrentProfile = Event.None; readonly currentProfile = toUserDataProfile('test', 'test', URI.file('tests').with({ scheme: 'vscode-tests' })); async updateCurrentProfile(): Promise { } + getShortName(profile: IUserDataProfile): string { return profile.shortName ?? profile.name; } } export class TestWebExtensionsScannerService implements IWebExtensionsScannerService { diff --git a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts index ac3c3da9155..fc06130a04a 100644 --- a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts @@ -188,7 +188,6 @@ export class TestSharedProcessService implements ISharedProcessService { } export class TestNativeHostService implements INativeHostService { - declare readonly _serviceBrand: undefined; readonly windowId = -1; @@ -276,6 +275,9 @@ export class TestNativeHostService implements INativeHostService { async hasClipboard(format: string, type?: 'selection' | 'clipboard' | undefined): Promise { return false; } async sendInputEvent(event: MouseInputEvent): Promise { } async windowsGetStringRegKey(hive: 'HKEY_CURRENT_USER' | 'HKEY_LOCAL_MACHINE' | 'HKEY_CLASSES_ROOT' | 'HKEY_USERS' | 'HKEY_CURRENT_CONFIG', path: string, name: string): Promise { return undefined; } + async startHeartbeat(): Promise { return false; } + async sendHeartbeat(): Promise { return false; } + async stopHeartbeat(): Promise { return false; } } export function workbenchInstantiationService(disposables = new DisposableStore()): ITestInstantiationService { diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 89d79d75b2e..01588f36305 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -128,21 +128,21 @@ import { IUserDataSyncLogService } from 'vs/platform/userDataSync/common/userDat import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog'; import { IExtensionsProfileScannerService, ExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; -registerSingleton(IUserDataSyncLogService, UserDataSyncLogService, true); -registerSingleton(IIgnoredExtensionsManagementService, IgnoredExtensionsManagementService, true); -registerSingleton(IGlobalExtensionEnablementService, GlobalExtensionEnablementService, true); -registerSingleton(IExtensionStorageService, ExtensionStorageService, true); -registerSingleton(IExtensionGalleryService, ExtensionGalleryService, true); -registerSingleton(IContextViewService, ContextViewService, true); -registerSingleton(IListService, ListService, true); +registerSingleton(IUserDataSyncLogService, UserDataSyncLogService, InstantiationType.Delayed); +registerSingleton(IIgnoredExtensionsManagementService, IgnoredExtensionsManagementService, InstantiationType.Delayed); +registerSingleton(IGlobalExtensionEnablementService, GlobalExtensionEnablementService, InstantiationType.Delayed); +registerSingleton(IExtensionStorageService, ExtensionStorageService, InstantiationType.Delayed); +registerSingleton(IExtensionGalleryService, ExtensionGalleryService, InstantiationType.Delayed); +registerSingleton(IContextViewService, ContextViewService, InstantiationType.Delayed); +registerSingleton(IListService, ListService, InstantiationType.Delayed); registerSingleton(IEditorWorkerService, EditorWorkerService, InstantiationType.Eager /* registers link detection and word based suggestions for any document */); -registerSingleton(IMarkerDecorationsService, MarkerDecorationsService, true); -registerSingleton(IMarkerService, MarkerService, true); -registerSingleton(IContextKeyService, ContextKeyService, true); -registerSingleton(ITextResourceConfigurationService, TextResourceConfigurationService, true); -registerSingleton(IDownloadService, DownloadService, true); -registerSingleton(IOpenerService, OpenerService, true); -registerSingleton(IExtensionsProfileScannerService, ExtensionsProfileScannerService, true); +registerSingleton(IMarkerDecorationsService, MarkerDecorationsService, InstantiationType.Delayed); +registerSingleton(IMarkerService, MarkerService, InstantiationType.Delayed); +registerSingleton(IContextKeyService, ContextKeyService, InstantiationType.Delayed); +registerSingleton(ITextResourceConfigurationService, TextResourceConfigurationService, InstantiationType.Delayed); +registerSingleton(IDownloadService, DownloadService, InstantiationType.Delayed); +registerSingleton(IOpenerService, OpenerService, InstantiationType.Delayed); +registerSingleton(IExtensionsProfileScannerService, ExtensionsProfileScannerService, InstantiationType.Delayed); //#endregion @@ -234,6 +234,7 @@ import 'vs/workbench/contrib/extensions/browser/extensions.contribution'; import 'vs/workbench/contrib/extensions/browser/extensionsViewlet'; // Output View +import 'vs/workbench/contrib/output/common/outputChannelModelService'; import 'vs/workbench/contrib/output/browser/output.contribution'; import 'vs/workbench/contrib/output/browser/outputView'; diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 187cb6c2a77..0bbbc48473b 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -46,7 +46,6 @@ import 'vs/workbench/services/lifecycle/electron-sandbox/lifecycleService'; import 'vs/workbench/services/title/electron-sandbox/titleService'; import 'vs/workbench/services/host/electron-sandbox/nativeHostService'; import 'vs/workbench/services/request/electron-sandbox/requestService'; -import 'vs/workbench/services/extensionResourceLoader/electron-sandbox/extensionResourceLoaderService'; import 'vs/workbench/services/clipboard/electron-sandbox/clipboardService'; import 'vs/workbench/services/contextmenu/electron-sandbox/contextmenuService'; import 'vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService'; @@ -61,6 +60,7 @@ import 'vs/workbench/services/encryption/electron-sandbox/encryptionService'; import 'vs/workbench/services/localization/electron-sandbox/languagePackService'; import 'vs/workbench/services/telemetry/electron-sandbox/telemetryService'; import 'vs/workbench/services/extensions/electron-sandbox/extensionHostStarter'; +import 'vs/platform/extensionResourceLoader/electron-sandbox/extensionResourceLoaderService'; import 'vs/platform/extensionManagement/electron-sandbox/extensionsScannerService'; import 'vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService'; import 'vs/workbench/services/extensionManagement/electron-sandbox/extensionTipsService'; @@ -79,16 +79,17 @@ import 'vs/workbench/services/tunnel/electron-sandbox/tunnelService'; import 'vs/platform/diagnostics/electron-sandbox/diagnosticsService'; import 'vs/platform/profiling/electron-sandbox/profilingService'; import 'vs/platform/telemetry/electron-sandbox/customEndpointTelemetryService'; +import 'vs/platform/remoteTunnel/electron-sandbox/remoteTunnelService'; import 'vs/workbench/services/files/electron-sandbox/elevatedFileService'; import 'vs/workbench/services/search/electron-sandbox/searchService'; import 'vs/workbench/services/workingCopy/electron-sandbox/workingCopyHistoryService'; import 'vs/workbench/services/userDataSync/browser/userDataSyncEnablementService'; import 'vs/workbench/services/extensions/electron-sandbox/sandboxExtensionService'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IUserDataInitializationService, UserDataInitializationService } from 'vs/workbench/services/userData/browser/userDataInit'; -registerSingleton(IUserDataInitializationService, UserDataInitializationService, true); +registerSingleton(IUserDataInitializationService, UserDataInitializationService, InstantiationType.Delayed); //#endregion @@ -132,9 +133,6 @@ import 'vs/workbench/contrib/themes/browser/themes.test.contribution'; // User Data Sync import 'vs/workbench/contrib/userDataSync/electron-sandbox/userDataSync.contribution'; -// Output -import 'vs/workbench/contrib/output/electron-sandbox/outputChannelModelService'; - // Tags import 'vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService'; import 'vs/workbench/contrib/tags/electron-sandbox/tags.contribution'; @@ -160,4 +158,7 @@ import 'vs/workbench/contrib/localHistory/electron-sandbox/localHistory.contribu // Merge Editor import 'vs/workbench/contrib/mergeEditor/electron-sandbox/mergeEditor.contribution'; +// Remote Tunnel +import 'vs/workbench/contrib/remoteTunnel/electron-sandbox/remoteTunnel.contribution'; + //#endregion diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index ec48c866af4..23b6577a3cc 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -53,7 +53,6 @@ import 'vs/workbench/services/dialogs/browser/fileDialogService'; import 'vs/workbench/services/host/browser/browserHostService'; import 'vs/workbench/services/lifecycle/browser/lifecycleService'; import 'vs/workbench/services/clipboard/browser/clipboardService'; -import 'vs/workbench/services/extensionResourceLoader/browser/extensionResourceLoaderService'; import 'vs/workbench/services/path/browser/pathService'; import 'vs/workbench/services/themes/browser/browserHostColorSchemeService'; import 'vs/workbench/services/encryption/browser/encryptionService'; @@ -64,6 +63,7 @@ import 'vs/workbench/services/workingCopy/browser/workingCopyHistoryService'; import 'vs/workbench/services/userDataSync/browser/webUserDataSyncEnablementService'; import 'vs/workbench/services/userDataSync/browser/userDataSyncProfilesStorageService'; import 'vs/workbench/services/configurationResolver/browser/configurationResolverService'; +import 'vs/platform/extensionResourceLoader/browser/extensionResourceLoaderService'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; @@ -73,8 +73,7 @@ import { IExtensionTipsService } from 'vs/platform/extensionManagement/common/ex import { ExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionTipsService'; import { IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagementService'; -import { ILoggerService, LogLevel } from 'vs/platform/log/common/log'; -import { FileLoggerService } from 'vs/platform/log/common/fileLog'; +import { LogLevel } from 'vs/platform/log/common/log'; import { UserDataSyncMachinesService, IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; import { IUserDataSyncStoreService, IUserDataSyncService, IUserDataAutoSyncService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; @@ -92,31 +91,27 @@ import { IDiagnosticsService, NullDiagnosticsService } from 'vs/platform/diagnos import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks'; import { WebLanguagePacksService } from 'vs/platform/languagePacks/browser/languagePacks'; -registerSingleton(IWorkbenchExtensionManagementService, ExtensionManagementService, true); -registerSingleton(IAccessibilityService, AccessibilityService, true); -registerSingleton(IContextMenuService, ContextMenuService, true); -registerSingleton(ILoggerService, FileLoggerService, true); -registerSingleton(IUserDataSyncStoreService, UserDataSyncStoreService, true); -registerSingleton(IUserDataSyncMachinesService, UserDataSyncMachinesService, true); -registerSingleton(IUserDataSyncBackupStoreService, UserDataSyncBackupStoreService, true); -registerSingleton(IUserDataSyncAccountService, UserDataSyncAccountService, true); -registerSingleton(IUserDataSyncService, UserDataSyncService, true); +registerSingleton(IWorkbenchExtensionManagementService, ExtensionManagementService, InstantiationType.Delayed); +registerSingleton(IAccessibilityService, AccessibilityService, InstantiationType.Delayed); +registerSingleton(IContextMenuService, ContextMenuService, InstantiationType.Delayed); +registerSingleton(IUserDataSyncStoreService, UserDataSyncStoreService, InstantiationType.Delayed); +registerSingleton(IUserDataSyncMachinesService, UserDataSyncMachinesService, InstantiationType.Delayed); +registerSingleton(IUserDataSyncBackupStoreService, UserDataSyncBackupStoreService, InstantiationType.Delayed); +registerSingleton(IUserDataSyncAccountService, UserDataSyncAccountService, InstantiationType.Delayed); +registerSingleton(IUserDataSyncService, UserDataSyncService, InstantiationType.Delayed); registerSingleton(IUserDataAutoSyncService, UserDataAutoSyncService, InstantiationType.Eager /* Eager to start auto sync */); registerSingleton(ITitleService, TitlebarPart, InstantiationType.Eager); -registerSingleton(IExtensionTipsService, ExtensionTipsService, true); -registerSingleton(ITimerService, TimerService, true); -registerSingleton(ICustomEndpointTelemetryService, NullEndpointTelemetryService, true); -registerSingleton(IDiagnosticsService, NullDiagnosticsService, true); -registerSingleton(ILanguagePackService, WebLanguagePacksService, true); +registerSingleton(IExtensionTipsService, ExtensionTipsService, InstantiationType.Delayed); +registerSingleton(ITimerService, TimerService, InstantiationType.Delayed); +registerSingleton(ICustomEndpointTelemetryService, NullEndpointTelemetryService, InstantiationType.Delayed); +registerSingleton(IDiagnosticsService, NullDiagnosticsService, InstantiationType.Delayed); +registerSingleton(ILanguagePackService, WebLanguagePacksService, InstantiationType.Delayed); //#endregion //#region --- workbench contributions -// Output -import 'vs/workbench/contrib/output/common/outputChannelModelService'; - // Logs import 'vs/workbench/contrib/logs/browser/logs.contribution'; diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index d1c831212b1..ebabd4c8827 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -3579,6 +3579,16 @@ declare module 'vscode' { iconPath?: Uri | { light: Uri; dark: Uri } | ThemeIcon; } + /** + * Additional data about a workspace edit. + */ + export interface WorkspaceEditMetadata { + /** + * Signal to the editor that this edit is a refactoring. + */ + isRefactoring?: boolean; + } + /** * A workspace edit is a collection of textual and files changes for * multiple resources and documents. @@ -3635,7 +3645,7 @@ declare module 'vscode' { * @param uri A resource identifier. * @param edits An array of edits. */ - set(uri: Uri, edits: NotebookEdit[]): void; + set(uri: Uri, edits: readonly NotebookEdit[]): void; /** * Set (and replace) notebook edits with metadata for a resource. @@ -3643,7 +3653,7 @@ declare module 'vscode' { * @param uri A resource identifier. * @param edits An array of edits. */ - set(uri: Uri, edits: [NotebookEdit, WorkspaceEditEntryMetadata][]): void; + set(uri: Uri, edits: ReadonlyArray<[NotebookEdit, WorkspaceEditEntryMetadata]>): void; /** * Set (and replace) text edits or snippet edits for a resource. @@ -3651,7 +3661,7 @@ declare module 'vscode' { * @param uri A resource identifier. * @param edits An array of edits. */ - set(uri: Uri, edits: (TextEdit | SnippetTextEdit)[]): void; + set(uri: Uri, edits: ReadonlyArray): void; /** * Set (and replace) text edits or snippet edits with metadata for a resource. @@ -3659,7 +3669,7 @@ declare module 'vscode' { * @param uri A resource identifier. * @param edits An array of edits. */ - set(uri: Uri, edits: [TextEdit | SnippetTextEdit, WorkspaceEditEntryMetadata][]): void; + set(uri: Uri, edits: ReadonlyArray<[TextEdit | SnippetTextEdit, WorkspaceEditEntryMetadata]>): void; /** * Get the text edits for a resource. @@ -3680,7 +3690,7 @@ declare module 'vscode' { * the file is being created with. * @param metadata Optional metadata for the entry. */ - createFile(uri: Uri, options?: { overwrite?: boolean; ignoreIfExists?: boolean; contents?: Uint8Array }, metadata?: WorkspaceEditEntryMetadata): void; + createFile(uri: Uri, options?: { readonly overwrite?: boolean; readonly ignoreIfExists?: boolean; readonly contents?: Uint8Array }, metadata?: WorkspaceEditEntryMetadata): void; /** * Delete a file or folder. @@ -3688,7 +3698,7 @@ declare module 'vscode' { * @param uri The uri of the file that is to be deleted. * @param metadata Optional metadata for the entry. */ - deleteFile(uri: Uri, options?: { recursive?: boolean; ignoreIfNotExists?: boolean }, metadata?: WorkspaceEditEntryMetadata): void; + deleteFile(uri: Uri, options?: { readonly recursive?: boolean; readonly ignoreIfNotExists?: boolean }, metadata?: WorkspaceEditEntryMetadata): void; /** * Rename a file or folder. @@ -3699,7 +3709,7 @@ declare module 'vscode' { * ignored. When overwrite and ignoreIfExists are both set overwrite wins. * @param metadata Optional metadata for the entry. */ - renameFile(oldUri: Uri, newUri: Uri, options?: { overwrite?: boolean; ignoreIfExists?: boolean }, metadata?: WorkspaceEditEntryMetadata): void; + renameFile(oldUri: Uri, newUri: Uri, options?: { readonly overwrite?: boolean; readonly ignoreIfExists?: boolean }, metadata?: WorkspaceEditEntryMetadata): void; /** * Get all text edits grouped by resource. @@ -10726,7 +10736,7 @@ declare module 'vscode' { * Whether the terminal process environment should be exactly as provided in * `TerminalOptions.env`. When this is false (default), the environment will be based on the * window's environment and also apply configured platform settings like - * `terminal.integrated.windows.env` on top. When this is true, the complete environment + * `terminal.integrated.env.windows` on top. When this is true, the complete environment * must be provided as nothing will be inherited from the process or any configuration. */ strictEnv?: boolean; @@ -12165,9 +12175,10 @@ declare module 'vscode' { * not be attempted, when a single edit fails. * * @param edit A workspace edit. + * @param metadata Optional {@link WorkspaceEditMetadata metadata} for the edit. * @return A thenable that resolves when the edit could be applied. */ - export function applyEdit(edit: WorkspaceEdit): Thenable; + export function applyEdit(edit: WorkspaceEdit, metadata?: WorkspaceEditMetadata): Thenable; /** * All text documents currently known to the editor. @@ -14817,6 +14828,26 @@ declare module 'vscode' { * If compact is true, debug sessions with a single child are hidden in the CALL STACK view to make the tree more compact. */ compact?: boolean; + + /** + * When true, a save will not be triggered for open editors when starting a debug session, regardless of the value of the `debug.saveBeforeStart` setting. + */ + suppressSaveBeforeStart?: boolean; + + /** + * When true, the debug toolbar will not be shown for this session. + */ + suppressDebugToolbar?: boolean; + + /** + * When true, the window statusbar color will not be changed for this session. + */ + suppressDebugStatusbar?: boolean; + + /** + * When true, the debug viewlet will not be automatically revealed for this session. + */ + suppressDebugView?: boolean; } /** @@ -15587,6 +15618,78 @@ declare module 'vscode' { export function registerAuthenticationProvider(id: string, label: string, provider: AuthenticationProvider, options?: AuthenticationProviderOptions): Disposable; } + /** + * Namespace for localization-related functionality in the extension API. To use this properly, + * you must have `l10n` defined in your `package.json` and have bundle.l10n..json files. + * For more information on how to generate bundle.l10n..json files, check out the + * [vscode-l10n repo](https://github.com/microsoft/vscode-l10n). + */ + export namespace l10n { + /** + * Marks a string for localization. If a localized bundle is available for the language specified by + * {@link env.language} and the bundle has a localized value for this message, then that localized + * value will be returned (with injected {@link args} values for any templated values). + * @param message - The message to localize. Supports index templating where strings like `{0}` and `{1}` are + * replaced by the item at that index in the {@link args} array. + * @param args - The arguments to be used in the localized string. The index of the argument is used to + * match the template placeholder in the localized string. + * @returns localized string with injected arguments. + * @example `l10n.localize('hello', 'Hello {0}!', 'World');` + */ + export function t(message: string, ...args: Array): string; + + /** + * Marks a string for localization. If a localized bundle is available for the language specified by + * {@link env.language} and the bundle has a localized value for this message, then that localized + * value will be returned (with injected {@link args} values for any templated values). + * @param message The message to localize. Supports named templating where strings like `{foo}` and `{bar}` are + * replaced by the value in the Record for that key (foo, bar, etc). + * @param args The arguments to be used in the localized string. The name of the key in the record is used to + * match the template placeholder in the localized string. + * @returns localized string with injected arguments. + * @example `l10n.t('Hello {name}', { name: 'Erich' });` + */ + export function t(message: string, args: Record): string; + /** + * Marks a string for localization. If a localized bundle is available for the language specified by + * {@link env.language} and the bundle has a localized value for this message, then that localized + * value will be returned (with injected args values for any templated values). + * @param options The options to use when localizing the message. + * @returns localized string with injected arguments. + */ + export function t(options: { + /** + * The message to localize. If {@link args} is an array, this message supports index templating where strings like + * `{0}` and `{1}` are replaced by the item at that index in the {@link args} array. If `args` is a `Record`, + * this supports named templating where strings like `{foo}` and `{bar}` are replaced by the value in + * the Record for that key (foo, bar, etc). + */ + message: string; + /** + * The arguments to be used in the localized string. As an array, the index of the argument is used to + * match the template placeholder in the localized string. As a Record, the key is used to match the template + * placeholder in the localized string. + */ + args?: Array | Record; + /** + * A comment to help translators understand the context of the message. + */ + comment: string | string[]; + }): string; + /** + * The bundle of localized strings that have been loaded for the extension. + * It's undefined if no bundle has been loaded. The bundle is typically not loaded if + * there was no bundle found or when we are running with the default language. + */ + export const bundle: { [key: string]: string } | undefined; + /** + * The URI of the localization bundle that has been loaded for the extension. + * It's undefined if no bundle has been loaded. The bundle is typically not loaded if + * there was no bundle found or when we are running with the default language. + */ + export const uri: Uri | undefined; + } + /** * Namespace for testing functionality. Tests are published by registering * {@link TestController} instances, then adding {@link TestItem TestItems}. @@ -15818,9 +15921,9 @@ declare module 'vscode' { /** * A TestRunRequest is a precursor to a {@link TestRun}, which in turn is - * created by passing a request to {@link tests.runTests}. The TestRunRequest - * contains information about which tests should be run, which should not be - * run, and how they are run (via the {@link TestRunRequest.profile profile}). + * created by passing a request to {@link TestController.createTestRun}. The + * TestRunRequest contains information about which tests should be run, which + * should not be run, and how they are run (via the {@link TestRunRequest.profile profile}). * * In general, TestRunRequests are created by the editor and pass to * {@link TestRunProfile.runHandler}, however you can also create test @@ -15863,7 +15966,8 @@ declare module 'vscode' { } /** - * Options given to {@link TestController.runTests} + * A TestRun represents an in-progress or completed test run and + * provides methods to report the state of individual tests in the run. */ export interface TestRun { /** diff --git a/src/vscode-dts/vscode.proposed.contribNotebookStaticPreloads.d.ts b/src/vscode-dts/vscode.proposed.contribNotebookStaticPreloads.d.ts new file mode 100644 index 00000000000..b01c68ebabb --- /dev/null +++ b/src/vscode-dts/vscode.proposed.contribNotebookStaticPreloads.d.ts @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// empty placeholder declaration for the `notebookPreload` contribution point diff --git a/src/vscode-dts/vscode.proposed.extensionLog.d.ts b/src/vscode-dts/vscode.proposed.extensionLog.d.ts index e575defff20..12d7689ec57 100644 --- a/src/vscode-dts/vscode.proposed.extensionLog.d.ts +++ b/src/vscode-dts/vscode.proposed.extensionLog.d.ts @@ -5,10 +5,45 @@ declare module 'vscode' { + export enum LogLevel { + Trace = 0, + Debug = 1, + Info = 2, + Warning = 3, + Error = 4, + Critical = 5, + Off = 6 + } + + export namespace env { + + /** + * The current log level of the application. + */ + export const logLevel: LogLevel; + + /** + * An {@link Event} which fires when the log level of the application changes. + */ + export const onDidChangeLogLevel: Event; + + } + /** * A channel for containing log output. */ export interface LogOutputChannel extends OutputChannel { + + /** + * The current log level of the channel. + */ + readonly logLevel: LogLevel; + + /** + * An {@link Event} which fires when the log level of the channel changes. + */ + readonly onDidChangeLogLevel: Event; + /** * Log the given trace message to the channel. * diff --git a/src/vscode-dts/vscode.proposed.localization.d.ts b/src/vscode-dts/vscode.proposed.localization.d.ts deleted file mode 100644 index e57d4477b74..00000000000 --- a/src/vscode-dts/vscode.proposed.localization.d.ts +++ /dev/null @@ -1,25 +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 'vscode' { - export namespace l10n { - /** - * A string that can be pulled out of a localization bundle if it exists. - */ - export function t(message: string, ...args: any[]): string; - /** - * A string that can be pulled out of a localization bundle if it exists. - */ - export function t(options: { message: string; args?: any[]; comment: string[] }): string; - /** - * The bundle of localized strings that have been loaded for the extension. - */ - export const bundle: { [key: string]: string }; - /** - * The URI of the localization bundle that has been loaded for the extension. - */ - export const uri: Uri | undefined; - } -} diff --git a/src/vscode-dts/vscode.proposed.notebookDebugOptions.d.ts b/src/vscode-dts/vscode.proposed.notebookDebugOptions.d.ts deleted file mode 100644 index a05e21671b3..00000000000 --- a/src/vscode-dts/vscode.proposed.notebookDebugOptions.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module 'vscode' { - - // eslint-disable-next-line local/vscode-dts-region-comments - // @roblourens: debugUI.simple: https://github.com/microsoft/vscode/issues/147264. Used for Jupyter's Run By Line. - // suppressSaveBeforeStart: https://github.com/microsoft/vscode/issues/147263. Used to enable debugging untitled/unsaved notebooks. - - /** - * Options for {@link debug.startDebugging starting a debug session}. - */ - export interface DebugSessionOptions { - - debugUI?: { - /** - * When true, the debug toolbar will not be shown for this session, the window statusbar color will not be changed, and the debug viewlet will not be automatically revealed. - */ - simple?: boolean; - }; - - /** - * When true, a save will not be triggered for open editors when starting a debug session, regardless of the value of the `debug.saveBeforeStart` setting. - */ - suppressSaveBeforeStart?: boolean; - } -} diff --git a/src/vscode-dts/vscode.proposed.resolvers.d.ts b/src/vscode-dts/vscode.proposed.resolvers.d.ts index 292b5a5fb23..c1c413bc31f 100644 --- a/src/vscode-dts/vscode.proposed.resolvers.d.ts +++ b/src/vscode-dts/vscode.proposed.resolvers.d.ts @@ -43,7 +43,7 @@ declare module 'vscode' { label: string; } - export interface TunnelOptions { + interface TunnelOptions { remoteAddress: { port: number; host: string }; // The desired local port. If this port can't be used, then another will be chosen. localAddressPort?: number; @@ -56,7 +56,7 @@ declare module 'vscode' { protocol?: string; } - export interface TunnelDescription { + interface TunnelDescription { remoteAddress: { port: number; host: string }; //The complete local address(ex. localhost:1234) localAddress: { port: number; host: string } | string; @@ -69,7 +69,7 @@ declare module 'vscode' { protocol?: string; } - export interface Tunnel extends TunnelDescription { + interface Tunnel extends TunnelDescription { // Implementers of Tunnel should fire onDidDispose when dispose is called. onDidDispose: Event; dispose(): void | Thenable; @@ -164,29 +164,6 @@ declare module 'vscode' { candidatePortSource?: CandidatePortSource; } - export namespace workspace { - /** - * Forwards a port. If the current resolver implements RemoteAuthorityResolver:forwardPort then that will be used to make the tunnel. - * By default, openTunnel only support localhost; however, RemoteAuthorityResolver:tunnelFactory can be used to support other ips. - * - * @throws When run in an environment without a remote. - * - * @param tunnelOptions The `localPort` is a suggestion only. If that port is not available another will be chosen. - */ - export function openTunnel(tunnelOptions: TunnelOptions): Thenable; - - /** - * Gets an array of the currently available tunnels. This does not include environment tunnels, only tunnels that have been created by the user. - * Note that these are of type TunnelDescription and cannot be disposed. - */ - // export let tunnels: Thenable; - - /** - * Fired when the list of tunnels has changed. - */ - export const onDidChangeTunnels: Event; - } - export interface ResourceLabelFormatter { scheme: string; authority?: string; diff --git a/src/vscode-dts/vscode.proposed.telemetryLog.d.ts b/src/vscode-dts/vscode.proposed.telemetryLog.d.ts deleted file mode 100644 index 962016a02d8..00000000000 --- a/src/vscode-dts/vscode.proposed.telemetryLog.d.ts +++ /dev/null @@ -1,19 +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 'vscode' { - - export namespace window { - /** - * Logs a telemetry event to a shared extension output channel when the log level is set to trace. - * This is similar in function to cores' telemetry output channel that can be seen when log level is set to trace. - * Extension authors should only log to the output channel when sending telemetry. - * - * @param eventName The name of the telemetry event - * @param data The data associated with the telemetry event - */ - export function logTelemetryToOutputChannel(eventName: string, data: Record): void; - } -} diff --git a/src/vscode-dts/vscode.proposed.telemetryLogger.d.ts b/src/vscode-dts/vscode.proposed.telemetryLogger.d.ts new file mode 100644 index 00000000000..94a9a0a186e --- /dev/null +++ b/src/vscode-dts/vscode.proposed.telemetryLogger.d.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. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + export interface TelemetryLogger { + //TODO feels weird having this on all loggers + readonly onDidChangeEnableStates: Event; + readonly isUsageEnabled: boolean; + readonly isErrorsEnabled: boolean; + + /** + * After completing cleaning, telemetry setting checks, and data mix-in calls `TelemetryAppender.logEvent` to log the event. + * Automatically supports echoing to extension telemetry output channel. + * @param eventName The event name to log + * @param data The data to log + */ + logUsage(eventName: string, data?: Record): void; + + /** + * After completing cleaning, telemetry setting checks, and data mix-in calls `TelemetryAppender.logEvent` to log the event. Differs from `logUsage` in that it will log the event if the telemetry setting is Error+. + * Automatically supports echoing to extension telemetry output channel. + * @param eventName The event name to log + * @param data The data to log + */ + logError(eventName: string, data?: Record): void; + + /** + * Calls `TelemetryAppender.logException`. Does cleaning, telemetry checks, and data mix-in. + * Automatically supports echoing to extension telemetry output channel. + * Will also automatically log any exceptions thrown within the extension host process. + * @param exception The error object which contains the stack trace cleaned of PII + * @param data Additional data to log alongside the stack trace + */ + logError(exception: Error, data?: Record): void; + + dispose(): void; + } + + export interface TelemetryAppender { + /** + * Whether or not you want to avoid having the built-in common properties such as os, extension name, etc injected into the data object. + */ + readonly ignoreBuiltInCommonProperties: boolean; + + /** + * Any additional common properties which should be injected into the data object. + */ + readonly additionalCommonProperties?: Record; + + /** + * User-defined function which logs an event, used within the TelemetryLogger + * @param eventName The name of the event which you are logging + * @param data A serializable key value pair that is being logged + */ + logEvent(eventName: string, data?: Record): void; + + /** + * User-defined function which logs an error, used within the TelemetryLogger + * @param exception The exception being logged + * @param data Any additional data to be collected with the exception + */ + logException(exception: Error, data?: Record): void; + + /** + * Optional flush function which will give your appender one last chance to send any remaining events as the TelemetryLogger is being disposed + */ + flush?(): void | Thenable; + } + + export namespace env { + /** + * A wrapper around a TelemetryAppender which provides built-in setting checks, common properties, data cleaning, output channel logging, and internal ext host process exception catching. + * @param appender The core piece which we call when it is time to log telemetry. It is highly recommended that you don't call the methods within the appender directly as the logger provides extra guards and cleaning. + * @returns An instantiated telemetry logger which you can use for recording telemetry + */ + export function createTelemetryLogger(appender: TelemetryAppender): TelemetryLogger; + } +} diff --git a/src/vscode-dts/vscode.proposed.tunnels.d.ts b/src/vscode-dts/vscode.proposed.tunnels.d.ts index 4ff7a18f037..1f83bbbeb90 100644 --- a/src/vscode-dts/vscode.proposed.tunnels.d.ts +++ b/src/vscode-dts/vscode.proposed.tunnels.d.ts @@ -12,6 +12,10 @@ declare module 'vscode' { // The desired local port. If this port can't be used, then another will be chosen. localAddressPort?: number; label?: string; + /** + * @deprecated Use privacy instead + */ + public?: boolean; privacy?: string; protocol?: string; } @@ -20,6 +24,10 @@ declare module 'vscode' { remoteAddress: { port: number; host: string }; //The complete local address(ex. localhost:1234) localAddress: { port: number; host: string } | string; + /** + * @deprecated Use privacy instead + */ + public?: boolean; privacy?: string; // If protocol is not provided it is assumed to be http, regardless of the localAddress. protocol?: string; @@ -51,6 +59,6 @@ declare module 'vscode' { /** * Fired when the list of tunnels has changed. */ - // export const onDidChangeTunnels: Event; + export const onDidChangeTunnels: Event; } } diff --git a/src/vscode-dts/vscode.proposed.workspaceEditIsRefactoring.d.ts b/src/vscode-dts/vscode.proposed.workspaceEditIsRefactoring.d.ts deleted file mode 100644 index 2589a016e4c..00000000000 --- a/src/vscode-dts/vscode.proposed.workspaceEditIsRefactoring.d.ts +++ /dev/null @@ -1,27 +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 'vscode' { - - // https://github.com/microsoft/vscode/issues/112109 - - /** - * Additional data about a workspace edit. - */ - export interface WorkspaceEditMetadata { - /** - * Signal to the editor that this edit is a refactoring. - */ - isRefactoring?: boolean; - } - - export namespace workspace { - - /** - * @param metadata Optional {@link WorkspaceEditMetadata metadata} for the edit. - */ - export function applyEdit(edit: WorkspaceEdit, metadata?: WorkspaceEditMetadata): Thenable; - } -} diff --git a/test/automation/package.json b/test/automation/package.json index 239844d7df5..4ff47338bab 100644 --- a/test/automation/package.json +++ b/test/automation/package.json @@ -33,4 +33,4 @@ "npm-run-all": "^4.1.5", "watch": "^1.0.2" } -} \ No newline at end of file +} diff --git a/test/automation/src/electron.ts b/test/automation/src/electron.ts index f818b2b8136..1fea286c6cf 100644 --- a/test/automation/src/electron.ts +++ b/test/automation/src/electron.ts @@ -49,6 +49,15 @@ export async function resolveElectronConfiguration(options: LaunchOptions): Prom args.push('--disable-dev-shm-usage'); } + if (process.platform === 'darwin') { + // On macOS force software based rendering since we are seeing GPU process + // hangs when initializing GL context. This is very likely possible + // that there are new displays available in the CI hardware and + // the relevant drivers couldn't be loaded via the GPU sandbox. + // TODO(deepak1556): remove this switch with Electron update. + args.push('--use-gl=swiftshader'); + } + if (remote) { // Replace workspace path with URI args[0] = `--${workspacePath.endsWith('.code-workspace') ? 'file' : 'folder'}-uri=vscode-remote://test+test/${URI.file(workspacePath).path}`; diff --git a/test/automation/yarn.lock b/test/automation/yarn.lock index 97df3a29fb2..3cfb4e7b414 100644 --- a/test/automation/yarn.lock +++ b/test/automation/yarn.lock @@ -22,9 +22,9 @@ integrity sha512-aK9jxMypeSrhiYofWWBf/T7O+KwaiAHzM4sveCdWPn71lzUSMimRnKzhXDKfKwV1kWoBo2P1aGgaIYGLf9/ljw== "@types/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== + version "16.11.64" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.64.tgz#9171f327298b619e2c52238b120c19056415d820" + integrity sha512-z5hPTlVFzNwtJ2LNozTpJcD1Cu44c4LNuzaq1mwxmiHWQh2ULdR6Vjwo1UGldzRpzL0yUEdZddnfqGW2G70z6Q== "@types/tmp@0.2.2": version "0.2.2" diff --git a/test/integration/browser/yarn.lock b/test/integration/browser/yarn.lock index b3498682cd8..1056ba89b05 100644 --- a/test/integration/browser/yarn.lock +++ b/test/integration/browser/yarn.lock @@ -34,9 +34,9 @@ integrity sha512-GnZbirvmqZUzMgkFn70c74OQpTTUcCzlhQliTzYjQMqg+hVKcDnxdL19Ne3UdYzdMA/+W3eb646FWn/ZaT1NfQ== "@types/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== + version "16.11.64" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.64.tgz#9171f327298b619e2c52238b120c19056415d820" + integrity sha512-z5hPTlVFzNwtJ2LNozTpJcD1Cu44c4LNuzaq1mwxmiHWQh2ULdR6Vjwo1UGldzRpzL0yUEdZddnfqGW2G70z6Q== "@types/optimist@0.0.29": version "0.0.29" diff --git a/test/smoke/src/areas/search/search.test.ts b/test/smoke/src/areas/search/search.test.ts index 28796e9b0d7..92c75f14404 100644 --- a/test/smoke/src/areas/search/search.test.ts +++ b/test/smoke/src/areas/search/search.test.ts @@ -29,14 +29,16 @@ export function setup(logger: Logger) { it('searches only for *.js files & checks for correct result number', async function () { const app = this.app as Application; - await app.workbench.search.searchFor('body'); - await app.workbench.search.showQueryDetails(); - await app.workbench.search.setFilesToIncludeText('*.js'); - await app.workbench.search.submitSearch(); + try { + await app.workbench.search.setFilesToIncludeText('*.js'); + await app.workbench.search.searchFor('body'); + await app.workbench.search.showQueryDetails(); - await app.workbench.search.waitForResultText('4 results in 1 file'); - await app.workbench.search.setFilesToIncludeText(''); - await app.workbench.search.hideQueryDetails(); + await app.workbench.search.waitForResultText('4 results in 1 file'); + } finally { + await app.workbench.search.setFilesToIncludeText(''); + await app.workbench.search.hideQueryDetails(); + } }); it('dismisses result & checks for correct result number', async function () { diff --git a/test/smoke/yarn.lock b/test/smoke/yarn.lock index e73fb8b7865..42f75ad058a 100644 --- a/test/smoke/yarn.lock +++ b/test/smoke/yarn.lock @@ -59,9 +59,9 @@ integrity sha512-uM4mnmsIIPK/yeO+42F2RQhGUIs39K2RFmugcJANppXe6J1nvH87PvzPZYpza7Xhhs8Yn9yIAVdLZ84z61+0xQ== "@types/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== + version "16.11.64" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.64.tgz#9171f327298b619e2c52238b120c19056415d820" + integrity sha512-z5hPTlVFzNwtJ2LNozTpJcD1Cu44c4LNuzaq1mwxmiHWQh2ULdR6Vjwo1UGldzRpzL0yUEdZddnfqGW2G70z6Q== "@types/rimraf@3.0.2": version "3.0.2" diff --git a/test/unit/electron/renderer.js b/test/unit/electron/renderer.js index 9b291fd0bd7..47f8fb0a53e 100644 --- a/test/unit/electron/renderer.js +++ b/test/unit/electron/renderer.js @@ -298,7 +298,7 @@ function runTests(opts) { mocha.grep(opts.grep); } - if (!opts.debug) { + if (!opts.dev) { mocha.reporter(IPCReporter); } @@ -308,7 +308,7 @@ function runTests(opts) { }); }); - if (opts.debug) { + if (opts.dev) { runner.on('fail', (test, err) => { console.error(test.fullTitle()); diff --git a/yarn.lock b/yarn.lock index 99c9297fccf..03376126fba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1011,9 +1011,9 @@ integrity sha512-LXRap3bb4AjtLZ5NOFc4ssVZrQPTgdPcNm++0SEJuJZaOA+xHkojJNYqy33A5q/94BmG5tA6yaMeD4VdCv5aSA== "@types/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== + version "16.11.64" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.64.tgz#9171f327298b619e2c52238b120c19056415d820" + integrity sha512-z5hPTlVFzNwtJ2LNozTpJcD1Cu44c4LNuzaq1mwxmiHWQh2ULdR6Vjwo1UGldzRpzL0yUEdZddnfqGW2G70z6Q== "@types/node@^10.11.7": version "10.12.21" @@ -1156,137 +1156,91 @@ dependencies: "@types/node" "*" -"@typescript-eslint/eslint-plugin@^5.10.0": - version "5.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.10.0.tgz#e90afea96dff8620892ad216b0e4ccdf8ee32d3a" - integrity sha512-XXVKnMsq2fuu9K2KsIxPUGqb6xAImz8MEChClbXmE3VbveFtBUU5bzM6IPVWqzyADIgdkS2Ws/6Xo7W2TeZWjQ== +"@typescript-eslint/eslint-plugin@^5.39.0": + version "5.39.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.39.0.tgz#778b2d9e7f293502c7feeea6c74dca8eb3e67511" + integrity sha512-xVfKOkBm5iWMNGKQ2fwX5GVgBuHmZBO1tCRwXmY5oAIsPscfwm2UADDuNB8ZVYCtpQvJK4xpjrK7jEhcJ0zY9A== dependencies: - "@typescript-eslint/scope-manager" "5.10.0" - "@typescript-eslint/type-utils" "5.10.0" - "@typescript-eslint/utils" "5.10.0" - debug "^4.3.2" - functional-red-black-tree "^1.0.1" - ignore "^5.1.8" + "@typescript-eslint/scope-manager" "5.39.0" + "@typescript-eslint/type-utils" "5.39.0" + "@typescript-eslint/utils" "5.39.0" + debug "^4.3.4" + ignore "^5.2.0" regexpp "^3.2.0" - semver "^7.3.5" + semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/experimental-utils@^5.10.0": - version "5.35.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.35.1.tgz#4ec46ad8cd04001e44536f427c553f120a262c9b" - integrity sha512-nF7JD9alMkhEx50QYDUdP8koeHtldnm7EfZkr68ikkc87ffFBIPkH3dqoWyOeQeIiJicB0uHzpMXKR6PP+1Jbg== +"@typescript-eslint/experimental-utils@^5.39.0": + version "5.39.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.39.0.tgz#9263bb72b57449cc2f07ffb7fd4e12d0160b7f5e" + integrity sha512-n5N9kG/oGu2xXhHzsWzn94s6CWoiUj59FPU2dF2IQZxPftw+q6Jm5sV2vj5qTgAElRooHhrgtl2gxBQDCPt6WA== dependencies: - "@typescript-eslint/utils" "5.35.1" + "@typescript-eslint/utils" "5.39.0" -"@typescript-eslint/parser@^5.10.0": - version "5.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.10.0.tgz#8f59e036f5f1cffc178cacbd5ccdd02aeb96c91c" - integrity sha512-pJB2CCeHWtwOAeIxv8CHVGJhI5FNyJAIpx5Pt72YkK3QfEzt6qAlXZuyaBmyfOdM62qU0rbxJzNToPTVeJGrQw== +"@typescript-eslint/parser@^5.39.0": + version "5.39.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.39.0.tgz#93fa0bc980a3a501e081824f6097f7ca30aaa22b" + integrity sha512-PhxLjrZnHShe431sBAGHaNe6BDdxAASDySgsBCGxcBecVCi8NQWxQZMcizNA4g0pN51bBAn/FUfkWG3SDVcGlA== dependencies: - "@typescript-eslint/scope-manager" "5.10.0" - "@typescript-eslint/types" "5.10.0" - "@typescript-eslint/typescript-estree" "5.10.0" - debug "^4.3.2" + "@typescript-eslint/scope-manager" "5.39.0" + "@typescript-eslint/types" "5.39.0" + "@typescript-eslint/typescript-estree" "5.39.0" + debug "^4.3.4" -"@typescript-eslint/scope-manager@5.10.0": - version "5.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.10.0.tgz#bb5d872e8b9e36203908595507fbc4d3105329cb" - integrity sha512-tgNgUgb4MhqK6DoKn3RBhyZ9aJga7EQrw+2/OiDk5hKf3pTVZWyqBi7ukP+Z0iEEDMF5FDa64LqODzlfE4O/Dg== +"@typescript-eslint/scope-manager@5.39.0": + version "5.39.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.39.0.tgz#873e1465afa3d6c78d8ed2da68aed266a08008d0" + integrity sha512-/I13vAqmG3dyqMVSZPjsbuNQlYS082Y7OMkwhCfLXYsmlI0ca4nkL7wJ/4gjX70LD4P8Hnw1JywUVVAwepURBw== dependencies: - "@typescript-eslint/types" "5.10.0" - "@typescript-eslint/visitor-keys" "5.10.0" + "@typescript-eslint/types" "5.39.0" + "@typescript-eslint/visitor-keys" "5.39.0" -"@typescript-eslint/scope-manager@5.35.1": - version "5.35.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.35.1.tgz#ccb69d54b7fd0f2d0226a11a75a8f311f525ff9e" - integrity sha512-kCYRSAzIW9ByEIzmzGHE50NGAvAP3wFTaZevgWva7GpquDyFPFcmvVkFJGWJJktg/hLwmys/FZwqM9EKr2u24Q== +"@typescript-eslint/type-utils@5.39.0": + version "5.39.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.39.0.tgz#0a8c00f95dce4335832ad2dc6bc431c14e32a0a6" + integrity sha512-KJHJkOothljQWzR3t/GunL0TPKY+fGJtnpl+pX+sJ0YiKTz3q2Zr87SGTmFqsCMFrLt5E0+o+S6eQY0FAXj9uA== dependencies: - "@typescript-eslint/types" "5.35.1" - "@typescript-eslint/visitor-keys" "5.35.1" - -"@typescript-eslint/type-utils@5.10.0": - version "5.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.10.0.tgz#8524b9479c19c478347a7df216827e749e4a51e5" - integrity sha512-TzlyTmufJO5V886N+hTJBGIfnjQDQ32rJYxPaeiyWKdjsv2Ld5l8cbS7pxim4DeNs62fKzRSt8Q14Evs4JnZyQ== - dependencies: - "@typescript-eslint/utils" "5.10.0" - debug "^4.3.2" + "@typescript-eslint/typescript-estree" "5.39.0" + "@typescript-eslint/utils" "5.39.0" + debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.10.0": - version "5.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.10.0.tgz#beb3cb345076f5b088afe996d57bcd1dfddaa75c" - integrity sha512-wUljCgkqHsMZbw60IbOqT/puLfyqqD5PquGiBo1u1IS3PLxdi3RDGlyf032IJyh+eQoGhz9kzhtZa+VC4eWTlQ== +"@typescript-eslint/types@5.39.0": + version "5.39.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.39.0.tgz#f4e9f207ebb4579fd854b25c0bf64433bb5ed78d" + integrity sha512-gQMZrnfEBFXK38hYqt8Lkwt8f4U6yq+2H5VDSgP/qiTzC8Nw8JO3OuSUOQ2qW37S/dlwdkHDntkZM6SQhKyPhw== -"@typescript-eslint/types@5.35.1": - version "5.35.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.35.1.tgz#af355fe52a0cc88301e889bc4ada72f279b63d61" - integrity sha512-FDaujtsH07VHzG0gQ6NDkVVhi1+rhq0qEvzHdJAQjysN+LHDCKDKCBRlZFFE0ec0jKxiv0hN63SNfExy0KrbQQ== - -"@typescript-eslint/typescript-estree@5.10.0": - version "5.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.0.tgz#4be24a3dea0f930bb1397c46187d0efdd955a224" - integrity sha512-x+7e5IqfwLwsxTdliHRtlIYkgdtYXzE0CkFeV6ytAqq431ZyxCFzNMNR5sr3WOlIG/ihVZr9K/y71VHTF/DUQA== +"@typescript-eslint/typescript-estree@5.39.0": + version "5.39.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.39.0.tgz#c0316aa04a1a1f4f7f9498e3c13ef1d3dc4cf88b" + integrity sha512-qLFQP0f398sdnogJoLtd43pUgB18Q50QSA+BTE5h3sUxySzbWDpTSdgt4UyxNSozY/oDK2ta6HVAzvGgq8JYnA== dependencies: - "@typescript-eslint/types" "5.10.0" - "@typescript-eslint/visitor-keys" "5.10.0" - debug "^4.3.2" - globby "^11.0.4" - is-glob "^4.0.3" - semver "^7.3.5" - tsutils "^3.21.0" - -"@typescript-eslint/typescript-estree@5.35.1": - version "5.35.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.35.1.tgz#db878a39a0dbdc9bb133f11cdad451770bfba211" - integrity sha512-JUqE1+VRTGyoXlDWWjm6MdfpBYVq+hixytrv1oyjYIBEOZhBCwtpp5ZSvBt4wIA1MKWlnaC2UXl2XmYGC3BoQA== - dependencies: - "@typescript-eslint/types" "5.35.1" - "@typescript-eslint/visitor-keys" "5.35.1" + "@typescript-eslint/types" "5.39.0" + "@typescript-eslint/visitor-keys" "5.39.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.10.0": - version "5.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.10.0.tgz#c3d152a85da77c400e37281355561c72fb1b5a65" - integrity sha512-IGYwlt1CVcFoE2ueW4/ioEwybR60RAdGeiJX/iDAw0t5w0wK3S7QncDwpmsM70nKgGTuVchEWB8lwZwHqPAWRg== +"@typescript-eslint/utils@5.39.0": + version "5.39.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.39.0.tgz#b7063cca1dcf08d1d21b0d91db491161ad0be110" + integrity sha512-+DnY5jkpOpgj+EBtYPyHRjXampJfC0yUZZzfzLuUWVZvCuKqSdJVC8UhdWipIw7VKNTfwfAPiOWzYkAwuIhiAg== dependencies: "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.10.0" - "@typescript-eslint/types" "5.10.0" - "@typescript-eslint/typescript-estree" "5.10.0" + "@typescript-eslint/scope-manager" "5.39.0" + "@typescript-eslint/types" "5.39.0" + "@typescript-eslint/typescript-estree" "5.39.0" eslint-scope "^5.1.1" eslint-utils "^3.0.0" -"@typescript-eslint/utils@5.35.1": - version "5.35.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.35.1.tgz#ae1399afbfd6aa7d0ed1b7d941e9758d950250eb" - integrity sha512-v6F8JNXgeBWI4pzZn36hT2HXXzoBBBJuOYvoQiaQaEEjdi5STzux3Yj8v7ODIpx36i/5s8TdzuQ54TPc5AITQQ== +"@typescript-eslint/visitor-keys@5.39.0": + version "5.39.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.39.0.tgz#8f41f7d241b47257b081ddba5d3ce80deaae61e2" + integrity sha512-yyE3RPwOG+XJBLrhvsxAidUgybJVQ/hG8BhiJo0k8JSAYfk/CshVcxf0HwP4Jt7WZZ6vLmxdo1p6EyN3tzFTkg== dependencies: - "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.35.1" - "@typescript-eslint/types" "5.35.1" - "@typescript-eslint/typescript-estree" "5.35.1" - eslint-scope "^5.1.1" - eslint-utils "^3.0.0" - -"@typescript-eslint/visitor-keys@5.10.0": - version "5.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.0.tgz#770215497ad67cd15a572b52089991d5dfe06281" - integrity sha512-GMxj0K1uyrFLPKASLmZzCuSddmjZVbVj3Ouy5QVuIGKZopxvOr24JsS7gruz6C3GExE01mublZ3mIBOaon9zuQ== - dependencies: - "@typescript-eslint/types" "5.10.0" - eslint-visitor-keys "^3.0.0" - -"@typescript-eslint/visitor-keys@5.35.1": - version "5.35.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.35.1.tgz#285e9e34aed7c876f16ff646a3984010035898e6" - integrity sha512-cEB1DvBVo1bxbW/S5axbGPE6b7FIMAbo3w+AGq6zNDA7+NYJOIkKj/sInfTv4edxd4PxJSgdN4t6/pbvgA+n5g== - dependencies: - "@typescript-eslint/types" "5.35.1" + "@typescript-eslint/types" "5.39.0" eslint-visitor-keys "^3.3.0" "@ungap/promise-all-settled@1.1.2": @@ -1299,6 +1253,18 @@ resolved "https://registry.yarnpkg.com/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.0.tgz#d2f1e0664ee6036408f9743fee264ea0699b0e48" integrity sha512-bRRFxLfg5dtAyl5XyiVWz/ZBPahpOpPrNYnnHpOpUZvam4tKH35wdhP4Kj6PbM0+KdliOsPzbGWpkxcdpNB/sg== +"@vscode/l10n-dev@0.0.15": + version "0.0.15" + resolved "https://registry.yarnpkg.com/@vscode/l10n-dev/-/l10n-dev-0.0.15.tgz#677b527987ccd39e32c50956f139736a788061d6" + integrity sha512-zLuo/pa+FtnFrVq/7M8VHshgejNZ6TvnRW9/um1pLkg92PZ9glDgmwXUv1AdpBu5KNzgH9odiMKS4YQDkS12wQ== + dependencies: + deepmerge-json "^1.5.0" + glob "^8.0.3" + pseudo-localization "^2.4.0" + typescript "^4.7.4" + xml2js "^0.4.23" + yargs "^17.5.1" + "@vscode/ripgrep@^1.14.2": version "1.14.2" resolved "https://registry.yarnpkg.com/@vscode/ripgrep/-/ripgrep-1.14.2.tgz#47c0eec2b64f53d8f7e1b5ffd22a62e229191c34" @@ -2827,6 +2793,15 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + clone-buffer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" @@ -3637,6 +3612,11 @@ deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= +deepmerge-json@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/deepmerge-json/-/deepmerge-json-1.5.0.tgz#6daa3600d53fc1f646604853bc99e95e260fbda0" + integrity sha512-jZRrDmBKjmGcqMFEUJ14FjMJwm05Qaked+1vxaALRtF0UAl7lPU8OLWXFxvoeg3jbQM249VPFVn8g2znaQkEtA== + deepmerge@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-3.1.0.tgz#a612626ce4803da410d77554bfd80361599c034d" @@ -4242,7 +4222,7 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.1.0, eslint-visitor-keys@^3.2.0: +eslint-visitor-keys@^3.1.0, eslint-visitor-keys@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz#6fbb166a6798ee5991358bc2daa1ba76cc1254a1" integrity sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ== @@ -5071,6 +5051,11 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: has "^1.0.3" has-symbols "^1.0.1" +get-stdin@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-7.0.0.tgz#8d5de98f15171a125c5e516643c7a6d0ea8a96f6" + integrity sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ== + get-stream@6.0.1, get-stream@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" @@ -5218,6 +5203,17 @@ glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^8.0.3: + version "8.0.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.0.3.tgz#415c6eb2deed9e502c68fa44a272e6da6eeca42e" + integrity sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + global-agent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-3.0.0.tgz#ae7cd31bd3583b93c5a16437a1afe27cc33a1ab6" @@ -5291,7 +5287,7 @@ globby@^11.0.1: merge2 "^1.3.0" slash "^3.0.0" -globby@^11.0.4, globby@^11.1.0: +globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -5921,7 +5917,7 @@ ignore@^5.1.4: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== -ignore@^5.1.8, ignore@^5.2.0: +ignore@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== @@ -7368,7 +7364,7 @@ minimatch@4.2.1: dependencies: brace-expansion "^1.1.7" -minimatch@^5.1.0: +minimatch@^5.0.1, minimatch@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7" integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg== @@ -8922,6 +8918,16 @@ prr@~1.0.1: resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= +pseudo-localization@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/pseudo-localization/-/pseudo-localization-2.4.0.tgz#5c19da35bc182ad7fc00d82d33dd42e88005e241" + integrity sha512-ISYMOKY8+f+PmiXMFw2y6KLY74LBrv/8ml/VjjoVEV2k+MS+OJZz7ydciK5ntJwxPrKQPTU1+oXq9Mx2b0zEzg== + dependencies: + flat "^5.0.2" + get-stdin "^7.0.0" + typescript "^4.7.4" + yargs "^17.2.1" + pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" @@ -10096,6 +10102,15 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" +string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string.prototype.padend@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.1.tgz#824c84265dbac46cade2b957b38b6a5d8d1683c5" @@ -10817,10 +10832,15 @@ typescript@^2.6.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4" integrity sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q= -typescript@^4.9.0-dev.20220921: - version "4.9.0-dev.20220921" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.0-dev.20220921.tgz#ad0a24dcc94a29ee8d3845ad025ced94fe732d82" - integrity sha512-0WdiNgM0YfgPURswuah1JJfnDwa6e7Ki0DJcfAEruw17mACMCJw2F7vK/4jLcd1uIl4La3ZIPnA7dOyM7y5Skg== +typescript@^4.7.4: + version "4.8.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" + integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== + +typescript@^4.9.0-dev.20221011: + version "4.9.0-dev.20221011" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.0-dev.20221011.tgz#5c0ccbb7cfc1d8928fec987b7fc490cd664869e3" + integrity sha512-ONzbj3hrlLtfCGhDV7zP9FHoVgYo7+zRvphDqNNoqg2B3uqnxw57Ql/zPThRfepFmISdd/P03owzxeBUE48xqA== typical@^4.0.0: version "4.0.0" @@ -11580,7 +11600,7 @@ ws@^7.2.0: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== -xml2js@^0.4.17: +xml2js@^0.4.17, xml2js@^0.4.23: version "0.4.23" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== @@ -11628,40 +11648,40 @@ xtend@~2.1.1: dependencies: object-keys "~0.4.0" -xterm-addon-canvas@0.3.0-beta.1: - version "0.3.0-beta.1" - resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.3.0-beta.1.tgz#17a65f5da65416b01d620ddef6247ff5013ffc15" - integrity sha512-34PKhrkvK1RtlOOmni4i5GUIyoFKGMph8fWFvA2d52IDTKmX9YoLzZfU73D/sUAx+/GKobCE8sr14CuBZctgNw== +xterm-addon-canvas@0.3.0-beta.13: + version "0.3.0-beta.13" + resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.3.0-beta.13.tgz#6fd23f4c8d4d013a540cb8ff1e4965ff6bf248b5" + integrity sha512-gNl2wG5zv5AttbTs1OgMRFcARs/dt0k2snFpMmrnaTKlgpi3S0k+Sh3C4nbdMuqbCZd+n7gtpAYP+F6ZXC1pdA== -xterm-addon-search@0.11.0-beta.1: - version "0.11.0-beta.1" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.11.0-beta.1.tgz#fe7178d70246cde73550447c5524672575467499" - integrity sha512-fKj8KnnhH1nC4oZpKsgnhtgxkTctoa9kGLMpTJjsNzFu0VvXvLGIRezTPI75UEIQdEdaxcwB7/aKelQTO+72LA== +xterm-addon-search@0.11.0-beta.4: + version "0.11.0-beta.4" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.11.0-beta.4.tgz#35c01a98b092adbbd1828b0d2eea28485a9a1538" + integrity sha512-rtDBg9CWDw6c0tMeBmaKan2UWwSY88UkfkLv+g50EopDyx6J337nxgjKGioUjF0pZ1VwFOaP1+rFRk8XwLiUHA== -xterm-addon-serialize@0.9.0-beta.1: - version "0.9.0-beta.1" - resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.9.0-beta.1.tgz#44a8047ec85abe4db232acc58c53355dd314bf6d" - integrity sha512-jVkpU5GC728ko0k190o+M1xubMkhRolKj18160rxlZhd0Sm/1yHUtFneC9pYSsLypynd3Te5LnZnHfEgVmka4g== +xterm-addon-serialize@0.9.0-beta.2: + version "0.9.0-beta.2" + resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.9.0-beta.2.tgz#2f37ba57cabcdbf6dfe56bce8209de04dcfaa8ef" + integrity sha512-oCRHXdlrlzcNmxRxHJlYq6FiJlgQDXft62DcE+WY7Y7R5rjPB8ahrSQHBwjc3ZIK0GRL8fRReuwxL1+Jy4Dt4Q== xterm-addon-unicode11@0.5.0-beta.1: version "0.5.0-beta.1" resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.5.0-beta.1.tgz#8a9e9356018e082318abbe2be1f9599fcc6b46a2" integrity sha512-uAErX4gwhW6N524stLG6oZR3yBGgPnFmZ2Tv4vyYy7tcgDuHRoc22xYSCDgO1ohz1FLlOm8JGXRjXliwO9ic3A== -xterm-addon-webgl@0.14.0-beta.2: - version "0.14.0-beta.2" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.14.0-beta.2.tgz#832c31b52b78fb67a65bbd23c9fb850caceb43ae" - integrity sha512-1ccbkJiUZ5ojnoAEJsbdV0jMZaYSnZ02wfV8yBU243u6TTgvCzZ7nq5BR9bT+5K/ESFWiekobfybxHwuYnylmQ== +xterm-addon-webgl@0.14.0-beta.20: + version "0.14.0-beta.20" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.14.0-beta.20.tgz#1bd723a58ab6414c4773d181bbae51751827768c" + integrity sha512-inUnGzw+ewMszMQY8PqO97/t6hh/qg712uyuHLLoinDOjzXN5x2IiPMrt4LL70P6YmmTU65m/Gmfp+rd2u4yWg== -xterm-headless@5.1.0-beta.1: - version "5.1.0-beta.1" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.1.0-beta.1.tgz#badec2e97e47aa44267a4de2c1b42b4d23ad49a2" - integrity sha512-V3G7l4pN6/HW//vKXryOCdDXVKdrQTQmtHEqkZ8waD68cJdeMdIoGYJuzavd5rHpxCqm/KR5O8ztI41jridong== +xterm-headless@5.1.0-beta.29: + version "5.1.0-beta.29" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.1.0-beta.29.tgz#b3c2bbd934fe5d9111b2206ddaf039d604aefe95" + integrity sha512-hzIzZwYtubZplfnmtx1IQdipp2eTt09zjTT6/JZFSTcTLBUCtePlgtCZPJENUyy5v+aSJgbMTDFrfjARRBCZMw== -xterm@5.1.0-beta.1: - version "5.1.0-beta.1" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.1.0-beta.1.tgz#a6617c6887066d166632d1e69b6eb83a179d8b63" - integrity sha512-ml7bqjO23bh4yu7qXKogXtCy4SbDTV21rfDXUvLPPaxrlQus6NoN1byy1eFH4ONWpv5ZHGeItRdQ/X00et9Pcw== +xterm@5.1.0-beta.29: + version "5.1.0-beta.29" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.1.0-beta.29.tgz#76368661b0503800c3f4374ab380e806290038e6" + integrity sha512-qirPDkX99thKjd7bkZMBQfP6EMOWTigYQz9lFU5tIkqsoYCu7zakaakkQuC2xbVRue9nftHZItzYKeO1/ldvTA== y18n@^3.2.1: version "3.2.2" @@ -11727,6 +11747,11 @@ yargs-parser@^20.2.2: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== +yargs-parser@^21.0.0: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + yargs-unparser@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" @@ -11784,6 +11809,19 @@ yargs@^15.3.0: y18n "^4.0.0" yargs-parser "^18.1.2" +yargs@^17.2.1, yargs@^17.5.1: + version "17.6.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.0.tgz#e134900fc1f218bc230192bdec06a0a5f973e46c" + integrity sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.0.0" + yargs@^7.1.0: version "7.1.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.1.tgz#67f0ef52e228d4ee0d6311acede8850f53464df6"