diff --git a/.devcontainer/cache/build-cache-image.sh b/.devcontainer/cache/build-cache-image.sh index 78d0fbfdf0c..42e143d7af4 100755 --- a/.devcontainer/cache/build-cache-image.sh +++ b/.devcontainer/cache/build-cache-image.sh @@ -8,7 +8,7 @@ set -e SCRIPT_PATH="$(cd "$(dirname $0)" && pwd)" CONTAINER_IMAGE_REPOSITORY="$1" -BRANCH="${2:-"master"}" +BRANCH="${2:-"main"}" if [ "${CONTAINER_IMAGE_REPOSITORY}" = "" ]; then echo "Container repository not specified!" diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index cd632e134ef..3b82cd9028d 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,7 +2,7 @@ "name": "Code - OSS", // Image contents: https://github.com/microsoft/vscode-dev-containers/blob/master/repository-containers/images/github.com/microsoft/vscode/.devcontainer/base.Dockerfile - "image": "mcr.microsoft.com/vscode/devcontainers/repos/microsoft/vscode:branch-master", + "image": "mcr.microsoft.com/vscode/devcontainers/repos/microsoft/vscode:branch-main", "workspaceMount": "source=${localWorkspaceFolder},target=/home/node/workspace/vscode,type=bind,consistency=cached", "workspaceFolder": "/home/node/workspace/vscode", diff --git a/.eslintrc.json b/.eslintrc.json index cf4c1418a11..09fa5a59cde 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -278,6 +278,7 @@ "sinon", "vs/nls", "**/vs/base/{common,browser}/**", + "**/vs/base/parts/*/{common,browser}/**", "**/vs/platform/*/{common,browser}/**", "**/vs/platform/*/test/{common,browser}/**" ] diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 45f9ee8f340..19314029215 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -2,7 +2,7 @@ * Read our Pull Request guidelines: https://github.com/microsoft/vscode/wiki/How-to-Contribute#pull-requests * Associate an issue with the Pull Request. -* Ensure that the code is up-to-date with the `master` branch. +* Ensure that the code is up-to-date with the `main` branch. * Include a description of the proposed changes and how to test them. --> diff --git a/.github/workflows/build-chat.yml b/.github/workflows/build-chat.yml index f9f146164ba..03f3bd42caf 100644 --- a/.github/workflows/build-chat.yml +++ b/.github/workflows/build-chat.yml @@ -7,7 +7,7 @@ on: types: - completed branches: - - master + - main - release/* jobs: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d53283f0cf4..f1324b59bc0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,11 +3,11 @@ name: CI on: push: branches: - - master + - main - release/* pull_request: branches: - - master + - main - release/* jobs: diff --git a/.github/workflows/devcontainer-cache.yml b/.github/workflows/devcontainer-cache.yml index a250b56cd7a..4670186bdda 100644 --- a/.github/workflows/devcontainer-cache.yml +++ b/.github/workflows/devcontainer-cache.yml @@ -2,9 +2,9 @@ name: VS Code Repo Dev Container Cache Image Generation on: push: - # Currently doing this for master, but could be done for PRs as well + # Currently doing this for main, but could be done for PRs as well branches: - - "master" + - "main" # Only updates to these files result in changes to installed packages, so skip otherwise paths: @@ -35,6 +35,6 @@ jobs: az acr login --name $ACR_REGISTRY_NAME GIT_BRANCH=$(echo "${{ github.ref }}" | grep -oP 'refs/(heads|tags)/\K(.+)') - if [ "$GIT_BRANCH" == "" ]; then GIT_BRANCH=master; fi + if [ "$GIT_BRANCH" == "" ]; then GIT_BRANCH=main; fi .devcontainer/cache/build-cache-image.sh "${{ secrets.CONTAINER_IMAGE_REGISTRY }}/public/vscode/devcontainers/repos/microsoft/vscode" "${GIT_BRANCH}" diff --git a/.github/workflows/rich-navigation.yml b/.github/workflows/rich-navigation.yml index aee0796fa28..71824cab83d 100644 --- a/.github/workflows/rich-navigation.yml +++ b/.github/workflows/rich-navigation.yml @@ -3,7 +3,7 @@ on: pull_request: push: branches: - - master + - main jobs: richnav: diff --git a/.vscode/launch.json b/.vscode/launch.json index 11c578b992d..b1db49c2549 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -110,7 +110,8 @@ // "${workspaceFolder}", // Uncomment for running out of sources. "${workspaceFolder}/extensions/vscode-api-tests/testWorkspace", "--extensionDevelopmentPath=${workspaceFolder}/extensions/vscode-api-tests", - "--extensionTestsPath=${workspaceFolder}/extensions/vscode-api-tests/out/singlefolder-tests" + "--extensionTestsPath=${workspaceFolder}/extensions/vscode-api-tests/out/singlefolder-tests", + "--disable-extensions" ], "outFiles": [ "${workspaceFolder}/out/**/*.js" diff --git a/.vscode/notebooks/api.github-issues b/.vscode/notebooks/api.github-issues index 71fd646776f..c0154336692 100644 --- a/.vscode/notebooks/api.github-issues +++ b/.vscode/notebooks/api.github-issues @@ -3,36 +3,1329 @@ "kind": 1, "language": "markdown", "value": "#### Config", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", "value": "$repo=repo:microsoft/vscode\n$milestone=milestone:\"February 2021\"", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "### Finalization", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", "value": "$repo $milestone label:api-finalization", - "editable": true + "editable": true, + "outputs": [ + { + "mime": "text/markdown", + "value": "- [#88309](https://github.com/microsoft/vscode/issues/88309 \"Authentication Provider API\") Authentication Provider API [api-finalization, authentication, feature-request, settings-sync]- [@RMacfarlane](https://github.com/RMacfarlane \"Issue 88309 is assigned to RMacfarlane\")\n\n" + }, + { + "mime": "x-application/github-issues", + "value": [ + { + "url": "https://api.github.com/repos/microsoft/vscode/issues/88309", + "repository_url": "https://api.github.com/repos/microsoft/vscode", + "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/88309/labels{/name}", + "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/88309/comments", + "events_url": "https://api.github.com/repos/microsoft/vscode/issues/88309/events", + "html_url": "https://github.com/microsoft/vscode/issues/88309", + "id": 547141160, + "node_id": "MDU6SXNzdWU1NDcxNDExNjA=", + "number": 88309, + "title": "Authentication Provider API", + "user": { + "login": "RMacfarlane", + "id": 3672607, + "node_id": "MDQ6VXNlcjM2NzI2MDc=", + "avatar_url": "https://avatars.githubusercontent.com/u/3672607?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/RMacfarlane", + "html_url": "https://github.com/RMacfarlane", + "followers_url": "https://api.github.com/users/RMacfarlane/followers", + "following_url": "https://api.github.com/users/RMacfarlane/following{/other_user}", + "gists_url": "https://api.github.com/users/RMacfarlane/gists{/gist_id}", + "starred_url": "https://api.github.com/users/RMacfarlane/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/RMacfarlane/subscriptions", + "organizations_url": "https://api.github.com/users/RMacfarlane/orgs", + "repos_url": "https://api.github.com/users/RMacfarlane/repos", + "events_url": "https://api.github.com/users/RMacfarlane/events{/privacy}", + "received_events_url": "https://api.github.com/users/RMacfarlane/received_events", + "type": "User", + "site_admin": false + }, + "labels": [ + { + "id": 974714207, + "node_id": "MDU6TGFiZWw5NzQ3MTQyMDc=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api-finalization", + "name": "api-finalization", + "color": "c5def5", + "default": false, + "description": "" + }, + { + "id": 1702048079, + "node_id": "MDU6TGFiZWwxNzAyMDQ4MDc5", + "url": "https://api.github.com/repos/microsoft/vscode/labels/authentication", + "name": "authentication", + "color": "c5def5", + "default": false, + "description": "Authentication issues" + }, + { + "id": 272689392, + "node_id": "MDU6TGFiZWwyNzI2ODkzOTI=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/feature-request", + "name": "feature-request", + "color": "dcdcdc", + "default": false, + "description": "Request for new features or functionality" + }, + { + "id": 1684021718, + "node_id": "MDU6TGFiZWwxNjg0MDIxNzE4", + "url": "https://api.github.com/repos/microsoft/vscode/labels/settings-sync", + "name": "settings-sync", + "color": "1d76db", + "default": false, + "description": "" + } + ], + "state": "open", + "locked": false, + "assignee": { + "login": "RMacfarlane", + "id": 3672607, + "node_id": "MDQ6VXNlcjM2NzI2MDc=", + "avatar_url": "https://avatars.githubusercontent.com/u/3672607?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/RMacfarlane", + "html_url": "https://github.com/RMacfarlane", + "followers_url": "https://api.github.com/users/RMacfarlane/followers", + "following_url": "https://api.github.com/users/RMacfarlane/following{/other_user}", + "gists_url": "https://api.github.com/users/RMacfarlane/gists{/gist_id}", + "starred_url": "https://api.github.com/users/RMacfarlane/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/RMacfarlane/subscriptions", + "organizations_url": "https://api.github.com/users/RMacfarlane/orgs", + "repos_url": "https://api.github.com/users/RMacfarlane/repos", + "events_url": "https://api.github.com/users/RMacfarlane/events{/privacy}", + "received_events_url": "https://api.github.com/users/RMacfarlane/received_events", + "type": "User", + "site_admin": false + }, + "assignees": [ + { + "login": "RMacfarlane", + "id": 3672607, + "node_id": "MDQ6VXNlcjM2NzI2MDc=", + "avatar_url": "https://avatars.githubusercontent.com/u/3672607?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/RMacfarlane", + "html_url": "https://github.com/RMacfarlane", + "followers_url": "https://api.github.com/users/RMacfarlane/followers", + "following_url": "https://api.github.com/users/RMacfarlane/following{/other_user}", + "gists_url": "https://api.github.com/users/RMacfarlane/gists{/gist_id}", + "starred_url": "https://api.github.com/users/RMacfarlane/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/RMacfarlane/subscriptions", + "organizations_url": "https://api.github.com/users/RMacfarlane/orgs", + "repos_url": "https://api.github.com/users/RMacfarlane/repos", + "events_url": "https://api.github.com/users/RMacfarlane/events{/privacy}", + "received_events_url": "https://api.github.com/users/RMacfarlane/received_events", + "type": "User", + "site_admin": false + } + ], + "milestone": { + "url": "https://api.github.com/repos/microsoft/vscode/milestones/142", + "html_url": "https://github.com/microsoft/vscode/milestone/142", + "labels_url": "https://api.github.com/repos/microsoft/vscode/milestones/142/labels", + "id": 6286096, + "node_id": "MDk6TWlsZXN0b25lNjI4NjA5Ng==", + "number": 142, + "title": "February 2021", + "description": "", + "creator": { + "login": "Tyriar", + "id": 2193314, + "node_id": "MDQ6VXNlcjIxOTMzMTQ=", + "avatar_url": "https://avatars.githubusercontent.com/u/2193314?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Tyriar", + "html_url": "https://github.com/Tyriar", + "followers_url": "https://api.github.com/users/Tyriar/followers", + "following_url": "https://api.github.com/users/Tyriar/following{/other_user}", + "gists_url": "https://api.github.com/users/Tyriar/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Tyriar/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Tyriar/subscriptions", + "organizations_url": "https://api.github.com/users/Tyriar/orgs", + "repos_url": "https://api.github.com/users/Tyriar/repos", + "events_url": "https://api.github.com/users/Tyriar/events{/privacy}", + "received_events_url": "https://api.github.com/users/Tyriar/received_events", + "type": "User", + "site_admin": false + }, + "open_issues": 232, + "closed_issues": 300, + "state": "open", + "created_at": "2021-01-07T18:23:10Z", + "updated_at": "2021-02-17T10:48:47Z", + "due_on": null, + "closed_at": null + }, + "comments": 23, + "created_at": "2020-01-08T22:31:35Z", + "updated_at": "2021-02-10T20:41:11Z", + "closed_at": null, + "author_association": "MEMBER", + "active_lock_reason": null, + "body": "### Problem\r\n\r\nThere are currently some extensions that attempt to provide authentication abilities that can be reused by other extensions. (An example being the Azure Account extension). Now that we've begun working on login for settings sync, it's worth revisiting if authentication should be a first-class concept in VS Code. By exposing an API to contribute an authentication flow\r\n\r\n- the core of VSCode can potentially leverage authentication\r\n- other extensions can leverage authentication\r\n- UI for account management could be centralized\r\n\r\n### Proposal\r\n\r\nI propose introducing a concept of an \"AuthenticationProvider\". Such a provider implements methods for logging in and logging out of a specified account, and exposes a list of accounts that are currently available with an event listener for changes to these. This abstracts away refreshing tokens from consumers - the AuthenticationProvider extension can manage refreshing in the background and fire an event when the accessToken has been changed.\r\n\r\n```ts\r\nexport interface Account {\r\n\treadonly id: string;\r\n\treadonly accessToken: string;\r\n\treadonly displayName: string;\r\n}\r\n\r\nexport interface AuthenticationProvider {\r\n\treadonly id: string; // perhaps \"type\"? Would be something like \"GitHub\", \"MSA\", etc.\r\n\treadonly displayName: string;\r\n\r\n\taccounts: ReadonlyArray;\r\n\tonDidChangeAccounts: Event>;\r\n\r\n\tlogin(): Promise;\r\n\tlogout(accountId: string): Promise;\r\n}\r\n\r\nexport namespace authentication {\r\n\texport function registerAuthenticationProvider(provider: AuthenticationProvider): Disposable;\r\n\texport const authenticationProviders: ReadonlyArray;\r\n}\r\n```\r\n\r\nConsumers would need to know the id of the provider they're looking for. For example, the settings sync code would look for an \"MSA\" provider since this is what the setting sync backend currently needs.\r\n\r\nSince the authentication provider extension would be activated in each VS Code window, the extension would be responsible for synchronizing state across instances. By default, such extensions would have [\"ui\", \"workspace\"] extensionKind, so that they can store and read credentials on the local machine in both the desktop and web case.", + "performed_via_github_app": null, + "score": 1 + } + ] + } + ] }, { "kind": 1, "language": "markdown", "value": "### Proposals", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", "value": "$repo $milestone is:open label:api-proposal ", - "editable": true + "editable": true, + "outputs": [ + { + "mime": "text/markdown", + "value": "- [#115631](https://github.com/microsoft/vscode/issues/115631 \"Provide a way for custom editors to process untitled files without relying on textDocument\") Provide a way for custom editors to process untitled files without relying on textDocument [api-proposal, custom-editors, notebook]- [@lramos15](https://github.com/lramos15 \"Issue 115631 is assigned to lramos15\")\n\n- [#115626](https://github.com/microsoft/vscode/issues/115626 \"Microsoft Auth Provider should support overriding client id and tenant id\") Microsoft Auth Provider should support overriding client id and tenant id [api-proposal, authentication]- [@TylerLeonhardt](https://github.com/TylerLeonhardt \"Issue 115626 is assigned to TylerLeonhardt\")\n\n- [#115616](https://github.com/microsoft/vscode/issues/115616 \"Provide extension API to exclude ports from forwarding\") Provide extension API to exclude ports from forwarding [api, api-proposal, feature-request, remote-explorer]\n- [#114123](https://github.com/microsoft/vscode/issues/114123 \"Resolve the conflict run button in editor context menu\") Resolve the conflict run button in editor context menu [api-proposal, feature-request, menus]- [@jrieken](https://github.com/jrieken \"Issue 114123 is assigned to jrieken\")\n\n- [#109277](https://github.com/microsoft/vscode/issues/109277 \"Let extensions hook into url opening\") Let extensions hook into url opening [api, api-proposal, under-discussion]- [@mjbvz](https://github.com/mjbvz \"Issue 109277 is assigned to mjbvz\")\n\n- [#107467](https://github.com/microsoft/vscode/issues/107467 \"Testing in VS Code\") Testing in VS Code [api-proposal, plan-item, under-discussion]- [@connor4312](https://github.com/connor4312 \"Issue 107467 is assigned to connor4312\")\n\n- [#105690](https://github.com/microsoft/vscode/issues/105690 \"Extension API for Inline Values\") Extension API for Inline Values [api, api-proposal, debug, feature-request]- [@weinand](https://github.com/weinand \"Issue 105690 is assigned to weinand\")\n\n" + }, + { + "mime": "x-application/github-issues", + "value": [ + { + "url": "https://api.github.com/repos/microsoft/vscode/issues/115631", + "repository_url": "https://api.github.com/repos/microsoft/vscode", + "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/115631/labels{/name}", + "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/115631/comments", + "events_url": "https://api.github.com/repos/microsoft/vscode/issues/115631/events", + "html_url": "https://github.com/microsoft/vscode/issues/115631", + "id": 799606785, + "node_id": "MDU6SXNzdWU3OTk2MDY3ODU=", + "number": 115631, + "title": "Provide a way for custom editors to process untitled files without relying on textDocument", + "user": { + "login": "lramos15", + "id": 4544166, + "node_id": "MDQ6VXNlcjQ1NDQxNjY=", + "avatar_url": "https://avatars.githubusercontent.com/u/4544166?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/lramos15", + "html_url": "https://github.com/lramos15", + "followers_url": "https://api.github.com/users/lramos15/followers", + "following_url": "https://api.github.com/users/lramos15/following{/other_user}", + "gists_url": "https://api.github.com/users/lramos15/gists{/gist_id}", + "starred_url": "https://api.github.com/users/lramos15/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/lramos15/subscriptions", + "organizations_url": "https://api.github.com/users/lramos15/orgs", + "repos_url": "https://api.github.com/users/lramos15/repos", + "events_url": "https://api.github.com/users/lramos15/events{/privacy}", + "received_events_url": "https://api.github.com/users/lramos15/received_events", + "type": "User", + "site_admin": false + }, + "labels": [ + { + "id": 869332220, + "node_id": "MDU6TGFiZWw4NjkzMzIyMjA=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api-proposal", + "name": "api-proposal", + "color": "c5def5", + "default": false, + "description": "" + }, + { + "id": 1713330180, + "node_id": "MDU6TGFiZWwxNzEzMzMwMTgw", + "url": "https://api.github.com/repos/microsoft/vscode/labels/custom-editors", + "name": "custom-editors", + "color": "c5def5", + "default": false, + "description": "Custom editor API (webview based editors)" + }, + { + "id": 1839857516, + "node_id": "MDU6TGFiZWwxODM5ODU3NTE2", + "url": "https://api.github.com/repos/microsoft/vscode/labels/notebook", + "name": "notebook", + "color": "c5def5", + "default": false, + "description": "" + } + ], + "state": "open", + "locked": false, + "assignee": { + "login": "lramos15", + "id": 4544166, + "node_id": "MDQ6VXNlcjQ1NDQxNjY=", + "avatar_url": "https://avatars.githubusercontent.com/u/4544166?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/lramos15", + "html_url": "https://github.com/lramos15", + "followers_url": "https://api.github.com/users/lramos15/followers", + "following_url": "https://api.github.com/users/lramos15/following{/other_user}", + "gists_url": "https://api.github.com/users/lramos15/gists{/gist_id}", + "starred_url": "https://api.github.com/users/lramos15/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/lramos15/subscriptions", + "organizations_url": "https://api.github.com/users/lramos15/orgs", + "repos_url": "https://api.github.com/users/lramos15/repos", + "events_url": "https://api.github.com/users/lramos15/events{/privacy}", + "received_events_url": "https://api.github.com/users/lramos15/received_events", + "type": "User", + "site_admin": false + }, + "assignees": [ + { + "login": "lramos15", + "id": 4544166, + "node_id": "MDQ6VXNlcjQ1NDQxNjY=", + "avatar_url": "https://avatars.githubusercontent.com/u/4544166?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/lramos15", + "html_url": "https://github.com/lramos15", + "followers_url": "https://api.github.com/users/lramos15/followers", + "following_url": "https://api.github.com/users/lramos15/following{/other_user}", + "gists_url": "https://api.github.com/users/lramos15/gists{/gist_id}", + "starred_url": "https://api.github.com/users/lramos15/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/lramos15/subscriptions", + "organizations_url": "https://api.github.com/users/lramos15/orgs", + "repos_url": "https://api.github.com/users/lramos15/repos", + "events_url": "https://api.github.com/users/lramos15/events{/privacy}", + "received_events_url": "https://api.github.com/users/lramos15/received_events", + "type": "User", + "site_admin": false + }, + { + "login": "mjbvz", + "id": 12821956, + "node_id": "MDQ6VXNlcjEyODIxOTU2", + "avatar_url": "https://avatars.githubusercontent.com/u/12821956?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/mjbvz", + "html_url": "https://github.com/mjbvz", + "followers_url": "https://api.github.com/users/mjbvz/followers", + "following_url": "https://api.github.com/users/mjbvz/following{/other_user}", + "gists_url": "https://api.github.com/users/mjbvz/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mjbvz/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mjbvz/subscriptions", + "organizations_url": "https://api.github.com/users/mjbvz/orgs", + "repos_url": "https://api.github.com/users/mjbvz/repos", + "events_url": "https://api.github.com/users/mjbvz/events{/privacy}", + "received_events_url": "https://api.github.com/users/mjbvz/received_events", + "type": "User", + "site_admin": false + } + ], + "milestone": { + "url": "https://api.github.com/repos/microsoft/vscode/milestones/142", + "html_url": "https://github.com/microsoft/vscode/milestone/142", + "labels_url": "https://api.github.com/repos/microsoft/vscode/milestones/142/labels", + "id": 6286096, + "node_id": "MDk6TWlsZXN0b25lNjI4NjA5Ng==", + "number": 142, + "title": "February 2021", + "description": "", + "creator": { + "login": "Tyriar", + "id": 2193314, + "node_id": "MDQ6VXNlcjIxOTMzMTQ=", + "avatar_url": "https://avatars.githubusercontent.com/u/2193314?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Tyriar", + "html_url": "https://github.com/Tyriar", + "followers_url": "https://api.github.com/users/Tyriar/followers", + "following_url": "https://api.github.com/users/Tyriar/following{/other_user}", + "gists_url": "https://api.github.com/users/Tyriar/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Tyriar/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Tyriar/subscriptions", + "organizations_url": "https://api.github.com/users/Tyriar/orgs", + "repos_url": "https://api.github.com/users/Tyriar/repos", + "events_url": "https://api.github.com/users/Tyriar/events{/privacy}", + "received_events_url": "https://api.github.com/users/Tyriar/received_events", + "type": "User", + "site_admin": false + }, + "open_issues": 232, + "closed_issues": 300, + "state": "open", + "created_at": "2021-01-07T18:23:10Z", + "updated_at": "2021-02-17T10:48:47Z", + "due_on": null, + "closed_at": null + }, + "comments": 1, + "created_at": "2021-02-02T19:29:05Z", + "updated_at": "2021-02-02T21:58:36Z", + "closed_at": null, + "author_association": "MEMBER", + "active_lock_reason": null, + "body": "Currently the \"Reopen with\" experience for untitled files and custom binary editors needs better support. See #114711. After discussion in the API call the best proposal seems to be placing the untitled file data in the OpenEditor / OpenNotebook context. There interface would be modified as shown:\r\n```ts\r\n\t/**\r\n\t * Additional information about the opening custom document.\r\n\t */\r\n\tinterface CustomDocumentOpenContext {\r\n\t\t/**\r\n\t\t * The id of the backup to restore the document from or `undefined` if there is no backup.\r\n\t\t *\r\n\t\t * If this is provided, your extension should restore the editor from the backup instead of reading the file\r\n\t\t * from the user's workspace.\r\n\t\t */\r\n\t\treadonly backupId?: string;\r\n\t\t/**\r\n\t\t * If the URI is an untitled file, this will be populated with the byte data of that file\r\n\t\t *\r\n\t\t * If this is provided, your extension should utilize this byte data rather than executing fs APIs on the URI passed in\r\n\t\t */\r\n\t\treadonly untitledDocumentData?: Uint8Array;\r\n\t}\r\n\r\n\tinterface NotebookDocumentOpenContext {\r\n\t\treadonly backupId?: string;\r\n\t\treadonly untitledDocumentData?: Uint8Array;\r\n\t}\r\n```\r\nThe extension other would then not be required to resolve the URI to a text document (which would have been disposed of). ", + "performed_via_github_app": null, + "score": 1 + }, + { + "url": "https://api.github.com/repos/microsoft/vscode/issues/115626", + "repository_url": "https://api.github.com/repos/microsoft/vscode", + "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/115626/labels{/name}", + "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/115626/comments", + "events_url": "https://api.github.com/repos/microsoft/vscode/issues/115626/events", + "html_url": "https://github.com/microsoft/vscode/issues/115626", + "id": 799566516, + "node_id": "MDU6SXNzdWU3OTk1NjY1MTY=", + "number": 115626, + "title": "Microsoft Auth Provider should support overriding client id and tenant id", + "user": { + "login": "TylerLeonhardt", + "id": 2644648, + "node_id": "MDQ6VXNlcjI2NDQ2NDg=", + "avatar_url": "https://avatars.githubusercontent.com/u/2644648?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/TylerLeonhardt", + "html_url": "https://github.com/TylerLeonhardt", + "followers_url": "https://api.github.com/users/TylerLeonhardt/followers", + "following_url": "https://api.github.com/users/TylerLeonhardt/following{/other_user}", + "gists_url": "https://api.github.com/users/TylerLeonhardt/gists{/gist_id}", + "starred_url": "https://api.github.com/users/TylerLeonhardt/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/TylerLeonhardt/subscriptions", + "organizations_url": "https://api.github.com/users/TylerLeonhardt/orgs", + "repos_url": "https://api.github.com/users/TylerLeonhardt/repos", + "events_url": "https://api.github.com/users/TylerLeonhardt/events{/privacy}", + "received_events_url": "https://api.github.com/users/TylerLeonhardt/received_events", + "type": "User", + "site_admin": false + }, + "labels": [ + { + "id": 869332220, + "node_id": "MDU6TGFiZWw4NjkzMzIyMjA=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api-proposal", + "name": "api-proposal", + "color": "c5def5", + "default": false, + "description": "" + }, + { + "id": 1702048079, + "node_id": "MDU6TGFiZWwxNzAyMDQ4MDc5", + "url": "https://api.github.com/repos/microsoft/vscode/labels/authentication", + "name": "authentication", + "color": "c5def5", + "default": false, + "description": "Authentication issues" + } + ], + "state": "open", + "locked": false, + "assignee": { + "login": "TylerLeonhardt", + "id": 2644648, + "node_id": "MDQ6VXNlcjI2NDQ2NDg=", + "avatar_url": "https://avatars.githubusercontent.com/u/2644648?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/TylerLeonhardt", + "html_url": "https://github.com/TylerLeonhardt", + "followers_url": "https://api.github.com/users/TylerLeonhardt/followers", + "following_url": "https://api.github.com/users/TylerLeonhardt/following{/other_user}", + "gists_url": "https://api.github.com/users/TylerLeonhardt/gists{/gist_id}", + "starred_url": "https://api.github.com/users/TylerLeonhardt/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/TylerLeonhardt/subscriptions", + "organizations_url": "https://api.github.com/users/TylerLeonhardt/orgs", + "repos_url": "https://api.github.com/users/TylerLeonhardt/repos", + "events_url": "https://api.github.com/users/TylerLeonhardt/events{/privacy}", + "received_events_url": "https://api.github.com/users/TylerLeonhardt/received_events", + "type": "User", + "site_admin": false + }, + "assignees": [ + { + "login": "TylerLeonhardt", + "id": 2644648, + "node_id": "MDQ6VXNlcjI2NDQ2NDg=", + "avatar_url": "https://avatars.githubusercontent.com/u/2644648?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/TylerLeonhardt", + "html_url": "https://github.com/TylerLeonhardt", + "followers_url": "https://api.github.com/users/TylerLeonhardt/followers", + "following_url": "https://api.github.com/users/TylerLeonhardt/following{/other_user}", + "gists_url": "https://api.github.com/users/TylerLeonhardt/gists{/gist_id}", + "starred_url": "https://api.github.com/users/TylerLeonhardt/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/TylerLeonhardt/subscriptions", + "organizations_url": "https://api.github.com/users/TylerLeonhardt/orgs", + "repos_url": "https://api.github.com/users/TylerLeonhardt/repos", + "events_url": "https://api.github.com/users/TylerLeonhardt/events{/privacy}", + "received_events_url": "https://api.github.com/users/TylerLeonhardt/received_events", + "type": "User", + "site_admin": false + }, + { + "login": "RMacfarlane", + "id": 3672607, + "node_id": "MDQ6VXNlcjM2NzI2MDc=", + "avatar_url": "https://avatars.githubusercontent.com/u/3672607?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/RMacfarlane", + "html_url": "https://github.com/RMacfarlane", + "followers_url": "https://api.github.com/users/RMacfarlane/followers", + "following_url": "https://api.github.com/users/RMacfarlane/following{/other_user}", + "gists_url": "https://api.github.com/users/RMacfarlane/gists{/gist_id}", + "starred_url": "https://api.github.com/users/RMacfarlane/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/RMacfarlane/subscriptions", + "organizations_url": "https://api.github.com/users/RMacfarlane/orgs", + "repos_url": "https://api.github.com/users/RMacfarlane/repos", + "events_url": "https://api.github.com/users/RMacfarlane/events{/privacy}", + "received_events_url": "https://api.github.com/users/RMacfarlane/received_events", + "type": "User", + "site_admin": false + } + ], + "milestone": { + "url": "https://api.github.com/repos/microsoft/vscode/milestones/142", + "html_url": "https://github.com/microsoft/vscode/milestone/142", + "labels_url": "https://api.github.com/repos/microsoft/vscode/milestones/142/labels", + "id": 6286096, + "node_id": "MDk6TWlsZXN0b25lNjI4NjA5Ng==", + "number": 142, + "title": "February 2021", + "description": "", + "creator": { + "login": "Tyriar", + "id": 2193314, + "node_id": "MDQ6VXNlcjIxOTMzMTQ=", + "avatar_url": "https://avatars.githubusercontent.com/u/2193314?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Tyriar", + "html_url": "https://github.com/Tyriar", + "followers_url": "https://api.github.com/users/Tyriar/followers", + "following_url": "https://api.github.com/users/Tyriar/following{/other_user}", + "gists_url": "https://api.github.com/users/Tyriar/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Tyriar/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Tyriar/subscriptions", + "organizations_url": "https://api.github.com/users/Tyriar/orgs", + "repos_url": "https://api.github.com/users/Tyriar/repos", + "events_url": "https://api.github.com/users/Tyriar/events{/privacy}", + "received_events_url": "https://api.github.com/users/Tyriar/received_events", + "type": "User", + "site_admin": false + }, + "open_issues": 232, + "closed_issues": 300, + "state": "open", + "created_at": "2021-01-07T18:23:10Z", + "updated_at": "2021-02-17T10:48:47Z", + "due_on": null, + "closed_at": null + }, + "comments": 0, + "created_at": "2021-02-02T18:42:12Z", + "updated_at": "2021-02-02T18:58:50Z", + "closed_at": null, + "author_association": "MEMBER", + "active_lock_reason": null, + "body": "\r\n\r\n\r\n\r\n\r\n\r\nThe Microsoft Auth Provider uses a specific AAD application with client id hardcoded here:\r\nhttps://github.com/microsoft/vscode/blob/582ea371c2bf785d88458dab95828387ad94a63d/extensions/microsoft-authentication/src/AADHelper.ts#L25-L26\r\n\r\nHowever, this application only has access to a handful of scopes, and to add _allowed_ scopes to this client id is a manual process (which for an external extension author means opening an issue here and then having one of us add that scope to the _allowed_ scopes for the application)\r\n\r\nAs an extension author, I should easily be able to create my own AAD application (in the Azure Portal for example) and use that client id instead of the one vscode uses so that I can have control over the scopes I care about and, if this exists, I can get telemetry when my client id is used.\r\n\r\nSince we have abstracted auth providers, I think it's fitting to be able to pass additional auth provider specific options down to an auth provider. For example, the Microsoft auth provider would take a client id and tenant that would replace the hard coded string above.\r\n\r\nProposal:\r\n\r\n```ts\r\n /**\r\n\t * Options to be used when getting an [AuthenticationSession](#AuthenticationSession) from an [AuthenticationProvider](#AuthenticationProvider).\r\n\t */\r\n\texport interface AuthenticationGetSessionOptions {\r\n\t\t/**\r\n\t\t * Whether login should be performed if there is no matching session.\r\n\t\t *\r\n\t\t * If true, a modal dialog will be shown asking the user to sign in. If false, a numbered badge will be shown\r\n\t\t * on the accounts activity bar icon. An entry for the extension will be added under the menu to sign in. This\r\n\t\t * allows quietly prompting the user to sign in.\r\n\t\t *\r\n\t\t * Defaults to false.\r\n\t\t */\r\n\t\tcreateIfNone?: boolean;\r\n\r\n\t\t/**\r\n\t\t * Whether the existing user session preference should be cleared.\r\n\t\t *\r\n\t\t * For authentication providers that support being signed into multiple accounts at once, the user will be\r\n\t\t * prompted to select an account to use when [getSession](#authentication.getSession) is called. This preference\r\n\t\t * is remembered until [getSession](#authentication.getSession) is called with this flag.\r\n\t\t *\r\n\t\t * Defaults to false.\r\n\t\t */\r\n\t\tclearSessionPreference?: boolean;\r\n\r\n\t\t/*************/\r\n\t\t/*** NEW ***/\r\n\t\t/*************/\r\n /**\r\n * Provider specific options for getting this session (i.e. client id, tenant)\r\n */\r\n\t\tproviderOptions?: { [key: string]: any; }\r\n\t}\r\n```\r\n\r\nThe Auth Provider would then need to be responsible for deciding if it already has created a session with these options or if it needs to create a new session based on these options.", + "performed_via_github_app": null, + "score": 1 + }, + { + "url": "https://api.github.com/repos/microsoft/vscode/issues/115616", + "repository_url": "https://api.github.com/repos/microsoft/vscode", + "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/115616/labels{/name}", + "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/115616/comments", + "events_url": "https://api.github.com/repos/microsoft/vscode/issues/115616/events", + "html_url": "https://github.com/microsoft/vscode/issues/115616", + "id": 799392757, + "node_id": "MDU6SXNzdWU3OTkzOTI3NTc=", + "number": 115616, + "title": "Provide extension API to exclude ports from forwarding", + "user": { + "login": "alexr00", + "id": 38270282, + "node_id": "MDQ6VXNlcjM4MjcwMjgy", + "avatar_url": "https://avatars.githubusercontent.com/u/38270282?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/alexr00", + "html_url": "https://github.com/alexr00", + "followers_url": "https://api.github.com/users/alexr00/followers", + "following_url": "https://api.github.com/users/alexr00/following{/other_user}", + "gists_url": "https://api.github.com/users/alexr00/gists{/gist_id}", + "starred_url": "https://api.github.com/users/alexr00/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/alexr00/subscriptions", + "organizations_url": "https://api.github.com/users/alexr00/orgs", + "repos_url": "https://api.github.com/users/alexr00/repos", + "events_url": "https://api.github.com/users/alexr00/events{/privacy}", + "received_events_url": "https://api.github.com/users/alexr00/received_events", + "type": "User", + "site_admin": false + }, + "labels": [ + { + "id": 290465400, + "node_id": "MDU6TGFiZWwyOTA0NjU0MDA=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api", + "name": "api", + "color": "1d76db", + "default": false, + "description": "" + }, + { + "id": 869332220, + "node_id": "MDU6TGFiZWw4NjkzMzIyMjA=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api-proposal", + "name": "api-proposal", + "color": "c5def5", + "default": false, + "description": "" + }, + { + "id": 272689392, + "node_id": "MDU6TGFiZWwyNzI2ODkzOTI=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/feature-request", + "name": "feature-request", + "color": "dcdcdc", + "default": false, + "description": "Request for new features or functionality" + }, + { + "id": 1772775110, + "node_id": "MDU6TGFiZWwxNzcyNzc1MTEw", + "url": "https://api.github.com/repos/microsoft/vscode/labels/remote-explorer", + "name": "remote-explorer", + "color": "1d76db", + "default": false, + "description": "Remote explorer view" + } + ], + "state": "open", + "locked": false, + "assignee": null, + "assignees": [ + { + "login": "alexr00", + "id": 38270282, + "node_id": "MDQ6VXNlcjM4MjcwMjgy", + "avatar_url": "https://avatars.githubusercontent.com/u/38270282?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/alexr00", + "html_url": "https://github.com/alexr00", + "followers_url": "https://api.github.com/users/alexr00/followers", + "following_url": "https://api.github.com/users/alexr00/following{/other_user}", + "gists_url": "https://api.github.com/users/alexr00/gists{/gist_id}", + "starred_url": "https://api.github.com/users/alexr00/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/alexr00/subscriptions", + "organizations_url": "https://api.github.com/users/alexr00/orgs", + "repos_url": "https://api.github.com/users/alexr00/repos", + "events_url": "https://api.github.com/users/alexr00/events{/privacy}", + "received_events_url": "https://api.github.com/users/alexr00/received_events", + "type": "User", + "site_admin": false + } + ], + "milestone": { + "url": "https://api.github.com/repos/microsoft/vscode/milestones/142", + "html_url": "https://github.com/microsoft/vscode/milestone/142", + "labels_url": "https://api.github.com/repos/microsoft/vscode/milestones/142/labels", + "id": 6286096, + "node_id": "MDk6TWlsZXN0b25lNjI4NjA5Ng==", + "number": 142, + "title": "February 2021", + "description": "", + "creator": { + "login": "Tyriar", + "id": 2193314, + "node_id": "MDQ6VXNlcjIxOTMzMTQ=", + "avatar_url": "https://avatars.githubusercontent.com/u/2193314?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Tyriar", + "html_url": "https://github.com/Tyriar", + "followers_url": "https://api.github.com/users/Tyriar/followers", + "following_url": "https://api.github.com/users/Tyriar/following{/other_user}", + "gists_url": "https://api.github.com/users/Tyriar/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Tyriar/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Tyriar/subscriptions", + "organizations_url": "https://api.github.com/users/Tyriar/orgs", + "repos_url": "https://api.github.com/users/Tyriar/repos", + "events_url": "https://api.github.com/users/Tyriar/events{/privacy}", + "received_events_url": "https://api.github.com/users/Tyriar/received_events", + "type": "User", + "site_admin": false + }, + "open_issues": 232, + "closed_issues": 300, + "state": "open", + "created_at": "2021-01-07T18:23:10Z", + "updated_at": "2021-02-17T10:48:47Z", + "due_on": null, + "closed_at": null + }, + "comments": 15, + "created_at": "2021-02-02T15:37:45Z", + "updated_at": "2021-02-12T16:08:10Z", + "closed_at": null, + "author_association": "MEMBER", + "active_lock_reason": null, + "body": "From @weinand:\r\nToday the tunneling service blindly forwards all communication ports.\r\nThis includes ports that are used for debugging (even if our remote debugging feature does not need these ports to be forwarded).\r\nThis is confusing for users because they see ports that they are not really interested in.\r\n\r\nI propose to add extension API so that individual ports or port ranges can be excluded from forwarding.\r\nDebug extensions could use this API.\r\n\r\n", + "performed_via_github_app": null, + "score": 1 + }, + { + "url": "https://api.github.com/repos/microsoft/vscode/issues/114123", + "repository_url": "https://api.github.com/repos/microsoft/vscode", + "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/114123/labels{/name}", + "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/114123/comments", + "events_url": "https://api.github.com/repos/microsoft/vscode/issues/114123/events", + "html_url": "https://github.com/microsoft/vscode/issues/114123", + "id": 783094648, + "node_id": "MDU6SXNzdWU3ODMwOTQ2NDg=", + "number": 114123, + "title": "Resolve the conflict run button in editor context menu", + "user": { + "login": "jdneo", + "id": 6193897, + "node_id": "MDQ6VXNlcjYxOTM4OTc=", + "avatar_url": "https://avatars.githubusercontent.com/u/6193897?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/jdneo", + "html_url": "https://github.com/jdneo", + "followers_url": "https://api.github.com/users/jdneo/followers", + "following_url": "https://api.github.com/users/jdneo/following{/other_user}", + "gists_url": "https://api.github.com/users/jdneo/gists{/gist_id}", + "starred_url": "https://api.github.com/users/jdneo/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/jdneo/subscriptions", + "organizations_url": "https://api.github.com/users/jdneo/orgs", + "repos_url": "https://api.github.com/users/jdneo/repos", + "events_url": "https://api.github.com/users/jdneo/events{/privacy}", + "received_events_url": "https://api.github.com/users/jdneo/received_events", + "type": "User", + "site_admin": false + }, + "labels": [ + { + "id": 869332220, + "node_id": "MDU6TGFiZWw4NjkzMzIyMjA=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api-proposal", + "name": "api-proposal", + "color": "c5def5", + "default": false, + "description": "" + }, + { + "id": 272689392, + "node_id": "MDU6TGFiZWwyNzI2ODkzOTI=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/feature-request", + "name": "feature-request", + "color": "dcdcdc", + "default": false, + "description": "Request for new features or functionality" + }, + { + "id": 795791582, + "node_id": "MDU6TGFiZWw3OTU3OTE1ODI=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/menus", + "name": "menus", + "color": "1d76db", + "default": false, + "description": "Menu items and widget issues" + } + ], + "state": "open", + "locked": false, + "assignee": { + "login": "jrieken", + "id": 1794099, + "node_id": "MDQ6VXNlcjE3OTQwOTk=", + "avatar_url": "https://avatars.githubusercontent.com/u/1794099?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/jrieken", + "html_url": "https://github.com/jrieken", + "followers_url": "https://api.github.com/users/jrieken/followers", + "following_url": "https://api.github.com/users/jrieken/following{/other_user}", + "gists_url": "https://api.github.com/users/jrieken/gists{/gist_id}", + "starred_url": "https://api.github.com/users/jrieken/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/jrieken/subscriptions", + "organizations_url": "https://api.github.com/users/jrieken/orgs", + "repos_url": "https://api.github.com/users/jrieken/repos", + "events_url": "https://api.github.com/users/jrieken/events{/privacy}", + "received_events_url": "https://api.github.com/users/jrieken/received_events", + "type": "User", + "site_admin": false + }, + "assignees": [ + { + "login": "jrieken", + "id": 1794099, + "node_id": "MDQ6VXNlcjE3OTQwOTk=", + "avatar_url": "https://avatars.githubusercontent.com/u/1794099?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/jrieken", + "html_url": "https://github.com/jrieken", + "followers_url": "https://api.github.com/users/jrieken/followers", + "following_url": "https://api.github.com/users/jrieken/following{/other_user}", + "gists_url": "https://api.github.com/users/jrieken/gists{/gist_id}", + "starred_url": "https://api.github.com/users/jrieken/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/jrieken/subscriptions", + "organizations_url": "https://api.github.com/users/jrieken/orgs", + "repos_url": "https://api.github.com/users/jrieken/repos", + "events_url": "https://api.github.com/users/jrieken/events{/privacy}", + "received_events_url": "https://api.github.com/users/jrieken/received_events", + "type": "User", + "site_admin": false + } + ], + "milestone": { + "url": "https://api.github.com/repos/microsoft/vscode/milestones/142", + "html_url": "https://github.com/microsoft/vscode/milestone/142", + "labels_url": "https://api.github.com/repos/microsoft/vscode/milestones/142/labels", + "id": 6286096, + "node_id": "MDk6TWlsZXN0b25lNjI4NjA5Ng==", + "number": 142, + "title": "February 2021", + "description": "", + "creator": { + "login": "Tyriar", + "id": 2193314, + "node_id": "MDQ6VXNlcjIxOTMzMTQ=", + "avatar_url": "https://avatars.githubusercontent.com/u/2193314?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Tyriar", + "html_url": "https://github.com/Tyriar", + "followers_url": "https://api.github.com/users/Tyriar/followers", + "following_url": "https://api.github.com/users/Tyriar/following{/other_user}", + "gists_url": "https://api.github.com/users/Tyriar/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Tyriar/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Tyriar/subscriptions", + "organizations_url": "https://api.github.com/users/Tyriar/orgs", + "repos_url": "https://api.github.com/users/Tyriar/repos", + "events_url": "https://api.github.com/users/Tyriar/events{/privacy}", + "received_events_url": "https://api.github.com/users/Tyriar/received_events", + "type": "User", + "site_admin": false + }, + "open_issues": 232, + "closed_issues": 300, + "state": "open", + "created_at": "2021-01-07T18:23:10Z", + "updated_at": "2021-02-17T10:48:47Z", + "due_on": null, + "closed_at": null + }, + "comments": 15, + "created_at": "2021-01-11T06:09:50Z", + "updated_at": "2021-02-09T13:40:28Z", + "closed_at": null, + "author_association": "MEMBER", + "active_lock_reason": null, + "body": "\r\n\r\n\r\n\r\n\r\n\r\n### Problem\r\nSince the contribution point: `editor/title` is open for all the extensions, sometimes different extensions may have conflicts at this area. For example, in Java, such conflicts affect the run experience when the user installs both the Java extensions and the Code Runner extension:\r\n\r\n![image](https://user-images.githubusercontent.com/6193897/104149682-30373100-5412-11eb-84be-8f05bfa9c042.png)\r\n\r\nThis is a very open and big topic, as a author of VS Code extensions, it will be great if VS Code as a platform, can provide solutions for this issue.\r\n\r\n### Potential Solutions\r\nBelow are just rough ideas on this, it's open for discussion!\r\n\r\n#### Editor Metadata\r\nExtensions can register context value per document/editor, and register command on the editor title area according to the context value. For example. the Java Debugger can use this to mark if the current Java file is executable or not. And register the run/debug command into the editor context area if it's executable.\r\n\r\n> This can somehow achieved by using the `in` expression of the when clause, something like `resource in hasMainMethodFiles`. But we also need to [get the context value dynamically from the code](https://github.com/microsoft/vscode/issues/10471#issuecomment-718548790) to handle with the corporation between multiple extensions. \r\n\r\nMeanwhile the Java feature team can contribute changes to the Code Runner extension to align the UX (for example, hide the run button from Code Runner if the current Java file contains an executable Main class).\r\n\r\n#### Official Support for the run experience in the editor title area.\r\nThis also may have some opportunity since I believe it's somehow related with #85759, if VS Code team will consider provide official `run/debug` functionality area in the editor title.\r\n\r\n", + "performed_via_github_app": null, + "score": 1 + }, + { + "url": "https://api.github.com/repos/microsoft/vscode/issues/109277", + "repository_url": "https://api.github.com/repos/microsoft/vscode", + "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/109277/labels{/name}", + "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/109277/comments", + "events_url": "https://api.github.com/repos/microsoft/vscode/issues/109277/events", + "html_url": "https://github.com/microsoft/vscode/issues/109277", + "id": 728636389, + "node_id": "MDU6SXNzdWU3Mjg2MzYzODk=", + "number": 109277, + "title": "Let extensions hook into url opening", + "user": { + "login": "mjbvz", + "id": 12821956, + "node_id": "MDQ6VXNlcjEyODIxOTU2", + "avatar_url": "https://avatars.githubusercontent.com/u/12821956?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/mjbvz", + "html_url": "https://github.com/mjbvz", + "followers_url": "https://api.github.com/users/mjbvz/followers", + "following_url": "https://api.github.com/users/mjbvz/following{/other_user}", + "gists_url": "https://api.github.com/users/mjbvz/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mjbvz/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mjbvz/subscriptions", + "organizations_url": "https://api.github.com/users/mjbvz/orgs", + "repos_url": "https://api.github.com/users/mjbvz/repos", + "events_url": "https://api.github.com/users/mjbvz/events{/privacy}", + "received_events_url": "https://api.github.com/users/mjbvz/received_events", + "type": "User", + "site_admin": false + }, + "labels": [ + { + "id": 290465400, + "node_id": "MDU6TGFiZWwyOTA0NjU0MDA=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api", + "name": "api", + "color": "1d76db", + "default": false, + "description": "" + }, + { + "id": 869332220, + "node_id": "MDU6TGFiZWw4NjkzMzIyMjA=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api-proposal", + "name": "api-proposal", + "color": "c5def5", + "default": false, + "description": "" + }, + { + "id": 578047123, + "node_id": "MDU6TGFiZWw1NzgwNDcxMjM=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/under-discussion", + "name": "under-discussion", + "color": "dcdcdc", + "default": false, + "description": "Issue is under discussion for relevance, priority, approach" + } + ], + "state": "open", + "locked": false, + "assignee": { + "login": "mjbvz", + "id": 12821956, + "node_id": "MDQ6VXNlcjEyODIxOTU2", + "avatar_url": "https://avatars.githubusercontent.com/u/12821956?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/mjbvz", + "html_url": "https://github.com/mjbvz", + "followers_url": "https://api.github.com/users/mjbvz/followers", + "following_url": "https://api.github.com/users/mjbvz/following{/other_user}", + "gists_url": "https://api.github.com/users/mjbvz/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mjbvz/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mjbvz/subscriptions", + "organizations_url": "https://api.github.com/users/mjbvz/orgs", + "repos_url": "https://api.github.com/users/mjbvz/repos", + "events_url": "https://api.github.com/users/mjbvz/events{/privacy}", + "received_events_url": "https://api.github.com/users/mjbvz/received_events", + "type": "User", + "site_admin": false + }, + "assignees": [ + { + "login": "mjbvz", + "id": 12821956, + "node_id": "MDQ6VXNlcjEyODIxOTU2", + "avatar_url": "https://avatars.githubusercontent.com/u/12821956?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/mjbvz", + "html_url": "https://github.com/mjbvz", + "followers_url": "https://api.github.com/users/mjbvz/followers", + "following_url": "https://api.github.com/users/mjbvz/following{/other_user}", + "gists_url": "https://api.github.com/users/mjbvz/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mjbvz/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mjbvz/subscriptions", + "organizations_url": "https://api.github.com/users/mjbvz/orgs", + "repos_url": "https://api.github.com/users/mjbvz/repos", + "events_url": "https://api.github.com/users/mjbvz/events{/privacy}", + "received_events_url": "https://api.github.com/users/mjbvz/received_events", + "type": "User", + "site_admin": false + } + ], + "milestone": { + "url": "https://api.github.com/repos/microsoft/vscode/milestones/142", + "html_url": "https://github.com/microsoft/vscode/milestone/142", + "labels_url": "https://api.github.com/repos/microsoft/vscode/milestones/142/labels", + "id": 6286096, + "node_id": "MDk6TWlsZXN0b25lNjI4NjA5Ng==", + "number": 142, + "title": "February 2021", + "description": "", + "creator": { + "login": "Tyriar", + "id": 2193314, + "node_id": "MDQ6VXNlcjIxOTMzMTQ=", + "avatar_url": "https://avatars.githubusercontent.com/u/2193314?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Tyriar", + "html_url": "https://github.com/Tyriar", + "followers_url": "https://api.github.com/users/Tyriar/followers", + "following_url": "https://api.github.com/users/Tyriar/following{/other_user}", + "gists_url": "https://api.github.com/users/Tyriar/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Tyriar/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Tyriar/subscriptions", + "organizations_url": "https://api.github.com/users/Tyriar/orgs", + "repos_url": "https://api.github.com/users/Tyriar/repos", + "events_url": "https://api.github.com/users/Tyriar/events{/privacy}", + "received_events_url": "https://api.github.com/users/Tyriar/received_events", + "type": "User", + "site_admin": false + }, + "open_issues": 232, + "closed_issues": 300, + "state": "open", + "created_at": "2021-01-07T18:23:10Z", + "updated_at": "2021-02-17T10:48:47Z", + "due_on": null, + "closed_at": null + }, + "comments": 10, + "created_at": "2020-10-24T02:27:26Z", + "updated_at": "2021-02-11T23:51:08Z", + "closed_at": null, + "author_association": "MEMBER", + "active_lock_reason": null, + "body": "## Overview\r\nLet extensions hook into url opening. Motivating use case: I click on a link in the integrated terminal and it opens in my [browser preview extension](https://marketplace.visualstudio.com/items?itemName=auchenberg.vscode-browser-preview)\r\n\r\nPotential places to handle links:\r\n\r\n- Links in the terminal\r\n- Links in documents\r\n- Links from the remote port forwarding views\r\n- Debugger launch?\r\n- Open external?\r\n\r\n## Additional requirements\r\n\r\n- A url opener should be able to decline opening a link\r\n\r\n Some openers may only support specific types of links, such as `localhost`\r\n\r\n- Clicking a link should activate relevant extensions\r\n\r\n We'd need a new activation event so that extensions can make sure they handle link opening\r\n\r\n- Let users fallback to VS Code's default behavior\r\n\r\n This typically is to open using the default browser\r\n\r\n- Handle multiple url openers being registered at the same time\r\n\r\n Users should be able to select which opener to use in this case. They should potentially be able to specify a default opener.\r\n", + "performed_via_github_app": null, + "score": 1 + }, + { + "url": "https://api.github.com/repos/microsoft/vscode/issues/107467", + "repository_url": "https://api.github.com/repos/microsoft/vscode", + "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/107467/labels{/name}", + "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/107467/comments", + "events_url": "https://api.github.com/repos/microsoft/vscode/issues/107467/events", + "html_url": "https://github.com/microsoft/vscode/issues/107467", + "id": 709128519, + "node_id": "MDU6SXNzdWU3MDkxMjg1MTk=", + "number": 107467, + "title": "Testing in VS Code", + "user": { + "login": "connor4312", + "id": 2230985, + "node_id": "MDQ6VXNlcjIyMzA5ODU=", + "avatar_url": "https://avatars.githubusercontent.com/u/2230985?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/connor4312", + "html_url": "https://github.com/connor4312", + "followers_url": "https://api.github.com/users/connor4312/followers", + "following_url": "https://api.github.com/users/connor4312/following{/other_user}", + "gists_url": "https://api.github.com/users/connor4312/gists{/gist_id}", + "starred_url": "https://api.github.com/users/connor4312/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/connor4312/subscriptions", + "organizations_url": "https://api.github.com/users/connor4312/orgs", + "repos_url": "https://api.github.com/users/connor4312/repos", + "events_url": "https://api.github.com/users/connor4312/events{/privacy}", + "received_events_url": "https://api.github.com/users/connor4312/received_events", + "type": "User", + "site_admin": false + }, + "labels": [ + { + "id": 869332220, + "node_id": "MDU6TGFiZWw4NjkzMzIyMjA=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api-proposal", + "name": "api-proposal", + "color": "c5def5", + "default": false, + "description": "" + }, + { + "id": 293426086, + "node_id": "MDU6TGFiZWwyOTM0MjYwODY=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/plan-item", + "name": "plan-item", + "color": "dcdcdc", + "default": false, + "description": "VS Code - planned item for upcoming" + }, + { + "id": 578047123, + "node_id": "MDU6TGFiZWw1NzgwNDcxMjM=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/under-discussion", + "name": "under-discussion", + "color": "dcdcdc", + "default": false, + "description": "Issue is under discussion for relevance, priority, approach" + } + ], + "state": "open", + "locked": false, + "assignee": { + "login": "connor4312", + "id": 2230985, + "node_id": "MDQ6VXNlcjIyMzA5ODU=", + "avatar_url": "https://avatars.githubusercontent.com/u/2230985?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/connor4312", + "html_url": "https://github.com/connor4312", + "followers_url": "https://api.github.com/users/connor4312/followers", + "following_url": "https://api.github.com/users/connor4312/following{/other_user}", + "gists_url": "https://api.github.com/users/connor4312/gists{/gist_id}", + "starred_url": "https://api.github.com/users/connor4312/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/connor4312/subscriptions", + "organizations_url": "https://api.github.com/users/connor4312/orgs", + "repos_url": "https://api.github.com/users/connor4312/repos", + "events_url": "https://api.github.com/users/connor4312/events{/privacy}", + "received_events_url": "https://api.github.com/users/connor4312/received_events", + "type": "User", + "site_admin": false + }, + "assignees": [ + { + "login": "connor4312", + "id": 2230985, + "node_id": "MDQ6VXNlcjIyMzA5ODU=", + "avatar_url": "https://avatars.githubusercontent.com/u/2230985?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/connor4312", + "html_url": "https://github.com/connor4312", + "followers_url": "https://api.github.com/users/connor4312/followers", + "following_url": "https://api.github.com/users/connor4312/following{/other_user}", + "gists_url": "https://api.github.com/users/connor4312/gists{/gist_id}", + "starred_url": "https://api.github.com/users/connor4312/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/connor4312/subscriptions", + "organizations_url": "https://api.github.com/users/connor4312/orgs", + "repos_url": "https://api.github.com/users/connor4312/repos", + "events_url": "https://api.github.com/users/connor4312/events{/privacy}", + "received_events_url": "https://api.github.com/users/connor4312/received_events", + "type": "User", + "site_admin": false + }, + { + "login": "sandy081", + "id": 10746682, + "node_id": "MDQ6VXNlcjEwNzQ2Njgy", + "avatar_url": "https://avatars.githubusercontent.com/u/10746682?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/sandy081", + "html_url": "https://github.com/sandy081", + "followers_url": "https://api.github.com/users/sandy081/followers", + "following_url": "https://api.github.com/users/sandy081/following{/other_user}", + "gists_url": "https://api.github.com/users/sandy081/gists{/gist_id}", + "starred_url": "https://api.github.com/users/sandy081/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/sandy081/subscriptions", + "organizations_url": "https://api.github.com/users/sandy081/orgs", + "repos_url": "https://api.github.com/users/sandy081/repos", + "events_url": "https://api.github.com/users/sandy081/events{/privacy}", + "received_events_url": "https://api.github.com/users/sandy081/received_events", + "type": "User", + "site_admin": false + } + ], + "milestone": { + "url": "https://api.github.com/repos/microsoft/vscode/milestones/142", + "html_url": "https://github.com/microsoft/vscode/milestone/142", + "labels_url": "https://api.github.com/repos/microsoft/vscode/milestones/142/labels", + "id": 6286096, + "node_id": "MDk6TWlsZXN0b25lNjI4NjA5Ng==", + "number": 142, + "title": "February 2021", + "description": "", + "creator": { + "login": "Tyriar", + "id": 2193314, + "node_id": "MDQ6VXNlcjIxOTMzMTQ=", + "avatar_url": "https://avatars.githubusercontent.com/u/2193314?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Tyriar", + "html_url": "https://github.com/Tyriar", + "followers_url": "https://api.github.com/users/Tyriar/followers", + "following_url": "https://api.github.com/users/Tyriar/following{/other_user}", + "gists_url": "https://api.github.com/users/Tyriar/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Tyriar/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Tyriar/subscriptions", + "organizations_url": "https://api.github.com/users/Tyriar/orgs", + "repos_url": "https://api.github.com/users/Tyriar/repos", + "events_url": "https://api.github.com/users/Tyriar/events{/privacy}", + "received_events_url": "https://api.github.com/users/Tyriar/received_events", + "type": "User", + "site_admin": false + }, + "open_issues": 232, + "closed_issues": 300, + "state": "open", + "created_at": "2021-01-07T18:23:10Z", + "updated_at": "2021-02-17T10:48:47Z", + "due_on": null, + "closed_at": null + }, + "comments": 53, + "created_at": "2020-09-25T17:19:53Z", + "updated_at": "2021-02-16T21:11:36Z", + "closed_at": null, + "author_association": "MEMBER", + "active_lock_reason": null, + "body": "## State of the World\r\n\r\nTesting support in VS Code has been a feature request for [a long time](https://github.com/microsoft/vscode/issues/9505). The VS Code community has build excellent extensions around testing, for example:\r\n\r\n- The [Test Explorer UI](https://marketplace.visualstudio.com/items?itemName=hbenl.vscode-test-explorer) from @hbenl\r\n- [Wallaby.js](https://wallabyjs.com/) from the Wallaby team\r\n- [Jest](https://marketplace.visualstudio.com/items?itemName=Orta.vscode-jest) from @orta\r\n- ...and many more\r\n\r\nEach implementation of testing presents a different set of features, UI, and idiomaticity. Because there is no sanctioned approach to tests in VS Code, extension developers tend to make bespoke implementations, as we've seen in the Python and Java language extensions. Ideally, like in debugging, a VS Code user would have just about the same experience as they work between projects and languages.\r\n\r\n## VS Code's Approach\r\n\r\n> Investigate how VS Code can improve the testing support. Several extensions are already providing testing support, explore what APIs/UIs could be added to improve these testing extensions and the test running experience. -- [2020 Roadmap](https://github.com/microsoft/vscode/wiki/Roadmap#testing)\r\n\r\nThe Test Explorer UI presents the best point of inspiration for us, as there are many existing extensions built on its API: it's capable and proven. Regardless of the direction we take in VS Code, we should have a way for its Test Adapters to be upgraded to the new world.\r\n\r\nWallaby is an excellent extension, but it's tailored and purpose-built to JavaScript, and includes functionality which is not readily portable to other languages. While it is a good source for inspiration, we're not aiming to encompass Wallaby's feature set in the extension points we provide, at least not yet.\r\n\r\nWe're prototyping an API in the extension host, but there are a number of approaches we can take:\r\n\r\n\r\n\t\r\n\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\r\n\t\r\n\t\t\r\n\t\t\r\n\t\t\r\n\t\r\n\t\r\n\t\t\r\n\t\t\r\n\t\t\r\n\t\r\n
Extension Host ('traditional' VS Code API)'Test Protocol' (like DAP/LSP)Extension (like existing test explorer)
\r\n\t\t\t+ Simple to adopt for extension authors
\r\n\t\t\t+ Easier to manage state
\r\n\t\t\t+ Clear way to build 'official' test extensions
\r\n\t\t
\r\n\t\t\t+ Encourages keeping expensive work in child processes
\r\n\t\t\t+ Could be theoretically shared with VS and other editors
\r\n\t\t
\r\n\t\t\t+ Keep VS Code core slim
\r\n\t\t\t+ Unclear whether there's significant functionality we'd want that's not already possible in exthost api
\r\n\t\t
\r\n\t\t\t- The 'obvious path' is doing heavy lifting in the extension host process, which is undesirable
\r\n\t\t
\r\n\t\t\t- Additional implementation and maintainence complexity for VS Code
\r\n\t\t\t- Less friendly, additional complexity than TS APIs for extension authors
\r\n\t\t
\r\n\t\t\t- Additional extension and set of libraries to maintain+version for types and implementation
\r\n\t\t\t- Less clear there's an official pathway for test extensions
\r\n\t\t
\r\n\r\n## API Design\r\n\r\nThe following is a working draft of an API design. It should not be considered final, or anything close to final. This post will be edited as it evolves.\r\n\r\n#### Changes versus the [Test Adapter API](https://github.com/hbenl/vscode-test-adapter-api)\r\n\r\nAs mentioned, the test adapter API and this one provide a similar end user experience. Here are the notable changes we made:\r\n\r\n- The test adapter API does not distinguish between watching a workspace and watching a file. In some cases, there is an existing process that reads workspace tests (such as a language server in Java) or it's not much more expensive to get workspace tests than file tests (such as mocha, perhaps). However, some cases, like Go, providing tests for a single file can be done very cheaply and efficiently without needing to involve the workspace.\r\n\r\n\tIn this API we expect the `TestProvider` to, after activation, always provide tests for the visible text editors, and we only request tests for the entire workspace when required (i.e. when the UI needs to enumerate them).\r\n\r\n- We have modeled the test state more closely after the existing `DiagnosticCollection`, where the Test Adapter API uses only events to enumerate tests and does not have a central collection.\r\n\r\n- The Test Adapter API makes the distinction between suites and tests, we do not. They have almost identical capabilities, and in [at least one scenario](https://blog.golang.org/subtests) the 'suites' are more like tests and the leaf 'tests' cannot be run individually.\r\n\r\n- We use object identity rather than ID for referencing tests. This is in line with other items in the VS Code API, including Diagnostics.\r\n\r\n#### Ideas and Open Questions\r\n\r\n- We do not (yet) have a concept of test invalidation and auto-run, which in the test adapter API via the \"retire\" event. We are still looking into how this can best be implemented.\r\n\t\r\n\tIn a golden scenario, invalidation of tests would be done by a language server which can intelligently determine specific tests that should be invalidated when a file or a file dependency changes. Maybe this is still handled by an event on the TestProvider, but if we take a \"Test Protocol\" approach then coordination will be harder.\r\n- As marked in the `todo`, we will expose APIs for other extensions to read test state and build UI, but this is not yet included in the API design.\r\n- How should errors loading tests be handled? Emit diagnostics or have some test-specific code?\r\n\r\n- We would like to support code coverage in testing as well, but that is further down the line.\r\n\r\n- How can we let users learn about/onboard to testing from within VS Code?\r\n\r\n### API\r\n\r\nSee the current working proposal in https://github.com/microsoft/vscode/blob/master/src/vs/vscode.proposed.d.ts (ctrl+f for 107467)", + "performed_via_github_app": null, + "score": 1 + }, + { + "url": "https://api.github.com/repos/microsoft/vscode/issues/105690", + "repository_url": "https://api.github.com/repos/microsoft/vscode", + "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/105690/labels{/name}", + "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/105690/comments", + "events_url": "https://api.github.com/repos/microsoft/vscode/issues/105690/events", + "html_url": "https://github.com/microsoft/vscode/issues/105690", + "id": 688793797, + "node_id": "MDU6SXNzdWU2ODg3OTM3OTc=", + "number": 105690, + "title": "Extension API for Inline Values", + "user": { + "login": "weinand", + "id": 1898161, + "node_id": "MDQ6VXNlcjE4OTgxNjE=", + "avatar_url": "https://avatars.githubusercontent.com/u/1898161?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/weinand", + "html_url": "https://github.com/weinand", + "followers_url": "https://api.github.com/users/weinand/followers", + "following_url": "https://api.github.com/users/weinand/following{/other_user}", + "gists_url": "https://api.github.com/users/weinand/gists{/gist_id}", + "starred_url": "https://api.github.com/users/weinand/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/weinand/subscriptions", + "organizations_url": "https://api.github.com/users/weinand/orgs", + "repos_url": "https://api.github.com/users/weinand/repos", + "events_url": "https://api.github.com/users/weinand/events{/privacy}", + "received_events_url": "https://api.github.com/users/weinand/received_events", + "type": "User", + "site_admin": false + }, + "labels": [ + { + "id": 290465400, + "node_id": "MDU6TGFiZWwyOTA0NjU0MDA=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api", + "name": "api", + "color": "1d76db", + "default": false, + "description": "" + }, + { + "id": 869332220, + "node_id": "MDU6TGFiZWw4NjkzMzIyMjA=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api-proposal", + "name": "api-proposal", + "color": "c5def5", + "default": false, + "description": "" + }, + { + "id": 291054922, + "node_id": "MDU6TGFiZWwyOTEwNTQ5MjI=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/debug", + "name": "debug", + "color": "1d76db", + "default": false, + "description": "Debug viewlet, configurations, breakpoints, adapter issues" + }, + { + "id": 272689392, + "node_id": "MDU6TGFiZWwyNzI2ODkzOTI=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/feature-request", + "name": "feature-request", + "color": "dcdcdc", + "default": false, + "description": "Request for new features or functionality" + } + ], + "state": "open", + "locked": false, + "assignee": { + "login": "weinand", + "id": 1898161, + "node_id": "MDQ6VXNlcjE4OTgxNjE=", + "avatar_url": "https://avatars.githubusercontent.com/u/1898161?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/weinand", + "html_url": "https://github.com/weinand", + "followers_url": "https://api.github.com/users/weinand/followers", + "following_url": "https://api.github.com/users/weinand/following{/other_user}", + "gists_url": "https://api.github.com/users/weinand/gists{/gist_id}", + "starred_url": "https://api.github.com/users/weinand/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/weinand/subscriptions", + "organizations_url": "https://api.github.com/users/weinand/orgs", + "repos_url": "https://api.github.com/users/weinand/repos", + "events_url": "https://api.github.com/users/weinand/events{/privacy}", + "received_events_url": "https://api.github.com/users/weinand/received_events", + "type": "User", + "site_admin": false + }, + "assignees": [ + { + "login": "weinand", + "id": 1898161, + "node_id": "MDQ6VXNlcjE4OTgxNjE=", + "avatar_url": "https://avatars.githubusercontent.com/u/1898161?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/weinand", + "html_url": "https://github.com/weinand", + "followers_url": "https://api.github.com/users/weinand/followers", + "following_url": "https://api.github.com/users/weinand/following{/other_user}", + "gists_url": "https://api.github.com/users/weinand/gists{/gist_id}", + "starred_url": "https://api.github.com/users/weinand/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/weinand/subscriptions", + "organizations_url": "https://api.github.com/users/weinand/orgs", + "repos_url": "https://api.github.com/users/weinand/repos", + "events_url": "https://api.github.com/users/weinand/events{/privacy}", + "received_events_url": "https://api.github.com/users/weinand/received_events", + "type": "User", + "site_admin": false + } + ], + "milestone": { + "url": "https://api.github.com/repos/microsoft/vscode/milestones/142", + "html_url": "https://github.com/microsoft/vscode/milestone/142", + "labels_url": "https://api.github.com/repos/microsoft/vscode/milestones/142/labels", + "id": 6286096, + "node_id": "MDk6TWlsZXN0b25lNjI4NjA5Ng==", + "number": 142, + "title": "February 2021", + "description": "", + "creator": { + "login": "Tyriar", + "id": 2193314, + "node_id": "MDQ6VXNlcjIxOTMzMTQ=", + "avatar_url": "https://avatars.githubusercontent.com/u/2193314?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Tyriar", + "html_url": "https://github.com/Tyriar", + "followers_url": "https://api.github.com/users/Tyriar/followers", + "following_url": "https://api.github.com/users/Tyriar/following{/other_user}", + "gists_url": "https://api.github.com/users/Tyriar/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Tyriar/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Tyriar/subscriptions", + "organizations_url": "https://api.github.com/users/Tyriar/orgs", + "repos_url": "https://api.github.com/users/Tyriar/repos", + "events_url": "https://api.github.com/users/Tyriar/events{/privacy}", + "received_events_url": "https://api.github.com/users/Tyriar/received_events", + "type": "User", + "site_admin": false + }, + "open_issues": 232, + "closed_issues": 300, + "state": "open", + "created_at": "2021-01-07T18:23:10Z", + "updated_at": "2021-02-17T10:48:47Z", + "due_on": null, + "closed_at": null + }, + "comments": 7, + "created_at": "2020-08-30T21:21:23Z", + "updated_at": "2021-02-08T05:21:59Z", + "closed_at": null, + "author_association": "MEMBER", + "active_lock_reason": null, + "body": "Today the \"Show Inline Values\" feature of VS Code's debugger is based on a generic implementation in the VS Code core and provides neither customisability through settings, nor extensibility via extensions.\r\n\r\nAs a consequence, it is not a perfect fit for all languages (e.g. #101797) and sometimes even shows incorrect values because it doesn't understand the scope regions of the underlying language. \r\n\r\nThis features asks for an extension API that either replaces the built-in implementation completely or allows to replace parts of the implementation with custom code.\r\n", + "performed_via_github_app": null, + "score": 1 + } + ] + } + ] } ] \ No newline at end of file diff --git a/.vscode/notebooks/inbox.github-issues b/.vscode/notebooks/inbox.github-issues index f14b59f1483..69c1b7b0aa9 100644 --- a/.vscode/notebooks/inbox.github-issues +++ b/.vscode/notebooks/inbox.github-issues @@ -3,45 +3,56 @@ "kind": 1, "language": "markdown", "value": "## tl;dr: Triage Inbox\n\nAll inbox issues but not those that need more information. These issues need to be triaged, e.g assigned to a user or ask for more information", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", - "value": "$inbox -label:\"needs more info\"" + "value": "$inbox -label:\"needs more info\"", + "editable": true, + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "##### `Config`: defines the inbox query", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", - "value": "$inbox=repo:microsoft/vscode is:open no:assignee -label:feature-request -label:testplan-item -label:plan-item " + "value": "$inbox=repo:microsoft/vscode is:open no:assignee -label:feature-request -label:testplan-item -label:plan-item ", + "editable": true, + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "## Inbox tracking and Issue triage", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 1, "language": "markdown", - "value": "New issues or pull requests submitted by the community are initially triaged by an [automatic classification bot](https://github.com/microsoft/vscode-github-triage-actions/tree/master/classifier-deep). Issues that the bot does not correctly triage are then triaged by a team member. The team rotates the inbox tracker on a weekly basis.\n\nA [mirror](https://github.com/JacksonKearl/testissues/issues) of the VS Code issue stream is available with details about how the bot classifies issues, including feature-area classifications and confidence ratings. Per-category confidence thresholds and feature-area ownership data is maintained in [.github/classifier.json](https://github.com/microsoft/vscode/blob/master/.github/classifier.json). \n\n💡 The bot is being run through a GitHub action that runs every 30 minutes. Give the bot the opportunity to classify an issue before doing it manually.\n\n### Inbox Tracking\n\nThe inbox tracker is responsible for the [global inbox](https://github.com/microsoft/vscode/issues?utf8=%E2%9C%93&q=is%3Aopen+no%3Aassignee+-label%3Afeature-request+-label%3Atestplan-item+-label%3Aplan-item) containing all **open issues and pull requests** that\n- are neither **feature requests** nor **test plan items** nor **plan items** and\n- have **no owner assignment**.\n\nThe **inbox tracker** may perform any step described in our [issue triaging documentation](https://github.com/microsoft/vscode/wiki/Issues-Triaging) but its main responsibility is to route issues to the actual feature area owner.\n\nFeature area owners track the **feature area inbox** containing all **open issues and pull requests** that\n- are personally assigned to them and are not assigned to any milestone\n- are labeled with their feature area label and are not assigned to any milestone.\nThis secondary triage may involve any of the steps described in our [issue triaging documentation](https://github.com/microsoft/vscode/wiki/Issues-Triaging) and results in a fully triaged or closed issue.\n\nThe [github triage extension](https://github.com/microsoft/vscode-github-triage-extension) can be used to assist with triaging — it provides a \"Command Palette\"-style list of triaging actions like assignment, labeling, and triggers for various bot actions.", - "editable": true + "value": "New issues or pull requests submitted by the community are initially triaged by an [automatic classification bot](https://github.com/microsoft/vscode-github-triage-actions/tree/master/classifier-deep). Issues that the bot does not correctly triage are then triaged by a team member. The team rotates the inbox tracker on a weekly basis.\n\nA [mirror](https://github.com/JacksonKearl/testissues/issues) of the VS Code issue stream is available with details about how the bot classifies issues, including feature-area classifications and confidence ratings. Per-category confidence thresholds and feature-area ownership data is maintained in [.github/classifier.json](https://github.com/microsoft/vscode/blob/main/.github/classifier.json). \n\n💡 The bot is being run through a GitHub action that runs every 30 minutes. Give the bot the opportunity to classify an issue before doing it manually.\n\n### Inbox Tracking\n\nThe inbox tracker is responsible for the [global inbox](https://github.com/microsoft/vscode/issues?utf8=%E2%9C%93&q=is%3Aopen+no%3Aassignee+-label%3Afeature-request+-label%3Atestplan-item+-label%3Aplan-item) containing all **open issues and pull requests** that\n- are neither **feature requests** nor **test plan items** nor **plan items** and\n- have **no owner assignment**.\n\nThe **inbox tracker** may perform any step described in our [issue triaging documentation](https://github.com/microsoft/vscode/wiki/Issues-Triaging) but its main responsibility is to route issues to the actual feature area owner.\n\nFeature area owners track the **feature area inbox** containing all **open issues and pull requests** that\n- are personally assigned to them and are not assigned to any milestone\n- are labeled with their feature area label and are not assigned to any milestone.\nThis secondary triage may involve any of the steps described in our [issue triaging documentation](https://github.com/microsoft/vscode/wiki/Issues-Triaging) and results in a fully triaged or closed issue.\n\nThe [github triage extension](https://github.com/microsoft/vscode-github-triage-extension) can be used to assist with triaging — it provides a \"Command Palette\"-style list of triaging actions like assignment, labeling, and triggers for various bot actions.", + "editable": true, + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "## All Inbox Items\n\nAll issues that have no assignee and that have neither **feature requests** nor **test plan items** nor **plan items**.", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", - "value": "$inbox" + "value": "$inbox", + "editable": true, + "outputs": [] } ] \ No newline at end of file diff --git a/.vscode/notebooks/my-work.github-issues b/.vscode/notebooks/my-work.github-issues index fb59e914e78..c72d5cde062 100644 --- a/.vscode/notebooks/my-work.github-issues +++ b/.vscode/notebooks/my-work.github-issues @@ -3,105 +3,132 @@ "kind": 1, "language": "markdown", "value": "##### `Config`: This should be changed every month/milestone", - "editable": true + "editable": true, + "outputs": [] }, { "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\n\n// current milestone name\n$milestone=milestone:\"February 2021\"" + "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\n\n// current milestone name\n$milestone=milestone:\"February 2021\"", + "editable": true, + "outputs": [] }, { "kind": 1, "language": "github-issues", "value": "## Milestone Work", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", - "value": "$repos $milestone assignee:@me is:open" + "value": "$repos $milestone assignee:@me is:open", + "outputs": [] }, { "kind": 1, "language": "github-issues", "value": "## Bugs, Debt, Features...", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "#### My Bugs", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open label:bug" + "value": "$repos assignee:@me is:open label:bug", + "editable": true, + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "#### Debt & Engineering", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open label:debt OR $repos assignee:@me is:open label:engineering" + "value": "$repos assignee:@me is:open label:debt OR $repos assignee:@me is:open label:engineering", + "editable": true, + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "#### Performance 🐌 🔜 🏎", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open label:perf OR $repos assignee:@me is:open label:perf-startup OR $repos assignee:@me is:open label:perf-bloat OR $repos assignee:@me is:open label:freeze-slow-crash-leak" + "value": "$repos assignee:@me is:open label:perf OR $repos assignee:@me is:open label:perf-startup OR $repos assignee:@me is:open label:perf-bloat OR $repos assignee:@me is:open label:freeze-slow-crash-leak", + "editable": true, + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "#### Feature Requests", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open label:feature-request milestone:Backlog sort:reactions-+1-desc" + "value": "$repos assignee:@me is:open label:feature-request milestone:Backlog sort:reactions-+1-desc", + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open milestone:\"Backlog Candidates\"" + "value": "$repos assignee:@me is:open milestone:\"Backlog Candidates\"", + "editable": true, + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "### Personal Inbox\n", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "\n#### Missing Type label", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open type:issue -label:bug -label:\"needs more info\" -label:feature-request -label:under-discussion -label:debt -label:plan-item -label:upstream" + "value": "$repos assignee:@me is:open type:issue -label:bug -label:\"needs more info\" -label:feature-request -label:under-discussion -label:debt -label:plan-item -label:upstream", + "editable": true, + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "#### Not Actionable", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open label:\"needs more info\"" + "value": "$repos assignee:@me is:open label:\"needs more info\"", + "editable": true, + "outputs": [] } ] \ No newline at end of file diff --git a/.vscode/notebooks/papercuts.github-issues b/.vscode/notebooks/papercuts.github-issues index 4da4734ce5d..01bb21518fc 100644 --- a/.vscode/notebooks/papercuts.github-issues +++ b/.vscode/notebooks/papercuts.github-issues @@ -3,42 +3,49 @@ "kind": 1, "language": "markdown", "value": "## Papercuts\n\nThis notebook serves as an ongoing collection of papercut issues that we encounter while dogfooding. With that in mind only promote issues that really turn you off, e.g. issues that make you want to stop using VS Code or its extensions. To mark an issue (bug, feature-request, etc.) as papercut add the labels: `papercut :drop_of_blood:`", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "## All Papercuts\n\nThese are all papercut issues that we encounter while dogfooding vscode or extensions that we author.", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", "value": "repo:microsoft/vscode is:open -label:notebook label:\"papercut :drop_of_blood:\"", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "## Native Notebook", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", "value": "repo:microsoft/vscode is:open label:notebook label:\"papercut :drop_of_blood:\"", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "### My Papercuts", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", "value": "repo:microsoft/vscode is:open assignee:@me label:\"papercut :drop_of_blood:\"", - "editable": true + "editable": true, + "outputs": [] } -] +] \ No newline at end of file diff --git a/README.md b/README.md index ec206f09460..541c6e3773c 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ## The Repository -This repository ("`Code - OSS`") is where we (Microsoft) develop the [Visual Studio Code](https://code.visualstudio.com) product. Not only do we work on code and issues here, we also publish our [roadmap](https://github.com/microsoft/vscode/wiki/Roadmap), [monthly iteration plans](https://github.com/microsoft/vscode/wiki/Iteration-Plans), and our [endgame plans](https://github.com/microsoft/vscode/wiki/Running-the-Endgame). This source code is available to everyone under the standard [MIT license](https://github.com/microsoft/vscode/blob/master/LICENSE.txt). +This repository ("`Code - OSS`") is where we (Microsoft) develop the [Visual Studio Code](https://code.visualstudio.com) product. Not only do we work on code and issues here, we also publish our [roadmap](https://github.com/microsoft/vscode/wiki/Roadmap), [monthly iteration plans](https://github.com/microsoft/vscode/wiki/Iteration-Plans), and our [endgame plans](https://github.com/microsoft/vscode/wiki/Running-the-Endgame). This source code is available to everyone under the standard [MIT license](https://github.com/microsoft/vscode/blob/main/LICENSE.txt). ## Visual Studio Code diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index a34ae1c4247..3bbbbb461e1 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -71,6 +71,7 @@ steps: condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - script: | + set -e npx https://aka.ms/enablesecurefeed standAlone timeoutInMinutes: 5 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) @@ -387,7 +388,3 @@ steps: displayName: Upload configuration (for Bing settings search) condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), ne(variables['VSCODE_PUBLISH'], 'false')) continueOnError: true - - - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 - displayName: "Component Detection" - continueOnError: true diff --git a/build/azure-pipelines/distro-build.yml b/build/azure-pipelines/distro-build.yml index 331fbf9675e..22d6983e7f8 100644 --- a/build/azure-pipelines/distro-build.yml +++ b/build/azure-pipelines/distro-build.yml @@ -1,9 +1,9 @@ trigger: branches: - include: ["master", "release/*"] + include: ["main", "release/*"] pr: branches: - include: ["master", "release/*"] + include: ["main", "release/*"] steps: - task: NodeTool@0 @@ -31,8 +31,8 @@ steps: git remote add distro "https://github.com/$VSCODE_MIXIN_REPO.git" git fetch distro - # Push master branch into oss/master - git push distro origin/master:refs/heads/oss/master + # Push main branch into oss/main + git push distro origin/main:refs/heads/oss/main # Push every release branch into oss/release git for-each-ref --format="%(refname:short)" refs/remotes/origin/release/* | sed 's/^origin\/\(.*\)$/\0:refs\/heads\/oss\/\1/' | xargs git push distro diff --git a/build/azure-pipelines/exploration-build.yml b/build/azure-pipelines/exploration-build.yml index 22e2602d0aa..719e6e469cb 100644 --- a/build/azure-pipelines/exploration-build.yml +++ b/build/azure-pipelines/exploration-build.yml @@ -28,9 +28,9 @@ steps: git config user.name "VSCode" git checkout origin/electron-11.x.y - git merge origin/master + git merge origin/main - # Push master branch into exploration branch + # Push main branch into exploration branch git push origin HEAD:electron-11.x.y displayName: Sync & Merge Exploration diff --git a/build/azure-pipelines/linux/product-build-alpine.yml b/build/azure-pipelines/linux/product-build-alpine.yml index a2bbb119bfb..4a1b8a2c64a 100644 --- a/build/azure-pipelines/linux/product-build-alpine.yml +++ b/build/azure-pipelines/linux/product-build-alpine.yml @@ -69,6 +69,7 @@ steps: displayName: Extract node_modules cache - script: | + set -e npx https://aka.ms/enablesecurefeed standAlone timeoutInMinutes: 5 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) @@ -132,7 +133,3 @@ steps: artifact: vscode-server-linux-alpine-web displayName: Publish web server archive condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 - displayName: "Component Detection" - continueOnError: true diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 9a42f058155..d0c8e0894e7 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -67,6 +67,7 @@ steps: condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['VSCODE_ARCH'], 'x64')) - script: | + set -e npx https://aka.ms/enablesecurefeed standAlone timeoutInMinutes: 5 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) @@ -286,7 +287,3 @@ steps: artifactName: "snap-$(VSCODE_ARCH)" targetPath: .build/linux/snap-tarball condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 - displayName: "Component Detection" - continueOnError: true diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index 4d376246944..011f8d4f7d8 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -5,7 +5,7 @@ schedules: displayName: Mon-Fri at 7:00 branches: include: - - master + - main parameters: - name: VSCODE_QUALITY diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index 08e3f694520..c51ed5644c6 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -50,6 +50,7 @@ steps: displayName: Extract node_modules cache - script: | + set -e npx https://aka.ms/enablesecurefeed standAlone timeoutInMinutes: 5 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) @@ -136,3 +137,9 @@ steps: targetPath: $(Build.ArtifactStagingDirectory)/compilation.tar.gz artifactName: Compilation displayName: Publish compilation artifact + + - 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/publish-types/publish-types.yml b/build/azure-pipelines/publish-types/publish-types.yml index 0e3f4e4daa4..09964dc6ad0 100644 --- a/build/azure-pipelines/publish-types/publish-types.yml +++ b/build/azure-pipelines/publish-types/publish-types.yml @@ -61,7 +61,7 @@ steps: TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`) CHANNEL="G1C14HJ2F" - MESSAGE="DefinitelyTyped/DefinitelyTyped#vscode-types-$TAG_VERSION created. Endgame master, please open this link, examine changes and create a PR:" + MESSAGE="DefinitelyTyped/DefinitelyTyped#vscode-types-$TAG_VERSION created. Endgame champion, please open this link, examine changes and create a PR:" LINK="https://github.com/DefinitelyTyped/DefinitelyTyped/compare/vscode-types-$TAG_VERSION?quick_pull=1&body=Updating%20VS%20Code%20Extension%20API.%20See%20https%3A%2F%2Fgithub.com%2Fmicrosoft%2Fvscode%2Fissues%2F70175%20for%20details." MESSAGE2="[@eamodio, @jrieken, @kmaetzel, @egamma]. Please review and merge PR to publish @types/vscode." diff --git a/build/azure-pipelines/publish-types/update-types.ts b/build/azure-pipelines/publish-types/update-types.ts index bbce67221da..eae002e23a7 100644 --- a/build/azure-pipelines/publish-types/update-types.ts +++ b/build/azure-pipelines/publish-types/update-types.ts @@ -72,7 +72,7 @@ function getNewFileHeader(tag: string) { `/*---------------------------------------------------------------------------------------------`, ` * Copyright (c) Microsoft Corporation. All rights reserved.`, ` * Licensed under the MIT License.`, - ` * See https://github.com/microsoft/vscode/blob/master/LICENSE.txt for license information.`, + ` * See https://github.com/microsoft/vscode/blob/main/LICENSE.txt for license information.`, ` *--------------------------------------------------------------------------------------------*/`, ``, `/**`, diff --git a/build/azure-pipelines/web/product-build-web.yml b/build/azure-pipelines/web/product-build-web.yml index 05aa68fe126..0a8e1c36a88 100644 --- a/build/azure-pipelines/web/product-build-web.yml +++ b/build/azure-pipelines/web/product-build-web.yml @@ -60,6 +60,7 @@ steps: displayName: Extract node_modules cache - script: | + set -e npx https://aka.ms/enablesecurefeed standAlone timeoutInMinutes: 5 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index 035580035b0..5ca1f825865 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -65,8 +65,10 @@ steps: condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true')) displayName: Extract node_modules cache - - script: | - npx https://aka.ms/enablesecurefeed standAlone + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { npx https://aka.ms/enablesecurefeed standAlone } timeoutInMinutes: 5 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) displayName: Switch to Terrapin packages @@ -320,7 +322,3 @@ steps: artifact: vscode-server-win32-$(VSCODE_ARCH)-web displayName: Publish web server archive condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - - - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 - displayName: "Component Detection" - continueOnError: true diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 7ef97f15be3..230082e4ad9 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -49,7 +49,7 @@ let BUNDLED_FILE_HEADER = [ ' * Copyright (c) Microsoft Corporation. All rights reserved.', ' * Version: ' + headerVersion, ' * Released under the MIT license', - ' * https://github.com/microsoft/vscode/blob/master/LICENSE.txt', + ' * https://github.com/microsoft/vscode/blob/main/LICENSE.txt', ' *-----------------------------------------------------------*/', '' ].join('\n'); diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 6d3a369082a..e3ca3514bb6 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -52,13 +52,13 @@ const vscodeResources = [ 'out-build/bootstrap-amd.js', 'out-build/bootstrap-node.js', 'out-build/bootstrap-window.js', - 'out-build/paths.js', 'out-build/vs/**/*.{svg,png,html,jpg}', '!out-build/vs/code/browser/**/*.html', '!out-build/vs/editor/standalone/**/*.svg', 'out-build/vs/base/common/performance.js', 'out-build/vs/base/node/languagePacks.js', 'out-build/vs/base/node/{stdForkStart.js,terminateProcess.sh,cpuUsage.sh,ps.sh}', + 'out-build/vs/base/node/userDataPath.js', 'out-build/vs/base/browser/ui/codicons/codicon/**', 'out-build/vs/base/parts/sandbox/electron-browser/preload.js', 'out-build/vs/workbench/browser/media/*-theme.css', @@ -284,6 +284,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op let result = all .pipe(util.skipDirectories()) .pipe(util.fixWin32DirectoryPermissions()) + .pipe(filter(['**', '!**/.github/**'], { dot: true })) // https://github.com/microsoft/vscode/issues/116523 .pipe(electron(_.extend({}, config, { platform, arch: arch === 'armhf' ? 'arm' : arch, ffmpegChromium: true }))) .pipe(filter(['**', '!LICENSE', '!LICENSES.chromium.html', '!version'], { dot: true })); @@ -526,7 +527,7 @@ gulp.task(task.define( if (!shouldSetupSettingsSearch()) { const branch = process.env.BUILD_SOURCEBRANCH; - console.log(`Only runs on master and release branches, not ${branch}`); + console.log(`Only runs on main and release branches, not ${branch}`); return; } @@ -552,21 +553,21 @@ gulp.task(task.define( function shouldSetupSettingsSearch() { const branch = process.env.BUILD_SOURCEBRANCH; - return branch && (/\/master$/.test(branch) || branch.indexOf('/release/') >= 0); + return branch && (/\/main$/.test(branch) || branch.indexOf('/release/') >= 0); } function getSettingsSearchBuildId(packageJson) { try { const branch = process.env.BUILD_SOURCEBRANCH; const branchId = branch.indexOf('/release/') >= 0 ? 0 : - /\/master$/.test(branch) ? 1 : + /\/main$/.test(branch) ? 1 : 2; // Some unexpected branch const out = cp.execSync(`git rev-list HEAD --count`); const count = parseInt(out.toString()); // - // 1.25.1, 1,234,567 commits, master = 1250112345671 + // 1.25.1, 1,234,567 commits, main = 1250112345671 return util.versionStringToNumber(packageJson.version) * 1e8 + count * 10 + branchId; } catch (e) { throw new Error('Could not determine build number: ' + e.toString()); diff --git a/build/monaco/README-npm.md b/build/monaco/README-npm.md index ee0ffc6e95c..737e06bbc5c 100644 --- a/build/monaco/README-npm.md +++ b/build/monaco/README-npm.md @@ -11,4 +11,4 @@ a good page describing the code editor's features is [here](https://code.visuals This npm module contains the core editor functionality, as it comes from the [vscode repository](https://github.com/microsoft/vscode). ## License -[MIT](https://github.com/microsoft/vscode/blob/master/LICENSE.txt) +[MIT](https://github.com/microsoft/vscode/blob/main/LICENSE.txt) diff --git a/build/package.json b/build/package.json index 7de0c8f734c..a6ece3f020f 100644 --- a/build/package.json +++ b/build/package.json @@ -51,7 +51,7 @@ "p-limit": "^3.1.0", "plist": "^3.0.1", "source-map": "0.6.1", - "typescript": "4.2.0-dev.20201207", + "typescript": "^4.3.0-dev.20210216", "vsce": "1.48.0", "vscode-universal": "deepak1556/universal#61454d96223b774c53cda10f72c2098c0ce02d58" }, diff --git a/build/yarn.lock b/build/yarn.lock index a111c831be1..28014afaad9 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -1695,16 +1695,16 @@ typed-rest-client@^0.9.0: tunnel "0.0.4" underscore "1.8.3" -typescript@4.2.0-dev.20201207: - version "4.2.0-dev.20201207" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.0-dev.20201207.tgz#19a34bc7d2d42a7467c512c63f135587ac848807" - integrity sha512-fPHBDi/fgdX4WiRC7cFVv/aL069PgUaDWuLYUSHatWZujz/Lkc9bkf/zL3rKdNSCxlNKAMs3fhJv/yompOphZA== - typescript@^4.1.3: version "4.1.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7" integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== +typescript@^4.3.0-dev.20210216: + version "4.3.0-dev.20210216" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.0-dev.20210216.tgz#233327e6094008c02265ba140f8d9ece9133421e" + integrity sha512-pJLcC/kqnE+0rftTRc2/gYBkz9nl+kJfaU8sSOLYnzUvD8p+LOZMzXfaLoKPdGFJ6U9+Ox/sYV9HBTJVEjSTYg== + uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.5.tgz#0c65f15f815aa08b560a61ce8b4db7ffc3f45376" diff --git a/extensions/configuration-editing/src/configurationEditingMain.ts b/extensions/configuration-editing/src/configurationEditingMain.ts index a3ef34f3d83..28bcda61df2 100644 --- a/extensions/configuration-editing/src/configurationEditingMain.ts +++ b/extensions/configuration-editing/src/configurationEditingMain.ts @@ -22,6 +22,9 @@ export function activate(context: vscode.ExtensionContext): void { // task.json variable suggestions context.subscriptions.push(registerVariableCompletions('**/tasks.json')); + + // keybindings.json/package.json context key suggestions + context.subscriptions.push(registerContextKeyCompletions()); } function registerSettingsCompletions(): vscode.Disposable { @@ -136,3 +139,45 @@ vscode.languages.registerDocumentSymbolProvider({ pattern: '**/launch.json', lan return result; } }, { label: 'Launch Targets' }); + +function registerContextKeyCompletions(): vscode.Disposable { + type ContextKeyInfo = { key: string, type?: string, description?: string }; + return vscode.languages.registerCompletionItemProvider( + [{ language: 'json', pattern: '**/package.json' }, { language: 'jsonc', pattern: '**/keybindings.json' }], + { + async provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken) { + + const location = getLocation(document.getText(), document.offsetAt(position)); + + if (location.isAtPropertyKey) { + return; + } + + if (!location.matches(['*', 'when']) && !location.matches(['contributes', 'menus', '*', '*', 'when'])) { + return; + } + + const replacing = document.getWordRangeAtPosition(position, /[^"\s]+/); + if (!replacing) { + return; + } + const inserting = replacing.with(undefined, position); + + const data = await vscode.commands.executeCommand('getContextKeyInfo'); + if (token.isCancellationRequested || !data) { + return; + } + + const result = new vscode.CompletionList(); + for (const item of data) { + const completion = new vscode.CompletionItem(item.key, vscode.CompletionItemKind.Constant); + completion.detail = item.type; + completion.range = { replacing, inserting }; + completion.documentation = item.description; + result.items.push(completion); + } + return result; + } + } + ); +} diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index c786c715cde..0766cda7bc4 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -212,6 +212,11 @@ "type": "number", "default": 0.3, "description": "%emmetPreferencesCssFuzzySearchMinScore%" + }, + "output.reverseAttributes": { + "type": "boolean", + "default": false, + "description": "%emmetPreferencesOutputReverseAttributes%" } } }, @@ -430,8 +435,7 @@ "deps": "yarn add vscode-emmet-helper" }, "devDependencies": { - "@types/node": "^12.19.9", - "emmet": "https://github.com/rzhao271/emmet.git#1b2df677d8925ef5ea6da9df8845968403979a0a" + "@types/node": "^12.19.9" }, "dependencies": { "@emmetio/abbreviation": "^2.2.0", @@ -439,7 +443,7 @@ "@emmetio/html-matcher": "^0.3.3", "@emmetio/math-expression": "^1.0.4", "image-size": "^0.5.2", - "vscode-emmet-helper": "2.2.4", + "vscode-emmet-helper": "^2.3.0", "vscode-languageserver-textdocument": "^1.0.1" } } diff --git a/extensions/emmet/package.nls.json b/extensions/emmet/package.nls.json index 2a1add8935e..8e168ec4c4e 100644 --- a/extensions/emmet/package.nls.json +++ b/extensions/emmet/package.nls.json @@ -55,5 +55,6 @@ "emmetPreferencesCssOProperties": "Comma separated CSS properties that get the 'o' vendor prefix when used in Emmet abbreviation that starts with `-`. Set to empty string to always avoid the 'o' prefix.", "emmetPreferencesCssMsProperties": "Comma separated CSS properties that get the 'ms' vendor prefix when used in Emmet abbreviation that starts with `-`. Set to empty string to always avoid the 'ms' prefix.", "emmetPreferencesCssFuzzySearchMinScore": "The minimum score (from 0 to 1) that fuzzy-matched abbreviation should achieve. Lower values may produce many false-positive matches, higher values may reduce possible matches.", - "emmetOptimizeStylesheetParsing": "When set to `false`, the whole file is parsed to determine if current position is valid for expanding Emmet abbreviations. When set to `true`, only the content around the current position in css/scss/less files is parsed." + "emmetOptimizeStylesheetParsing": "When set to `false`, the whole file is parsed to determine if current position is valid for expanding Emmet abbreviations. When set to `true`, only the content around the current position in css/scss/less files is parsed.", + "emmetPreferencesOutputReverseAttributes": "If `true`, reverses attribute merging directions when resolving snippets." } diff --git a/extensions/emmet/yarn.lock b/extensions/emmet/yarn.lock index bebce02cf48..e9ac076c3f0 100644 --- a/extensions/emmet/yarn.lock +++ b/extensions/emmet/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@emmetio/abbreviation@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@emmetio/abbreviation/-/abbreviation-2.2.0.tgz#9f8dedbdb00e3136d6d37c6415375c82c0bb477f" - integrity sha512-NPGVUmnr7cLj4i6MKS4c8NjuoIIJROrruJl/8nXsp2MdbDRHvtfq25foySvv/NbfqTQm+P9JzVLDD9JxGIpvkQ== +"@emmetio/abbreviation@^2.2.0", "@emmetio/abbreviation@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@emmetio/abbreviation/-/abbreviation-2.2.1.tgz#d9458fe1f09fe042f019c48aa681165ba613a48d" + integrity sha512-uUNwNgbH0JPlrdXhy8VQbNPLLG7abMvOaLVMblx22i68Rl9r+2N235ALgIYFUty1yXC9DkVw6xMbz/D4QVARcQ== dependencies: "@emmetio/scanner" "^1.0.0" @@ -54,15 +54,16 @@ integrity sha1-Rs/+oRmgoAMxKiHC2bVijLX81EI= "@types/node@^12.19.9": - version "12.19.15" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.19.15.tgz#0de7e978fb43db62da369db18ea088a63673c182" - integrity sha512-lowukE3GUI+VSYSu6VcBXl14d61Rp5hA1D+61r16qnwC0lYNSqdxcvRh0pswejorHfS+HgwBasM8jLXz0/aOsw== + version "12.20.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.0.tgz#692dfdecd6c97f5380c42dd50f19261f9f604deb" + integrity sha512-0/41wHcurotvSOTHQUFkgL702c3pyWR1mToSrrX3pGPvGfpHTv3Ksx0M4UVuU5VJfjVb62Eyr1eKO1tWNUCg2Q== -"emmet@https://github.com/rzhao271/emmet.git#1b2df677d8925ef5ea6da9df8845968403979a0a": - version "2.3.0" - resolved "https://github.com/rzhao271/emmet.git#1b2df677d8925ef5ea6da9df8845968403979a0a" +emmet@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/emmet/-/emmet-2.3.1.tgz#77614d949d1d01e5c248d08043a13a7f4d539e47" + integrity sha512-u8h++9u3y9QWhn0imUXfQO+s80To5MGD97zd/00wGC39CfNGBPe//ZKepJz9I1LQ2FDRXHrn+e3JaN/53Y5z6A== dependencies: - "@emmetio/abbreviation" "^2.2.0" + "@emmetio/abbreviation" "^2.2.1" "@emmetio/css-abbreviation" "^2.1.2" image-size@^0.5.2: @@ -75,12 +76,12 @@ jsonc-parser@^2.3.0: resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.1.tgz#59549150b133f2efacca48fe9ce1ec0659af2342" integrity sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg== -vscode-emmet-helper@2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-2.2.4.tgz#8ab86d2b7fe9e6270b4c77c9fd8d1eb8f3f4c401" - integrity sha512-1N6bMzP1ZzkDGzamvsKxQ/lOmBc4+OQdj0dA2C9A5PSeYV9gh5xbJ061sm+VyFHOGZE+VyUQq5m/WFmFsLbKnA== +vscode-emmet-helper@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-2.3.0.tgz#a98357ad5ac9c71d7c00396f22b7963b1a74cc5c" + integrity sha512-QhU8+HlynMuUkqBYgA3wIDTSsUNkw8GWxLR214Hjvwr0lkFZa0CRqW/PzAI1CFREjSrTxJYQvkVnbfatZzKAuA== dependencies: - emmet "https://github.com/rzhao271/emmet.git#1b2df677d8925ef5ea6da9df8845968403979a0a" + emmet "^2.3.0" jsonc-parser "^2.3.0" vscode-languageserver-textdocument "^1.0.1" vscode-languageserver-types "^3.15.1" diff --git a/extensions/github-authentication/package.json b/extensions/github-authentication/package.json index 0fe917632fe..726949d02e0 100644 --- a/extensions/github-authentication/package.json +++ b/extensions/github-authentication/package.json @@ -53,7 +53,7 @@ "vscode:prepublish": "npm run compile" }, "dependencies": { - "node-fetch": "2.6.0", + "node-fetch": "2.6.1", "uuid": "8.1.0", "vscode-extension-telemetry": "0.1.1", "vscode-nls": "^4.1.2" diff --git a/extensions/github-authentication/src/extension.ts b/extensions/github-authentication/src/extension.ts index c9b76d81670..7ae1e1d2110 100644 --- a/extensions/github-authentication/src/extension.ts +++ b/extensions/github-authentication/src/extension.ts @@ -14,7 +14,7 @@ export async function activate(context: vscode.ExtensionContext) { const telemetryReporter = new TelemetryReporter(name, version, aiKey); context.subscriptions.push(vscode.window.registerUriHandler(uriHandler)); - const loginService = new GitHubAuthenticationProvider(context); + const loginService = new GitHubAuthenticationProvider(context, telemetryReporter); await loginService.initialize(context); @@ -24,8 +24,7 @@ export async function activate(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.authentication.registerAuthenticationProvider('github', 'GitHub', { onDidChangeSessions: onDidChangeSessions.event, - getAllSessions: () => Promise.resolve(loginService.sessions), - getSessions: (scopes: string[]) => loginService.getSessions(scopes), + getSessions: (scopes?: string[]) => loginService.getSessions(scopes), createSession: async (scopeList: string[]) => { try { /* __GDPR__ diff --git a/extensions/github-authentication/src/github.ts b/extensions/github-authentication/src/github.ts index 89893c94ccd..595d13147a1 100644 --- a/extensions/github-authentication/src/github.ts +++ b/extensions/github-authentication/src/github.ts @@ -9,6 +9,7 @@ import { Keychain } from './common/keychain'; import { GitHubServer, NETWORK_ERROR } from './githubServer'; import Logger from './common/logger'; import { arrayEquals } from './common/utils'; +import TelemetryReporter from 'vscode-extension-telemetry'; export const onDidChangeSessions = new vscode.EventEmitter(); @@ -25,12 +26,13 @@ interface SessionData { export class GitHubAuthenticationProvider { private _sessions: vscode.AuthenticationSession[] = []; - private _githubServer = new GitHubServer(); + private _githubServer: GitHubServer; private _keychain: Keychain; - constructor(context: vscode.ExtensionContext) { + constructor(context: vscode.ExtensionContext, telemetryReporter: TelemetryReporter) { this._keychain = new Keychain(context); + this._githubServer = new GitHubServer(telemetryReporter); } public async initialize(context: vscode.ExtensionContext): Promise { @@ -44,8 +46,10 @@ export class GitHubAuthenticationProvider { context.subscriptions.push(context.secrets.onDidChange(() => this.checkForUpdates())); } - async getSessions(scopes: string[]): Promise { - return this._sessions.filter(session => arrayEquals(session.scopes, scopes)); + async getSessions(scopes?: string[]): Promise { + return scopes + ? this._sessions.filter(session => arrayEquals(session.scopes, scopes)) + : this._sessions; } private async verifySessions(): Promise { @@ -53,6 +57,7 @@ export class GitHubAuthenticationProvider { const verificationPromises = this._sessions.map(async session => { try { await this._githubServer.getUserInfo(session.accessToken); + this._githubServer.checkIsEdu(session.accessToken); verifiedSessions.push(session); } catch (e) { // Remove sessions that return unauthorized response @@ -161,6 +166,7 @@ export class GitHubAuthenticationProvider { public async createSession(scopes: string): Promise { const token = await this._githubServer.login(scopes); const session = await this.tokenToSession(token, scopes.split(' ')); + this._githubServer.checkIsEdu(token); await this.setToken(session); return session; } diff --git a/extensions/github-authentication/src/githubServer.ts b/extensions/github-authentication/src/githubServer.ts index b50285c9ab2..13ec13b735b 100644 --- a/extensions/github-authentication/src/githubServer.ts +++ b/extensions/github-authentication/src/githubServer.ts @@ -9,6 +9,7 @@ import fetch, { Response } from 'node-fetch'; import { v4 as uuid } from 'uuid'; import { PromiseAdapter, promiseFromEvent } from './common/utils'; import Logger from './common/logger'; +import TelemetryReporter from 'vscode-extension-telemetry'; const localize = nls.loadMessageBundle(); @@ -41,6 +42,8 @@ export class GitHubServer { private _pendingStates = new Map(); private _codeExchangePromises = new Map>(); + constructor(private readonly telemetryReporter: TelemetryReporter) { } + private isTestEnvironment(url: vscode.Uri): boolean { return url.authority === 'vscode-web-test-playground.azurewebsites.net' || url.authority.startsWith('localhost:'); } @@ -210,4 +213,36 @@ export class GitHubServer { throw new Error(result.statusText); } } + + public async checkIsEdu(token: string): Promise { + try { + const result = await fetch('https://education.github.com/api/user', { + headers: { + Authorization: `token ${token}`, + 'faculty-check-preview': 'true', + 'User-Agent': 'Visual-Studio-Code' + } + }); + + if (result.ok) { + const json: { student: boolean, faculty: boolean } = await result.json(); + + /* __GDPR__ + "session" : { + "isEdu": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } + } + */ + this.telemetryReporter.sendTelemetryEvent('session', { + isEdu: json.student + ? 'student' + : json.faculty + ? 'faculty' + : 'none' + }); + } + } catch (e) { + // No-op + } + + } } diff --git a/extensions/github-authentication/yarn.lock b/extensions/github-authentication/yarn.lock index 1da6beed526..5220f268c51 100644 --- a/extensions/github-authentication/yarn.lock +++ b/extensions/github-authentication/yarn.lock @@ -84,10 +84,10 @@ mime-types@^2.1.12: dependencies: mime-db "1.44.0" -node-fetch@2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" - integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== +node-fetch@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== semver@^5.3.0: version "5.7.1" diff --git a/extensions/github/yarn.lock b/extensions/github/yarn.lock index 5e790ac2903..05a0b4cf6f9 100644 --- a/extensions/github/yarn.lock +++ b/extensions/github/yarn.lock @@ -125,9 +125,9 @@ is-plain-object@^3.0.0: integrity sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g== node-fetch@^2.3.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" - integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== once@^1.4.0: version "1.4.0" diff --git a/extensions/microsoft-authentication/package.json b/extensions/microsoft-authentication/package.json index e6416410be3..54405b6939f 100644 --- a/extensions/microsoft-authentication/package.json +++ b/extensions/microsoft-authentication/package.json @@ -47,7 +47,7 @@ }, "dependencies": { "buffer": "^5.6.0", - "node-fetch": "^2.6.0", + "node-fetch": "2.6.1", "randombytes": "github:rmacfarlane/randombytes#b28d4ecee46262801ea09f15fa1f1513a05c5971", "sha.js": "2.4.11", "stream": "0.0.2", diff --git a/extensions/microsoft-authentication/src/AADHelper.ts b/extensions/microsoft-authentication/src/AADHelper.ts index fbb4e10a0e8..b0631344346 100644 --- a/extensions/microsoft-authentication/src/AADHelper.ts +++ b/extensions/microsoft-authentication/src/AADHelper.ts @@ -300,7 +300,11 @@ export class AzureActiveDirectoryService { return Promise.all(this._tokens.map(token => this.convertToSession(token))); } - async getSessions(scopes: string[]): Promise { + async getSessions(scopes?: string[]): Promise { + if (!scopes) { + return this.sessions; + } + const orderedScopes = scopes.sort().join(' '); const matchingTokens = this._tokens.filter(token => token.scope === orderedScopes); return Promise.all(matchingTokens.map(token => this.convertToSession(token))); diff --git a/extensions/microsoft-authentication/src/extension.ts b/extensions/microsoft-authentication/src/extension.ts index 584a4027b64..41b8690f344 100644 --- a/extensions/microsoft-authentication/src/extension.ts +++ b/extensions/microsoft-authentication/src/extension.ts @@ -20,7 +20,6 @@ export async function activate(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.authentication.registerAuthenticationProvider('microsoft', 'Microsoft', { onDidChangeSessions: onDidChangeSessions.event, - getAllSessions: () => Promise.resolve(loginService.sessions), getSessions: (scopes: string[]) => loginService.getSessions(scopes), createSession: async (scopes: string[]) => { try { diff --git a/extensions/microsoft-authentication/yarn.lock b/extensions/microsoft-authentication/yarn.lock index 58e34ee843a..b58b488d970 100644 --- a/extensions/microsoft-authentication/yarn.lock +++ b/extensions/microsoft-authentication/yarn.lock @@ -126,10 +126,10 @@ mime-types@^2.1.12: dependencies: mime-db "1.44.0" -node-fetch@^2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" - integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== +node-fetch@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== "randombytes@github:rmacfarlane/randombytes#b28d4ecee46262801ea09f15fa1f1513a05c5971": version "2.1.0" diff --git a/extensions/theme-abyss/themes/abyss-color-theme.json b/extensions/theme-abyss/themes/abyss-color-theme.json index 8e97bbb6dc1..391b5af61b7 100644 --- a/extensions/theme-abyss/themes/abyss-color-theme.json +++ b/extensions/theme-abyss/themes/abyss-color-theme.json @@ -303,7 +303,7 @@ "list.activeSelectionBackground": "#08286b", // "list.activeSelectionForeground": "", - "list.focusBackground": "#08286b", + "quickInput.list.focusBackground": "#08286b", "list.hoverBackground": "#061940", "list.inactiveSelectionBackground": "#152037", "list.dropBackground": "#041D52", 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 3453c53dc2b..24ba31854fe 100644 --- a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json +++ b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json @@ -10,7 +10,7 @@ "list.highlightForeground": "#e3b583", "list.activeSelectionBackground": "#7c5021", "list.hoverBackground": "#7c502166", - "list.focusBackground": "#7c5021AA", + "quickInput.list.focusBackground": "#7c5021AA", "list.inactiveSelectionBackground": "#645342", "pickerGroup.foreground": "#e3b583", "pickerGroup.border": "#e3b583", diff --git a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json index bd8b896c011..9ca62cc15e3 100644 --- a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json +++ b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json @@ -3,7 +3,7 @@ "colors": { "dropdown.background": "#525252", "list.activeSelectionBackground": "#707070", - "list.focusBackground": "#707070", + "quickInput.list.focusBackground": "#707070", "list.inactiveSelectionBackground": "#4e4e4e", "list.hoverBackground": "#444444", "list.highlightForeground": "#e58520", diff --git a/extensions/theme-monokai/themes/monokai-color-theme.json b/extensions/theme-monokai/themes/monokai-color-theme.json index 969455d01e3..100d291ddad 100644 --- a/extensions/theme-monokai/themes/monokai-color-theme.json +++ b/extensions/theme-monokai/themes/monokai-color-theme.json @@ -9,7 +9,7 @@ "colors": { "dropdown.background": "#414339", "list.activeSelectionBackground": "#75715E", - "list.focusBackground": "#414339", + "quickInput.list.focusBackground": "#414339", "dropdown.listBackground": "#1e1f1c", "list.inactiveSelectionBackground": "#414339", "list.hoverBackground": "#3e3d32", diff --git a/extensions/theme-quietlight/themes/quietlight-color-theme.json b/extensions/theme-quietlight/themes/quietlight-color-theme.json index e395b99e3eb..376c5fda7a3 100644 --- a/extensions/theme-quietlight/themes/quietlight-color-theme.json +++ b/extensions/theme-quietlight/themes/quietlight-color-theme.json @@ -473,7 +473,7 @@ "pickerGroup.foreground": "#A6B39B", "pickerGroup.border": "#749351", "list.activeSelectionForeground": "#6c6c6c", - "list.focusBackground": "#CADEB9", + "quickInput.list.focusBackground": "#CADEB9", "list.hoverBackground": "#e0e0e0", "list.activeSelectionBackground": "#c4d9b1", "list.inactiveSelectionBackground": "#d3dbcd", diff --git a/extensions/theme-red/themes/Red-color-theme.json b/extensions/theme-red/themes/Red-color-theme.json index 8eeda13456e..c3e728b7f39 100644 --- a/extensions/theme-red/themes/Red-color-theme.json +++ b/extensions/theme-red/themes/Red-color-theme.json @@ -49,7 +49,7 @@ "list.activeSelectionBackground": "#880000", "list.inactiveSelectionBackground": "#770000", "list.dropBackground": "#662222", - "list.focusBackground": "#660000", + "quickInput.list.focusBackground": "#660000", "list.highlightForeground": "#ff4444", "pickerGroup.foreground": "#cc9999", "pickerGroup.border": "#ff000033", diff --git a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json index 8a9deb0cd4b..b887521b605 100644 --- a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json +++ b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json @@ -350,7 +350,7 @@ "list.activeSelectionBackground": "#005A6F", // "list.activeSelectionForeground": "", - "list.focusBackground": "#005A6F", + "quickInput.list.focusBackground": "#005A6F", "list.hoverBackground": "#004454AA", "list.inactiveSelectionBackground": "#00445488", "list.dropBackground": "#00445488", diff --git a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json index 427b7c3c359..1efe1e000e3 100644 --- a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json +++ b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json @@ -350,7 +350,7 @@ "list.activeSelectionBackground": "#DFCA88", "list.activeSelectionForeground": "#6C6C6C", - "list.focusBackground": "#DFCA8866", + "quickInput.list.focusBackground": "#DFCA8866", "list.hoverBackground": "#DFCA8844", "list.inactiveSelectionBackground": "#D1CBB8", "list.highlightForeground": "#B58900", 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 91c135342f1..e4ae768d9f5 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 @@ -5,7 +5,7 @@ "errorForeground": "#a92049", "input.background": "#001733", "dropdown.background": "#001733", - "list.focusBackground": "#ffffff60", + "quickInput.list.focusBackground": "#ffffff60", "list.activeSelectionBackground": "#ffffff60", "list.inactiveSelectionBackground": "#ffffff40", "list.hoverBackground": "#ffffff30", diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index 228a9681278..7e576805d5c 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -116,6 +116,27 @@ } ] } + ], + "notebookProvider": [ + { + "viewType": "notebookCoreTest", + "displayName": "Notebook Core Test", + "selector": [ + { + "filenamePattern": "*.vsctestnb", + "excludeFileNamePattern": "" + } + ] + }, + { + "viewType": "notebook.nbdtest", + "displayName": "notebook.nbdtest", + "selector": [ + { + "filenamePattern": "**/*.nbdtest" + } + ] + } ] }, "scripts": { diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts new file mode 100644 index 00000000000..0d5ecc671fa --- /dev/null +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts @@ -0,0 +1,235 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import * as vscode from 'vscode'; +import * as utils from '../utils'; + +suite('Notebook Document', function () { + + const contentProvider = new class implements vscode.NotebookContentProvider { + async openNotebook(uri: vscode.Uri, _openContext: vscode.NotebookDocumentOpenContext): Promise { + return { + cells: [{ cellKind: vscode.NotebookCellKind.Code, source: uri.toString(), language: 'javascript', metadata: new vscode.NotebookCellMetadata(), outputs: [] }], + metadata: new vscode.NotebookDocumentMetadata() + }; + } + async resolveNotebook(_document: vscode.NotebookDocument, _webview: vscode.NotebookCommunication) { + // + } + async saveNotebook(_document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) { + // + } + async saveNotebookAs(_targetResource: vscode.Uri, _document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) { + // + } + async backupNotebook(_document: vscode.NotebookDocument, _context: vscode.NotebookDocumentBackupContext, _cancellation: vscode.CancellationToken) { + return { id: '', delete() { } }; + } + }; + + const disposables: vscode.Disposable[] = []; + + suiteTeardown(async function () { + // utils.assertNoRpc(); + await utils.revertAllDirty(); + await utils.closeAllEditors(); + utils.disposeAll(disposables); + disposables.length = 0; + + for (let doc of vscode.notebook.notebookDocuments) { + assert.strictEqual(doc.isDirty, false, doc.uri.toString()); + } + }); + + suiteSetup(function () { + disposables.push(vscode.notebook.registerNotebookContentProvider('notebook.nbdtest', contentProvider)); + }); + + test('cannot register sample provider multiple times', function () { + assert.throws(() => { + vscode.notebook.registerNotebookContentProvider('notebook.nbdtest', contentProvider); + }); + }); + + test('cannot open unknown types', async function () { + try { + await vscode.notebook.openNotebookDocument(vscode.Uri.parse('some:///thing.notTypeKnown')); + assert.ok(false); + } catch { + assert.ok(true); + } + }); + + test('document basics', async function () { + const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest'); + const notebook = await vscode.notebook.openNotebookDocument(uri); + + assert.strictEqual(notebook.uri.toString(), uri.toString()); + assert.strictEqual(notebook.isDirty, false); + assert.strictEqual(notebook.isUntitled, false); + assert.strictEqual(notebook.cells.length, 1); + + assert.strictEqual(notebook.viewType, 'notebook.nbdtest'); + }); + + test('notebook open/close, notebook ready when cell-document open event is fired', async function () { + const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest'); + let didHappen = false; + const p = utils.asPromise(vscode.workspace.onDidOpenTextDocument).then(doc => { + if (doc.uri.scheme !== 'vscode-notebook-cell') { + return; + } + const notebook = vscode.notebook.notebookDocuments.find(notebook => { + const cell = notebook.cells.find(cell => cell.document === doc); + return Boolean(cell); + }); + assert.ok(notebook, `notebook for cell ${doc.uri} NOT found`); + didHappen = true; + }); + + await vscode.notebook.openNotebookDocument(uri); + await p; + assert.strictEqual(didHappen, true); + }); + + test('notebook open/close, all cell-documents are ready', async function () { + const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest'); + + const p = utils.asPromise(vscode.notebook.onDidOpenNotebookDocument).then(notebook => { + for (let cell of notebook.cells) { + const doc = vscode.workspace.textDocuments.find(doc => doc.uri.toString() === cell.uri.toString()); + assert.ok(doc); + assert.strictEqual(doc.notebook === notebook, true); + assert.strictEqual(doc === cell.document, true); + assert.strictEqual(doc?.languageId, cell.language); + assert.strictEqual(doc?.isDirty, false); + assert.strictEqual(doc?.isClosed, false); + } + }); + + await vscode.notebook.openNotebookDocument(uri); + await p; + }); + + + test('workspace edit API (replaceCells)', async function () { + const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest'); + + const document = await vscode.notebook.openNotebookDocument(uri); + assert.strictEqual(document.cells.length, 1); + + // inserting two new cells + { + const edit = new vscode.WorkspaceEdit(); + edit.replaceNotebookCells(document.uri, 0, 0, [{ + cellKind: vscode.NotebookCellKind.Markdown, + language: 'markdown', + metadata: undefined, + outputs: [], + source: 'new_markdown' + }, { + cellKind: vscode.NotebookCellKind.Code, + language: 'fooLang', + metadata: undefined, + outputs: [], + source: 'new_code' + }]); + + const success = await vscode.workspace.applyEdit(edit); + assert.strictEqual(success, true); + } + + assert.strictEqual(document.cells.length, 3); + assert.strictEqual(document.cells[0].document.getText(), 'new_markdown'); + assert.strictEqual(document.cells[1].document.getText(), 'new_code'); + + // deleting cell 1 and 3 + { + const edit = new vscode.WorkspaceEdit(); + edit.replaceNotebookCells(document.uri, 0, 1, []); + edit.replaceNotebookCells(document.uri, 2, 3, []); + const success = await vscode.workspace.applyEdit(edit); + assert.strictEqual(success, true); + } + + assert.strictEqual(document.cells.length, 1); + assert.strictEqual(document.cells[0].document.getText(), 'new_code'); + + // replacing all cells + { + const edit = new vscode.WorkspaceEdit(); + edit.replaceNotebookCells(document.uri, 0, 1, [{ + cellKind: vscode.NotebookCellKind.Markdown, + language: 'markdown', + metadata: undefined, + outputs: [], + source: 'new2_markdown' + }, { + cellKind: vscode.NotebookCellKind.Code, + language: 'fooLang', + metadata: undefined, + outputs: [], + source: 'new2_code' + }]); + const success = await vscode.workspace.applyEdit(edit); + assert.strictEqual(success, true); + } + assert.strictEqual(document.cells.length, 2); + assert.strictEqual(document.cells[0].document.getText(), 'new2_markdown'); + assert.strictEqual(document.cells[1].document.getText(), 'new2_code'); + + // remove all cells + { + const edit = new vscode.WorkspaceEdit(); + edit.replaceNotebookCells(document.uri, 0, document.cells.length, []); + const success = await vscode.workspace.applyEdit(edit); + assert.strictEqual(success, true); + } + assert.strictEqual(document.cells.length, 0); + }); + + test('workspace edit API (replaceCells, event)', async function () { + const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest'); + const document = await vscode.notebook.openNotebookDocument(uri); + assert.strictEqual(document.cells.length, 1); + + const edit = new vscode.WorkspaceEdit(); + edit.replaceNotebookCells(document.uri, 0, 0, [{ + cellKind: vscode.NotebookCellKind.Markdown, + language: 'markdown', + metadata: undefined, + outputs: [], + source: 'new_markdown' + }, { + cellKind: vscode.NotebookCellKind.Code, + language: 'fooLang', + metadata: undefined, + outputs: [], + source: 'new_code' + }]); + + const event = utils.asPromise(vscode.notebook.onDidChangeNotebookCells); + + const success = await vscode.workspace.applyEdit(edit); + assert.strictEqual(success, true); + + const data = await event; + + // check document + assert.strictEqual(document.cells.length, 3); + assert.strictEqual(document.cells[0].document.getText(), 'new_markdown'); + assert.strictEqual(document.cells[1].document.getText(), 'new_code'); + + // check event data + assert.strictEqual(data.document === document, true); + assert.strictEqual(data.changes.length, 1); + assert.strictEqual(data.changes[0].deletedCount, 0); + assert.strictEqual(data.changes[0].deletedItems.length, 0); + assert.strictEqual(data.changes[0].items.length, 2); + assert.strictEqual(data.changes[0].items[0], document.cells[0]); + assert.strictEqual(data.changes[0].items[1], document.cells[1]); + }); +}); diff --git a/extensions/vscode-notebook-tests/src/notebook.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts similarity index 79% rename from extensions/vscode-notebook-tests/src/notebook.test.ts rename to extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts index 0117e41160a..c21be1935ab 100644 --- a/extensions/vscode-notebook-tests/src/notebook.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts @@ -6,52 +6,13 @@ import 'mocha'; import * as assert from 'assert'; import * as vscode from 'vscode'; -import { createRandomFile } from './utils'; - -export function timeoutAsync(n: number): Promise { - return new Promise(resolve => { - setTimeout(() => { - resolve(); - }, n); - }); -} - -export function once(event: vscode.Event): vscode.Event { - return (listener: any, thisArgs = null, disposables?: any) => { - // we need this, in case the event fires during the listener call - let didFire = false; - let result: vscode.Disposable; - result = event(e => { - if (didFire) { - return; - } else if (result) { - result.dispose(); - } else { - didFire = true; - } - - return listener.call(thisArgs, e); - }, null, disposables); - - if (didFire) { - result.dispose(); - } - - return result; - }; -} - -async function getEventOncePromise(event: vscode.Event): Promise { - return new Promise((resolve, _reject) => { - once(event)((result: T) => resolve(result)); - }); -} +import { createRandomFile, asPromise, disposeAll, closeAllEditors, revertAllDirty } from '../utils'; // Since `workbench.action.splitEditor` command does await properly // Notebook editor/document events are not guaranteed to be sent to the ext host when promise resolves // The workaround here is waiting for the first visible notebook editor change event. async function splitEditor() { - const once = getEventOncePromise(vscode.window.onDidChangeVisibleNotebookEditors); + const once = asPromise(vscode.window.onDidChangeVisibleNotebookEditors); await vscode.commands.executeCommand('workbench.action.splitEditor'); await once; } @@ -100,7 +61,7 @@ async function updateNotebookMetadata(uri: vscode.Uri, newMetadata: vscode.Noteb } async function withEvent(event: vscode.Event, callback: (e: Promise) => Promise) { - const e = getEventOncePromise(event); + const e = asPromise(event); await callback(e); } @@ -112,7 +73,142 @@ function assertInitalState() { // assert.strictEqual(vscode.notebook.visibleNotebookEditors.length, 0); } -suite('Notebook API tests', () => { +suite('Notebook API tests', function () { + + const disposables: vscode.Disposable[] = []; + + suiteTeardown(async function () { + await revertAllDirty(); + await closeAllEditors(); + + disposeAll(disposables); + disposables.length = 0; + }); + + suiteSetup(function () { + disposables.push(vscode.notebook.registerNotebookContentProvider('notebookCoreTest', { + openNotebook: async (_resource: vscode.Uri): Promise => { + if (/.*empty\-.*\.vsctestnb$/.test(_resource.path)) { + return { + metadata: new vscode.NotebookDocumentMetadata(), + cells: [] + }; + } + + const dto: vscode.NotebookData = { + metadata: new vscode.NotebookDocumentMetadata().with({ custom: { testMetadata: false } }), + cells: [ + { + source: 'test', + language: 'typescript', + cellKind: vscode.NotebookCellKind.Code, + outputs: [], + metadata: new vscode.NotebookCellMetadata().with({ custom: { testCellMetadata: 123 } }) + } + ] + }; + return dto; + }, + resolveNotebook: async (_document: vscode.NotebookDocument) => { + return; + }, + saveNotebook: async (_document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) => { + return; + }, + saveNotebookAs: async (_targetResource: vscode.Uri, _document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) => { + return; + }, + backupNotebook: async (_document: vscode.NotebookDocument, _context: vscode.NotebookDocumentBackupContext, _cancellation: vscode.CancellationToken) => { + return { + id: '1', + delete: () => { } + }; + } + })); + + + const kernel: vscode.NotebookKernel = { + id: 'mainKernel', + label: 'Notebook Test Kernel', + isPreferred: true, + supportedLanguages: ['typescript', 'javascript'], + executeAllCells: async (_document: vscode.NotebookDocument) => { + const edit = new vscode.WorkspaceEdit(); + + edit.replaceNotebookCellOutput(_document.uri, 0, [new vscode.NotebookCellOutput([ + new vscode.NotebookCellOutputItem('text/plain', ['my output'], undefined) + ])]); + return vscode.workspace.applyEdit(edit); + }, + cancelAllCellsExecution: async (_document: vscode.NotebookDocument) => { }, + executeCell: async (document: vscode.NotebookDocument, cell: vscode.NotebookCell | undefined) => { + if (!cell) { + cell = document.cells[0]; + } + + if (document.uri.path.endsWith('customRenderer.vsctestnb')) { + const edit = new vscode.WorkspaceEdit(); + edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([ + new vscode.NotebookCellOutputItem('text/custom', ['test'], undefined) + ])]); + + return vscode.workspace.applyEdit(edit); + } + + const edit = new vscode.WorkspaceEdit(); + // const previousOutputs = cell.outputs; + edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([ + new vscode.NotebookCellOutputItem('text/plain', ['my output'], undefined) + ])]); + + return vscode.workspace.applyEdit(edit); + }, + cancelCellExecution: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell) => { } + }; + + const kernel2: vscode.NotebookKernel = { + id: 'secondaryKernel', + label: 'Notebook Secondary Test Kernel', + isPreferred: false, + supportedLanguages: ['typescript', 'javascript'], + executeAllCells: async (_document: vscode.NotebookDocument) => { + const edit = new vscode.WorkspaceEdit(); + edit.replaceNotebookCellOutput(_document.uri, 0, [new vscode.NotebookCellOutput([ + new vscode.NotebookCellOutputItem('text/plain', ['my second output'], undefined) + ])]); + + return vscode.workspace.applyEdit(edit); + }, + cancelAllCellsExecution: async (_document: vscode.NotebookDocument) => { }, + executeCell: async (document: vscode.NotebookDocument, cell: vscode.NotebookCell | undefined) => { + if (!cell) { + cell = document.cells[0]; + } + + const edit = new vscode.WorkspaceEdit(); + + if (document.uri.path.endsWith('customRenderer.vsctestnb')) { + edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([ + new vscode.NotebookCellOutputItem('text/custom', ['test 2'], undefined) + ])]); + } else { + edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([ + new vscode.NotebookCellOutputItem('text/plain', ['my second output'], undefined) + ])]); + } + + return vscode.workspace.applyEdit(edit); + }, + cancelCellExecution: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell) => { } + }; + + disposables.push(vscode.notebook.registerNotebookKernelProvider({ filenamePattern: '*.vsctestnb' }, { + provideKernels: async () => { + return [kernel, kernel2]; + } + })); + }); + // test.only('crash', async function () { // for (let i = 0; i < 200; i++) { // let resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb')); @@ -141,60 +237,20 @@ suite('Notebook API tests', () => { test('document open/close event', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); - const firstDocumentOpen = getEventOncePromise(vscode.notebook.onDidOpenNotebookDocument); + const resource = await createRandomFile('', undefined, '.vsctestnb'); + const firstDocumentOpen = asPromise(vscode.notebook.onDidOpenNotebookDocument); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); await firstDocumentOpen; - const firstDocumentClose = getEventOncePromise(vscode.notebook.onDidCloseNotebookDocument); + const firstDocumentClose = asPromise(vscode.notebook.onDidCloseNotebookDocument); await vscode.commands.executeCommand('workbench.action.closeAllEditors'); await firstDocumentClose; }); - test('notebook open/close, all cell-documents are ready', async function () { - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); - - const p = getEventOncePromise(vscode.notebook.onDidOpenNotebookDocument).then(notebook => { - for (let cell of notebook.cells) { - const doc = vscode.workspace.textDocuments.find(doc => doc.uri.toString() === cell.uri.toString()); - assert.ok(doc); - assert.strictEqual(doc === cell.document, true); - assert.strictEqual(doc?.languageId, cell.language); - assert.strictEqual(doc?.isDirty, false); - assert.strictEqual(doc?.isClosed, false); - } - }); - - await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - await p; - await vscode.commands.executeCommand('workbench.action.closeAllEditors'); - }); - - test('notebook open/close, notebook ready when cell-document open event is fired', async function () { - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); - let didHappen = false; - const p = getEventOncePromise(vscode.workspace.onDidOpenTextDocument).then(doc => { - if (doc.uri.scheme !== 'vscode-notebook-cell') { - return; - } - const notebook = vscode.notebook.notebookDocuments.find(notebook => { - const cell = notebook.cells.find(cell => cell.document === doc); - return Boolean(cell); - }); - assert.ok(notebook, `notebook for cell ${doc.uri} NOT found`); - didHappen = true; - }); - - await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - await p; - assert.strictEqual(didHappen, true); - await vscode.commands.executeCommand('workbench.action.closeAllEditors'); - }); - test('shared document in notebook editors', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); let counter = 0; const disposables: vscode.Disposable[] = []; disposables.push(vscode.notebook.onDidOpenNotebookDocument(() => { @@ -217,12 +273,12 @@ suite('Notebook API tests', () => { test('editor open/close event', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); - const firstEditorOpen = getEventOncePromise(vscode.window.onDidChangeVisibleNotebookEditors); + const resource = await createRandomFile('', undefined, '.vsctestnb'); + const firstEditorOpen = asPromise(vscode.window.onDidChangeVisibleNotebookEditors); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); await firstEditorOpen; - const firstEditorClose = getEventOncePromise(vscode.window.onDidChangeVisibleNotebookEditors); + const firstEditorClose = asPromise(vscode.window.onDidChangeVisibleNotebookEditors); await vscode.commands.executeCommand('workbench.action.closeAllEditors'); await firstEditorClose; }); @@ -230,7 +286,7 @@ suite('Notebook API tests', () => { test('editor open/close event 2', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); let count = 0; const disposables: vscode.Disposable[] = []; disposables.push(vscode.window.onDidChangeVisibleNotebookEditors(() => { @@ -250,10 +306,10 @@ suite('Notebook API tests', () => { test('editor editing event 2', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - const cellsChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); + const cellsChangeEvent = asPromise(vscode.notebook.onDidChangeNotebookCells); await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); const cellChangeEventRet = await cellsChangeEvent; assert.strictEqual(cellChangeEventRet.document, vscode.window.activeNotebookEditor?.document); @@ -269,7 +325,7 @@ suite('Notebook API tests', () => { const secondCell = vscode.window.activeNotebookEditor!.document.cells[1]; - const moveCellEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); + const moveCellEvent = asPromise(vscode.notebook.onDidChangeNotebookCells); await vscode.commands.executeCommand('notebook.cell.moveUp'); const moveCellEventRet = await moveCellEvent; assert.deepStrictEqual(moveCellEventRet, { @@ -290,7 +346,7 @@ suite('Notebook API tests', () => { ] }); - const cellOutputChange = getEventOncePromise(vscode.notebook.onDidChangeCellOutputs); + const cellOutputChange = asPromise(vscode.notebook.onDidChangeCellOutputs); await vscode.commands.executeCommand('notebook.cell.execute'); const cellOutputsAddedRet = await cellOutputChange; assert.deepStrictEqual(cellOutputsAddedRet, { @@ -299,7 +355,7 @@ suite('Notebook API tests', () => { }); assert.strictEqual(cellOutputsAddedRet.cells[0].outputs.length, 1); - const cellOutputClear = getEventOncePromise(vscode.notebook.onDidChangeCellOutputs); + const cellOutputClear = asPromise(vscode.notebook.onDidChangeCellOutputs); await vscode.commands.executeCommand('notebook.cell.clearOutputs'); const cellOutputsCleardRet = await cellOutputClear; assert.deepStrictEqual(cellOutputsCleardRet, { @@ -323,7 +379,7 @@ suite('Notebook API tests', () => { test('editor move cell event', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove'); @@ -331,7 +387,7 @@ suite('Notebook API tests', () => { const activeCell = vscode.window.activeNotebookEditor!.selection; assert.strictEqual(vscode.window.activeNotebookEditor!.document.cells.indexOf(activeCell!), 0); - const moveChange = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); + const moveChange = asPromise(vscode.notebook.onDidChangeNotebookCells); await vscode.commands.executeCommand('notebook.cell.moveDown'); const ret = await moveChange; assert.deepStrictEqual(ret, { @@ -365,7 +421,7 @@ suite('Notebook API tests', () => { test('notebook editor active/visible', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); const firstEditor = vscode.window.activeNotebookEditor; assert.strictEqual(firstEditor && vscode.window.visibleNotebookEditors.indexOf(firstEditor) >= 0, true); @@ -377,7 +433,7 @@ suite('Notebook API tests', () => { assert.strictEqual(firstEditor && vscode.window.visibleNotebookEditors.indexOf(firstEditor) >= 0, true); assert.strictEqual(vscode.window.visibleNotebookEditors.length, 2); - const untitledEditorChange = getEventOncePromise(vscode.window.onDidChangeActiveNotebookEditor); + const untitledEditorChange = asPromise(vscode.window.onDidChangeActiveNotebookEditor); await vscode.commands.executeCommand('workbench.action.files.newUntitledFile'); await untitledEditorChange; assert.strictEqual(firstEditor && vscode.window.visibleNotebookEditors.indexOf(firstEditor) >= 0, true); @@ -386,7 +442,7 @@ suite('Notebook API tests', () => { assert.notStrictEqual(secondEditor, vscode.window.activeNotebookEditor); assert.strictEqual(vscode.window.visibleNotebookEditors.length, 1); - const activeEditorClose = getEventOncePromise(vscode.window.onDidChangeActiveNotebookEditor); + const activeEditorClose = asPromise(vscode.window.onDidChangeActiveNotebookEditor); await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); await activeEditorClose; assert.strictEqual(secondEditor, vscode.window.activeNotebookEditor); @@ -399,24 +455,48 @@ suite('Notebook API tests', () => { test('notebook active editor change', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); - const firstEditorOpen = getEventOncePromise(vscode.window.onDidChangeActiveNotebookEditor); + const resource = await createRandomFile('', undefined, '.vsctestnb'); + const firstEditorOpen = asPromise(vscode.window.onDidChangeActiveNotebookEditor); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); await firstEditorOpen; - const firstEditorDeactivate = getEventOncePromise(vscode.window.onDidChangeActiveNotebookEditor); + const firstEditorDeactivate = asPromise(vscode.window.onDidChangeActiveNotebookEditor); await vscode.commands.executeCommand('workbench.action.splitEditor'); await firstEditorDeactivate; await saveFileAndCloseAll(resource); }); - test('edit API (replaceCells)', async function () { + test('change cell language', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - const cellsChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); + assert.strictEqual(vscode.window.activeNotebookEditor?.document.cells[0].language, 'typescript'); + assert.strictEqual(vscode.window.activeNotebookEditor?.document.cells[0].cellKind, vscode.NotebookCellKind.Code); + await withEvent(vscode.notebook.onDidChangeCellLanguage, async event => { + await vscode.commands.executeCommand('notebook.cell.changeLanguage', { start: 0, end: 1 }, 'javascript'); + await event; + assert.strictEqual(vscode.window.activeNotebookEditor?.document.cells[0].language, 'javascript'); + }); + + // switch to markdown will change the cell kind + await withEvent(vscode.notebook.onDidChangeNotebookCells, async event => { + await vscode.commands.executeCommand('notebook.cell.changeLanguage', { start: 0, end: 1 }, 'markdown'); + await event; + assert.strictEqual(vscode.window.activeNotebookEditor?.document.cells[0].language, 'markdown'); + assert.strictEqual(vscode.window.activeNotebookEditor?.document.cells[0].cellKind, vscode.NotebookCellKind.Markdown); + }); + + await saveAllFilesAndCloseAll(resource); + }); + + test('edit API (replaceCells)', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + const cellsChangeEvent = asPromise(vscode.notebook.onDidChangeNotebookCells); await vscode.window.activeNotebookEditor!.edit(editBuilder => { editBuilder.replaceCells(1, 0, [{ cellKind: vscode.NotebookCellKind.Code, language: 'javascript', source: 'test 2', outputs: [], metadata: undefined }]); }); @@ -434,7 +514,7 @@ suite('Notebook API tests', () => { test('edit API (replaceOutput, USE NotebookCellOutput-type)', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); await vscode.window.activeNotebookEditor!.edit(editBuilder => { @@ -463,7 +543,7 @@ suite('Notebook API tests', () => { test('edit API (replaceOutput)', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); await vscode.window.activeNotebookEditor!.edit(editBuilder => { @@ -485,10 +565,10 @@ suite('Notebook API tests', () => { test('edit API (replaceOutput, event)', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - const outputChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeCellOutputs); + const outputChangeEvent = asPromise(vscode.notebook.onDidChangeCellOutputs); await vscode.window.activeNotebookEditor!.edit(editBuilder => { editBuilder.replaceCellOutput(0, [new vscode.NotebookCellOutput([ new vscode.NotebookCellOutputItem('foo', 'bar') @@ -511,11 +591,11 @@ suite('Notebook API tests', () => { test('edit API (replaceMetadata)', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); await vscode.window.activeNotebookEditor!.edit(editBuilder => { - editBuilder.replaceCellMetadata(0, { inputCollapsed: true, executionOrder: 17 }); + editBuilder.replaceCellMetadata(0, new vscode.NotebookCellMetadata().with({ inputCollapsed: true, executionOrder: 17 })); }); const document = vscode.window.activeNotebookEditor?.document!; @@ -530,13 +610,13 @@ suite('Notebook API tests', () => { test('edit API (replaceMetadata, event)', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - const event = getEventOncePromise(vscode.notebook.onDidChangeCellMetadata); + const event = asPromise(vscode.notebook.onDidChangeCellMetadata); await vscode.window.activeNotebookEditor!.edit(editBuilder => { - editBuilder.replaceCellMetadata(0, { inputCollapsed: true, executionOrder: 17 }); + editBuilder.replaceCellMetadata(0, new vscode.NotebookCellMetadata().with({ inputCollapsed: true, executionOrder: 17 })); }); const data = await event; @@ -548,145 +628,17 @@ suite('Notebook API tests', () => { await saveFileAndCloseAll(resource); }); - test('workspace edit API (replaceCells)', async function () { - - assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); - await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - - const { document } = vscode.window.activeNotebookEditor!; - assert.strictEqual(document.cells.length, 1); - - // inserting two new cells - { - const edit = new vscode.WorkspaceEdit(); - edit.replaceNotebookCells(document.uri, 0, 0, [{ - cellKind: vscode.NotebookCellKind.Markdown, - language: 'markdown', - metadata: undefined, - outputs: [], - source: 'new_markdown' - }, { - cellKind: vscode.NotebookCellKind.Code, - language: 'fooLang', - metadata: undefined, - outputs: [], - source: 'new_code' - }]); - - const success = await vscode.workspace.applyEdit(edit); - assert.strictEqual(success, true); - } - - assert.strictEqual(document.cells.length, 3); - assert.strictEqual(document.cells[0].document.getText(), 'new_markdown'); - assert.strictEqual(document.cells[1].document.getText(), 'new_code'); - - // deleting cell 1 and 3 - { - const edit = new vscode.WorkspaceEdit(); - edit.replaceNotebookCells(document.uri, 0, 1, []); - edit.replaceNotebookCells(document.uri, 2, 3, []); - const success = await vscode.workspace.applyEdit(edit); - assert.strictEqual(success, true); - } - - assert.strictEqual(document.cells.length, 1); - assert.strictEqual(document.cells[0].document.getText(), 'new_code'); - - // replacing all cells - { - const edit = new vscode.WorkspaceEdit(); - edit.replaceNotebookCells(document.uri, 0, 1, [{ - cellKind: vscode.NotebookCellKind.Markdown, - language: 'markdown', - metadata: undefined, - outputs: [], - source: 'new2_markdown' - }, { - cellKind: vscode.NotebookCellKind.Code, - language: 'fooLang', - metadata: undefined, - outputs: [], - source: 'new2_code' - }]); - const success = await vscode.workspace.applyEdit(edit); - assert.strictEqual(success, true); - } - assert.strictEqual(document.cells.length, 2); - assert.strictEqual(document.cells[0].document.getText(), 'new2_markdown'); - assert.strictEqual(document.cells[1].document.getText(), 'new2_code'); - - // remove all cells - { - const edit = new vscode.WorkspaceEdit(); - edit.replaceNotebookCells(document.uri, 0, document.cells.length, []); - const success = await vscode.workspace.applyEdit(edit); - assert.strictEqual(success, true); - } - assert.strictEqual(document.cells.length, 0); - - await saveFileAndCloseAll(resource); - }); - - test('workspace edit API (replaceCells, event)', async function () { - - assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); - await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - - const { document } = vscode.window.activeNotebookEditor!; - assert.strictEqual(document.cells.length, 1); - - const edit = new vscode.WorkspaceEdit(); - edit.replaceNotebookCells(document.uri, 0, 0, [{ - cellKind: vscode.NotebookCellKind.Markdown, - language: 'markdown', - metadata: undefined, - outputs: [], - source: 'new_markdown' - }, { - cellKind: vscode.NotebookCellKind.Code, - language: 'fooLang', - metadata: undefined, - outputs: [], - source: 'new_code' - }]); - - const event = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); - - const success = await vscode.workspace.applyEdit(edit); - assert.strictEqual(success, true); - - const data = await event; - - // check document - assert.strictEqual(document.cells.length, 3); - assert.strictEqual(document.cells[0].document.getText(), 'new_markdown'); - assert.strictEqual(document.cells[1].document.getText(), 'new_code'); - - // check event data - assert.strictEqual(data.document === document, true); - assert.strictEqual(data.changes.length, 1); - assert.strictEqual(data.changes[0].deletedCount, 0); - assert.strictEqual(data.changes[0].deletedItems.length, 0); - assert.strictEqual(data.changes[0].items.length, 2); - assert.strictEqual(data.changes[0].items[0], document.cells[0]); - assert.strictEqual(data.changes[0].items[1], document.cells[1]); - await saveFileAndCloseAll(resource); - }); - test('edit API batch edits', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - const cellsChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); - const cellMetadataChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeCellMetadata); + const cellsChangeEvent = asPromise(vscode.notebook.onDidChangeNotebookCells); + const cellMetadataChangeEvent = asPromise(vscode.notebook.onDidChangeCellMetadata); const version = vscode.window.activeNotebookEditor!.document.version; await vscode.window.activeNotebookEditor!.edit(editBuilder => { editBuilder.replaceCells(1, 0, [{ cellKind: vscode.NotebookCellKind.Code, language: 'javascript', source: 'test 2', outputs: [], metadata: undefined }]); - editBuilder.replaceCellMetadata(0, { runnable: false }); + editBuilder.replaceCellMetadata(0, new vscode.NotebookCellMetadata().with({ runnable: false })); }); await cellsChangeEvent; @@ -697,15 +649,15 @@ suite('Notebook API tests', () => { test('edit API batch edits undo/redo', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - const cellsChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); - const cellMetadataChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeCellMetadata); + const cellsChangeEvent = asPromise(vscode.notebook.onDidChangeNotebookCells); + const cellMetadataChangeEvent = asPromise(vscode.notebook.onDidChangeCellMetadata); const version = vscode.window.activeNotebookEditor!.document.version; await vscode.window.activeNotebookEditor!.edit(editBuilder => { editBuilder.replaceCells(1, 0, [{ cellKind: vscode.NotebookCellKind.Code, language: 'javascript', source: 'test 2', outputs: [], metadata: undefined }]); - editBuilder.replaceCellMetadata(0, { runnable: false }); + editBuilder.replaceCellMetadata(0, new vscode.NotebookCellMetadata().with({ runnable: false })); }); await cellsChangeEvent; @@ -724,7 +676,7 @@ suite('Notebook API tests', () => { test('initialzation should not emit cell change events.', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); let count = 0; const disposables: vscode.Disposable[] = []; @@ -739,12 +691,13 @@ suite('Notebook API tests', () => { await saveFileAndCloseAll(resource); }); -}); + // }); + + // suite('notebook workflow', () => { -suite('notebook workflow', () => { test('notebook open', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), 'test'); @@ -766,7 +719,7 @@ suite('notebook workflow', () => { test('notebook cell actions', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), 'test'); @@ -840,7 +793,7 @@ suite('notebook workflow', () => { test('notebook join cells', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), 'test'); @@ -852,7 +805,7 @@ suite('notebook workflow', () => { edit.insert(vscode.window.activeNotebookEditor!.selection!.uri, new vscode.Position(0, 0), 'var abc = 0;'); await vscode.workspace.applyEdit(edit); - const cellsChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); + const cellsChangeEvent = asPromise(vscode.notebook.onDidChangeNotebookCells); await vscode.commands.executeCommand('notebook.cell.joinAbove'); await cellsChangeEvent; @@ -864,7 +817,7 @@ suite('notebook workflow', () => { test('move cells will not recreate cells in ExtHost', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove'); @@ -884,7 +837,7 @@ suite('notebook workflow', () => { }); // test.only('document metadata is respected', async function () { - // const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + // const resource = await createRandomFile('', undefined, '.vsctestnb'); // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); // assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); @@ -909,7 +862,7 @@ suite('notebook workflow', () => { test('cell runnable metadata is respected', async () => { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); const editor = vscode.window.activeNotebookEditor!; @@ -918,15 +871,15 @@ suite('notebook workflow', () => { const cell = editor.document.cells[0]; assert.strictEqual(cell.outputs.length, 0); - let metadataChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeCellMetadata); - await updateCellMetadata(resource, cell, { ...cell.metadata, runnable: false }); + let metadataChangeEvent = asPromise(vscode.notebook.onDidChangeCellMetadata); + await updateCellMetadata(resource, cell, cell.metadata.with({ runnable: false })); await metadataChangeEvent; await vscode.commands.executeCommand('notebook.cell.execute'); assert.strictEqual(cell.outputs.length, 0, 'should not execute'); // not runnable, didn't work - metadataChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeCellMetadata); - await updateCellMetadata(resource, cell, { ...cell.metadata, runnable: true }); + metadataChangeEvent = asPromise(vscode.notebook.onDidChangeCellMetadata); + await updateCellMetadata(resource, cell, cell.metadata.with({ runnable: true })); await metadataChangeEvent; await vscode.commands.executeCommand('notebook.cell.execute'); @@ -938,7 +891,7 @@ suite('notebook workflow', () => { test('document runnable metadata is respected', async () => { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); const editor = vscode.window.activeNotebookEditor!; @@ -947,7 +900,7 @@ suite('notebook workflow', () => { assert.strictEqual(cell.outputs.length, 0); await withEvent(vscode.notebook.onDidChangeNotebookDocumentMetadata, async event => { - updateNotebookMetadata(editor.document.uri, { ...editor.document.metadata, runnable: false }); + updateNotebookMetadata(editor.document.uri, editor.document.metadata.with({ runnable: false })); await event; }); @@ -955,7 +908,7 @@ suite('notebook workflow', () => { assert.strictEqual(cell.outputs.length, 0, 'should not execute'); // not runnable, didn't work await withEvent(vscode.notebook.onDidChangeNotebookDocumentMetadata, async event => { - updateNotebookMetadata(editor.document.uri, { ...editor.document.metadata, runnable: true }); + updateNotebookMetadata(editor.document.uri, editor.document.metadata.with({ runnable: true })); await event; }); @@ -973,7 +926,7 @@ suite('notebook workflow', () => { // TODO@rebornix this is wrong, `await vscode.commands.executeCommand('notebook.execute');` doesn't wait until the workspace edit is applied test.skip('cell execute command takes arguments', async () => { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); const editor = vscode.window.activeNotebookEditor!; @@ -988,14 +941,14 @@ suite('notebook workflow', () => { test('cell execute command takes arguments 2', async () => { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); const editor = vscode.window.activeNotebookEditor!; const cell = editor.document.cells[0]; await withEvent(vscode.notebook.onDidChangeNotebookDocumentMetadata, async event => { - updateNotebookMetadata(editor.document.uri, { ...editor.document.metadata, runnable: true }); + updateNotebookMetadata(editor.document.uri, editor.document.metadata.with({ runnable: true })); await event; }); @@ -1011,7 +964,7 @@ suite('notebook workflow', () => { assert.strictEqual(cell.outputs.length, 0, 'should clear'); }); - const secondResource = await createRandomFile('', undefined, 'second', '.vsctestnb'); + const secondResource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest'); await withEvent(vscode.notebook.onDidChangeCellOutputs, async (event) => { @@ -1029,14 +982,14 @@ suite('notebook workflow', () => { test('document execute command takes arguments', async () => { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); const editor = vscode.window.activeNotebookEditor!; const cell = editor.document.cells[0]; - const metadataChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookDocumentMetadata); - updateNotebookMetadata(editor.document.uri, { ...editor.document.metadata, runnable: true }); + const metadataChangeEvent = asPromise(vscode.notebook.onDidChangeNotebookDocumentMetadata); + updateNotebookMetadata(editor.document.uri, editor.document.metadata.with({ runnable: true })); await metadataChangeEvent; assert.strictEqual(editor.document.metadata.runnable, true); @@ -1046,12 +999,12 @@ suite('notebook workflow', () => { assert.strictEqual(cell.outputs.length, 1, 'should execute'); // runnable, it worked }); - const clearChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeCellOutputs); + const clearChangeEvent = asPromise(vscode.notebook.onDidChangeCellOutputs); await vscode.commands.executeCommand('notebook.cell.clearOutputs'); await clearChangeEvent; assert.strictEqual(cell.outputs.length, 0, 'should clear'); - const secondResource = await createRandomFile('', undefined, 'second', '.vsctestnb'); + const secondResource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest'); await withEvent(vscode.notebook.onDidChangeCellOutputs, async (event) => { @@ -1069,14 +1022,14 @@ suite('notebook workflow', () => { test('cell execute and select kernel', async () => { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); const editor = vscode.window.activeNotebookEditor!; const cell = editor.document.cells[0]; - const metadataChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookDocumentMetadata); - updateNotebookMetadata(editor.document.uri, { ...editor.document.metadata, runnable: true }); + const metadataChangeEvent = asPromise(vscode.notebook.onDidChangeNotebookDocumentMetadata); + updateNotebookMetadata(editor.document.uri, editor.document.metadata.with({ runnable: true })); await metadataChangeEvent; await vscode.commands.executeCommand('notebook.cell.execute'); @@ -1087,7 +1040,7 @@ suite('notebook workflow', () => { 'my output' ]); - await vscode.commands.executeCommand('notebook.selectKernel', { extension: 'vscode.vscode-notebook-tests', id: 'secondaryKernel' }); + await vscode.commands.executeCommand('notebook.selectKernel', { extension: 'vscode.vscode-api-tests', id: 'secondaryKernel' }); await vscode.commands.executeCommand('notebook.cell.execute'); assert.strictEqual(cell.outputs.length, 1, 'should execute'); // runnable, it worked assert.strictEqual(cell.outputs[0].outputs.length, 1); @@ -1099,12 +1052,12 @@ suite('notebook workflow', () => { await vscode.commands.executeCommand('workbench.action.files.save'); await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); }); -}); + // }); -suite('notebook dirty state', () => { + // suite('notebook dirty state', () => { test('notebook open', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), 'test'); @@ -1133,12 +1086,12 @@ suite('notebook dirty state', () => { await saveFileAndCloseAll(resource); }); -}); + // }); -suite('notebook undo redo', () => { + // suite('notebook undo redo', () => { test('notebook open', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), 'test'); @@ -1181,7 +1134,7 @@ suite('notebook undo redo', () => { // test.skip('execute and then undo redo', async function () { // assertInitalState(); - // const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + // const resource = await createRandomFile('', undefined, '.vsctestnb'); // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); // const cellsChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); @@ -1242,11 +1195,11 @@ suite('notebook undo redo', () => { // await saveFileAndCloseAll(resource); // }); -}); + // }); -suite('notebook working copy', () => { + // suite('notebook working copy', () => { // test('notebook revert on close', async function () { - // const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + // const resource = await createRandomFile('', undefined, '.vsctestnb'); // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); // await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); // assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), ''); @@ -1267,7 +1220,7 @@ suite('notebook working copy', () => { // }); // test('notebook revert', async function () { - // const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + // const resource = await createRandomFile('', undefined, '.vsctestnb'); // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); // await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); // assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), ''); @@ -1288,7 +1241,7 @@ suite('notebook working copy', () => { test('multiple tabs: dirty + clean', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), ''); @@ -1298,7 +1251,7 @@ suite('notebook working copy', () => { edit.insert(vscode.window.activeNotebookEditor!.selection!.uri, new vscode.Position(0, 0), 'var abc = 0;'); await vscode.workspace.applyEdit(edit); - const secondResource = await createRandomFile('', undefined, 'second', '.vsctestnb'); + const secondResource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest'); await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); @@ -1314,7 +1267,7 @@ suite('notebook working copy', () => { test('multiple tabs: two dirty tabs and switching', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), ''); @@ -1324,7 +1277,7 @@ suite('notebook working copy', () => { edit.insert(vscode.window.activeNotebookEditor!.selection!.uri, new vscode.Position(0, 0), 'var abc = 0;'); await vscode.workspace.applyEdit(edit); - const secondResource = await createRandomFile('', undefined, 'second', '.vsctestnb'); + const secondResource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest'); await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), ''); @@ -1353,7 +1306,7 @@ suite('notebook working copy', () => { test('multiple tabs: different editors with same document', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); const firstNotebookEditor = vscode.window.activeNotebookEditor; assert.strictEqual(firstNotebookEditor !== undefined, true, 'notebook first'); @@ -1375,12 +1328,12 @@ suite('notebook working copy', () => { // await vscode.commands.executeCommand('workbench.action.files.saveAll'); // await vscode.commands.executeCommand('workbench.action.closeAllEditors'); }); -}); + // }); -suite('metadata', () => { + // suite('metadata', () => { test('custom metadata should be supported', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); assert.strictEqual(vscode.window.activeNotebookEditor!.document.metadata.custom!['testMetadata'] as boolean, false); @@ -1394,7 +1347,7 @@ suite('metadata', () => { // TODO@rebornix skip as it crashes the process all the time test.skip('custom metadata should be supported 2', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); assert.strictEqual(vscode.window.activeNotebookEditor!.document.metadata.custom!['testMetadata'] as boolean, false); @@ -1409,9 +1362,9 @@ suite('metadata', () => { await saveFileAndCloseAll(resource); }); -}); + // }); -suite('regression', () => { + // suite('regression', () => { // test('microsoft/vscode-github-issue-notebooks#26. Insert template cell in the new empty document', async function () { // assertInitalState(); // await vscode.commands.executeCommand('workbench.action.files.newUntitledFile', { "viewType": "notebookCoreTest" }); @@ -1423,7 +1376,7 @@ suite('regression', () => { test('#106657. Opening a notebook from markers view is broken ', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); const document = vscode.window.activeNotebookEditor?.document!; @@ -1441,7 +1394,7 @@ suite('regression', () => { test.skip('Cannot open notebook from cell-uri with vscode.open-command', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); const document = vscode.window.activeNotebookEditor?.document!; @@ -1459,7 +1412,7 @@ suite('regression', () => { test('#97830, #97764. Support switch to other editor types', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'empty', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); const edit = new vscode.WorkspaceEdit(); @@ -1469,12 +1422,9 @@ suite('regression', () => { assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), 'var abc = 0;'); - // todo@jrieken enforce a kernel (how) and test that its language is picked - // assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.language, 'typescript'); - // no kernel -> no default language - assert.strictEqual(vscode.window.activeNotebookEditor!.kernel, undefined); - assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.language, 'plaintext'); + // assert.strictEqual(vscode.window.activeNotebookEditor!.kernel, undefined); + assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.language, 'typescript'); await vscode.commands.executeCommand('vscode.openWith', resource, 'default'); assert.strictEqual(vscode.window.activeTextEditor?.document.uri.path, resource.path); @@ -1485,7 +1435,7 @@ suite('regression', () => { // open text editor, pin, and then open a notebook test('#96105 - dirty editors', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'empty', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'default'); const edit = new vscode.WorkspaceEdit(); edit.insert(resource, new vscode.Position(0, 0), 'var abc = 0;'); @@ -1509,7 +1459,7 @@ suite('regression', () => { test('#102423 - copy/paste shares the same text buffer', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); let activeCell = vscode.window.activeNotebookEditor!.selection; @@ -1530,16 +1480,82 @@ suite('regression', () => { await vscode.commands.executeCommand('workbench.action.closeAllEditors'); }); -}); -suite('webview', () => { + test('#116598, output items change event.', async function () { + assertInitalState(); + + const resource = await createRandomFile('', undefined, '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + const edit = new vscode.WorkspaceEdit(); + edit.appendNotebookCellOutput(resource, 0, [new vscode.NotebookCellOutput([ + new vscode.NotebookCellOutputItem('application/foo', 'bar'), + new vscode.NotebookCellOutputItem('application/json', { data: true }, { metadata: true }), + ])]); + await vscode.workspace.applyEdit(edit); + assert.strictEqual(vscode.window.activeNotebookEditor!.document.cells[0].outputs.length, 1); + assert.strictEqual(vscode.window.activeNotebookEditor!.document.cells[0].outputs[0].outputs.length, 2); + + const appendEdit = new vscode.WorkspaceEdit(); + const newItem = new vscode.NotebookCellOutputItem('text/plain', '1'); + appendEdit.appendNotebookCellOutputItems( + resource, + 0, + vscode.window.activeNotebookEditor!.document.cells[0].outputs[0].id, + [newItem] + ); + await vscode.workspace.applyEdit(appendEdit); + assert.strictEqual(vscode.window.activeNotebookEditor!.document.cells[0].outputs[0].outputs.length, 3); + assert.deepStrictEqual(vscode.window.activeNotebookEditor!.document.cells[0].outputs[0].outputs[2], newItem); + }); + + test('#115855 onDidSaveNotebookDocument', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + const cellsChangeEvent = asPromise(vscode.notebook.onDidChangeNotebookCells); + await vscode.window.activeNotebookEditor!.edit(editBuilder => { + editBuilder.replaceCells(1, 0, [{ cellKind: vscode.NotebookCellKind.Code, language: 'javascript', source: 'test 2', outputs: [], metadata: undefined }]); + }); + + const cellChangeEventRet = await cellsChangeEvent; + assert.strictEqual(cellChangeEventRet.document === vscode.window.activeNotebookEditor?.document, true); + assert.strictEqual(cellChangeEventRet.document.isDirty, true); + + await withEvent(vscode.notebook.onDidSaveNotebookDocument, async event => { + await vscode.commands.executeCommand('workbench.action.files.saveAll'); + await event; + assert.strictEqual(cellChangeEventRet.document.isDirty, false); + }); + await saveAllFilesAndCloseAll(resource); + }); + + + test('#116808, active kernel should not be undefined', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + await withEvent(vscode.notebook.onDidChangeActiveNotebookKernel, async event => { + await event; + assert.notStrictEqual(vscode.window.activeNotebookEditor?.kernel, undefined); + assert.strictEqual(vscode.window.activeNotebookEditor?.kernel?.id, 'mainKernel'); + }); + + await saveAllFilesAndCloseAll(resource); + }); + + // }); + + // suite('webview', () => { // for web, `asWebUri` gets `https`? // test('asWebviewUri', async function () { // if (vscode.env.uiKind === vscode.UIKind.Web) { // return; // } - // const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + // const resource = await createRandomFile('', undefined, '.vsctestnb'); // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); // assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); // const uri = vscode.window.activeNotebookEditor!.asWebviewUri(vscode.Uri.file('./hello.png')); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts index 65fd89d3b96..615bb79ac6b 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts @@ -150,6 +150,13 @@ suite('vscode API - window', () => { }); test('active editor not always correct... #49125', async function () { + + if (!window.state.focused) { + // no focus! + this.skip(); + return; + } + if (process.env['BUILD_SOURCEVERSION']) { this.skip(); return; diff --git a/extensions/vscode-api-tests/src/utils.ts b/extensions/vscode-api-tests/src/utils.ts index f7923bbc9f7..8d94c8ea120 100644 --- a/extensions/vscode-api-tests/src/utils.ts +++ b/extensions/vscode-api-tests/src/utils.ts @@ -17,7 +17,7 @@ vscode.workspace.registerFileSystemProvider(testFs.scheme, testFs, { isCaseSensi export async function createRandomFile(contents = '', dir: vscode.Uri | undefined = undefined, ext = ''): Promise { let fakeFile: vscode.Uri; if (dir) { - assert.equal(dir.scheme, testFs.scheme); + assert.strictEqual(dir.scheme, testFs.scheme); fakeFile = dir.with({ path: dir.path + '/' + rndName() + ext }); } else { fakeFile = vscode.Uri.parse(`${testFs.scheme}:/${rndName() + ext}`); @@ -48,6 +48,10 @@ export function closeAllEditors(): Thenable { return vscode.commands.executeCommand('workbench.action.closeAllEditors'); } +export function saveAllEditors(): Thenable { + return vscode.commands.executeCommand('workbench.action.files.saveAll'); +} + export async function revertAllDirty(): Promise { return vscode.commands.executeCommand('_workbench.revertAllDirty'); } @@ -117,3 +121,19 @@ export function assertNoRpcFromEntry(entry: [obj: any, name: string]) { assert.strictEqual(rpcPaths.length, 0, rpcPaths.join('\n')); assert.strictEqual(proxyPaths.length, 0, proxyPaths.join('\n')); // happens... } + +export async function asPromise(event: vscode.Event, timeout = 5000): Promise { + return new Promise((resolve, reject) => { + + const handle = setTimeout(() => { + sub.dispose(); + reject(new Error('asPromise TIMEOUT reached')); + }, timeout); + + const sub = event(e => { + clearTimeout(handle); + sub.dispose(); + resolve(e); + }); + }); +} diff --git a/extensions/vscode-notebook-tests/package.json b/extensions/vscode-notebook-tests/package.json index 0451ebf9283..78164843df9 100644 --- a/extensions/vscode-notebook-tests/package.json +++ b/extensions/vscode-notebook-tests/package.json @@ -8,7 +8,7 @@ "activationEvents": [ "*" ], - "main": "./out/notebookTestMain", + "main": "./out/extension", "enableProposedApi": true, "engines": { "vscode": "^1.25.0" @@ -34,16 +34,6 @@ } ], "notebookProvider": [ - { - "viewType": "notebookCoreTest", - "displayName": "Notebook Core Test", - "selector": [ - { - "filenamePattern": "*.vsctestnb", - "excludeFileNamePattern": "" - } - ] - }, { "viewType": "notebookSmokeTest", "displayName": "Notebook Smoke Test", diff --git a/extensions/vscode-notebook-tests/src/notebookSmokeTestMain.ts b/extensions/vscode-notebook-tests/src/extension.ts similarity index 92% rename from extensions/vscode-notebook-tests/src/notebookSmokeTestMain.ts rename to extensions/vscode-notebook-tests/src/extension.ts index 0e3e7426f23..55231411594 100644 --- a/extensions/vscode-notebook-tests/src/notebookSmokeTestMain.ts +++ b/extensions/vscode-notebook-tests/src/extension.ts @@ -11,7 +11,7 @@ function wait(ms: number): Promise { return new Promise(r => setTimeout(r, ms)); } -export function smokeTestActivate(context: vscode.ExtensionContext): any { +export function activate(context: vscode.ExtensionContext): any { context.subscriptions.push(vscode.commands.registerCommand('vscode-notebook-tests.createNewNotebook', async () => { const workspacePath = vscode.workspace.workspaceFolders![0].uri.fsPath; const notebookPath = path.join(workspacePath, 'test.smoke-nb'); @@ -23,25 +23,21 @@ export function smokeTestActivate(context: vscode.ExtensionContext): any { context.subscriptions.push(vscode.notebook.registerNotebookContentProvider('notebookSmokeTest', { openNotebook: async (_resource: vscode.Uri) => { const dto: vscode.NotebookData = { - metadata: {}, + metadata: new vscode.NotebookDocumentMetadata(), cells: [ { source: 'code()', language: 'typescript', cellKind: vscode.NotebookCellKind.Code, outputs: [], - metadata: { - custom: { testCellMetadata: 123 } - } + metadata: new vscode.NotebookCellMetadata().with({ custom: { testCellMetadata: 123 } }) }, { source: 'Markdown Cell', language: 'markdown', cellKind: vscode.NotebookCellKind.Markdown, outputs: [], - metadata: { - custom: { testCellMetadata: 123 } - } + metadata: new vscode.NotebookCellMetadata().with({ custom: { testCellMetadata: 123 } }) } ] }; diff --git a/extensions/vscode-notebook-tests/src/notebookTestMain.ts b/extensions/vscode-notebook-tests/src/notebookTestMain.ts deleted file mode 100644 index c856d19e46c..00000000000 --- a/extensions/vscode-notebook-tests/src/notebookTestMain.ts +++ /dev/null @@ -1,136 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import { smokeTestActivate } from './notebookSmokeTestMain'; - -export function activate(context: vscode.ExtensionContext): any { - smokeTestActivate(context); - - context.subscriptions.push(vscode.notebook.registerNotebookContentProvider('notebookCoreTest', { - openNotebook: async (_resource: vscode.Uri): Promise => { - if (/.*empty\-.*\.vsctestnb$/.test(_resource.path)) { - return { - metadata: {}, - cells: [] - }; - } - - const dto: vscode.NotebookData = { - metadata: { - custom: { testMetadata: false } - }, - cells: [ - { - source: 'test', - language: 'typescript', - cellKind: vscode.NotebookCellKind.Code, - outputs: [], - metadata: { - custom: { testCellMetadata: 123 } - } - } - ] - }; - return dto; - }, - resolveNotebook: async (_document: vscode.NotebookDocument) => { - return; - }, - saveNotebook: async (_document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) => { - return; - }, - saveNotebookAs: async (_targetResource: vscode.Uri, _document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) => { - return; - }, - backupNotebook: async (_document: vscode.NotebookDocument, _context: vscode.NotebookDocumentBackupContext, _cancellation: vscode.CancellationToken) => { - return { - id: '1', - delete: () => { } - }; - } - })); - - const kernel: vscode.NotebookKernel = { - id: 'mainKernel', - label: 'Notebook Test Kernel', - isPreferred: true, - supportedLanguages: ['typescript'], - executeAllCells: async (_document: vscode.NotebookDocument) => { - const edit = new vscode.WorkspaceEdit(); - - edit.replaceNotebookCellOutput(_document.uri, 0, [new vscode.NotebookCellOutput([ - new vscode.NotebookCellOutputItem('text/plain', ['my output'], undefined) - ])]); - return vscode.workspace.applyEdit(edit); - }, - cancelAllCellsExecution: async (_document: vscode.NotebookDocument) => { }, - executeCell: async (document: vscode.NotebookDocument, cell: vscode.NotebookCell | undefined) => { - if (!cell) { - cell = document.cells[0]; - } - - if (document.uri.path.endsWith('customRenderer.vsctestnb')) { - const edit = new vscode.WorkspaceEdit(); - edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([ - new vscode.NotebookCellOutputItem('text/custom', ['test'], undefined) - ])]); - - return vscode.workspace.applyEdit(edit); - } - - const edit = new vscode.WorkspaceEdit(); - // const previousOutputs = cell.outputs; - edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([ - new vscode.NotebookCellOutputItem('text/plain', ['my output'], undefined) - ])]); - - return vscode.workspace.applyEdit(edit); - }, - cancelCellExecution: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell) => { } - }; - - const kernel2: vscode.NotebookKernel = { - id: 'secondaryKernel', - label: 'Notebook Secondary Test Kernel', - isPreferred: false, - supportedLanguages: ['typescript'], - executeAllCells: async (_document: vscode.NotebookDocument) => { - const edit = new vscode.WorkspaceEdit(); - edit.replaceNotebookCellOutput(_document.uri, 0, [new vscode.NotebookCellOutput([ - new vscode.NotebookCellOutputItem('text/plain', ['my second output'], undefined) - ])]); - - return vscode.workspace.applyEdit(edit); - }, - cancelAllCellsExecution: async (_document: vscode.NotebookDocument) => { }, - executeCell: async (document: vscode.NotebookDocument, cell: vscode.NotebookCell | undefined) => { - if (!cell) { - cell = document.cells[0]; - } - - const edit = new vscode.WorkspaceEdit(); - - if (document.uri.path.endsWith('customRenderer.vsctestnb')) { - edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([ - new vscode.NotebookCellOutputItem('text/custom', ['test 2'], undefined) - ])]); - } else { - edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([ - new vscode.NotebookCellOutputItem('text/plain', ['my second output'], undefined) - ])]); - } - - return vscode.workspace.applyEdit(edit); - }, - cancelCellExecution: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell) => { } - }; - - context.subscriptions.push(vscode.notebook.registerNotebookKernelProvider({ filenamePattern: '*.vsctestnb' }, { - provideKernels: async () => { - return [kernel, kernel2]; - } - })); -} diff --git a/package.json b/package.json index 5d67673be6b..5438b01e552 100644 --- a/package.json +++ b/package.json @@ -190,7 +190,7 @@ "style-loader": "^1.0.0", "ts-loader": "^6.2.1", "tsec": "0.1.3", - "typescript": "4.2.0-dev.20201207", + "typescript": "^4.3.0-dev.20210216", "typescript-formatter": "7.1.0", "underscore": "^1.8.2", "vinyl": "^2.0.0", diff --git a/product.json b/product.json index 47bb865de2e..e9561e59ee2 100644 --- a/product.json +++ b/product.json @@ -5,7 +5,7 @@ "dataFolderName": ".vscode-oss", "win32MutexName": "vscodeoss", "licenseName": "MIT", - "licenseUrl": "https://github.com/microsoft/vscode/blob/master/LICENSE.txt", + "licenseUrl": "https://github.com/microsoft/vscode/blob/main/LICENSE.txt", "win32DirName": "Microsoft Code OSS", "win32NameVersion": "Microsoft Code OSS", "win32RegValueName": "CodeOSS", diff --git a/scripts/generate-definitelytyped.sh b/scripts/generate-definitelytyped.sh index 118401b43cb..1b139ebbf79 100755 --- a/scripts/generate-definitelytyped.sh +++ b/scripts/generate-definitelytyped.sh @@ -14,7 +14,7 @@ header="// Type definitions for Visual Studio Code ${1} /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. - * See https://github.com/microsoft/vscode/blob/master/LICENSE.txt for license information. + * See https://github.com/microsoft/vscode/blob/main/LICENSE.txt for license information. *--------------------------------------------------------------------------------------------*/ /** diff --git a/scripts/test-integration.bat b/scripts/test-integration.bat index 3013dc04b90..3f8390b0b2b 100644 --- a/scripts/test-integration.bat +++ b/scripts/test-integration.bat @@ -45,31 +45,33 @@ if "%INTEGRATION_TEST_ELECTRON_PATH%"=="" ( :: Tests in the extension host -call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-api-tests\testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% +set ALL_PLATFORMS_API_TESTS_EXTRA_ARGS=--disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-keytar --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% + +call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-api-tests\testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests %ALL_PLATFORMS_API_TESTS_EXTRA_ARGS% if %errorlevel% neq 0 exit /b %errorlevel% -call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-api-tests\testworkspace.code-workspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\workspace-tests --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% +call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-api-tests\testworkspace.code-workspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\workspace-tests %ALL_PLATFORMS_API_TESTS_EXTRA_ARGS% if %errorlevel% neq 0 exit /b %errorlevel% -call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-colorize-tests\test --extensionDevelopmentPath=%~dp0\..\extensions\vscode-colorize-tests --extensionTestsPath=%~dp0\..\extensions\vscode-colorize-tests\out --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% +call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-colorize-tests\test --extensionDevelopmentPath=%~dp0\..\extensions\vscode-colorize-tests --extensionTestsPath=%~dp0\..\extensions\vscode-colorize-tests\out %ALL_PLATFORMS_API_TESTS_EXTRA_ARGS% if %errorlevel% neq 0 exit /b %errorlevel% -call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\typescript-language-features\test-workspace --extensionDevelopmentPath=%~dp0\..\extensions\typescript-language-features --extensionTestsPath=%~dp0\..\extensions\typescript-language-features\out\test\unit --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% +call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\typescript-language-features\test-workspace --extensionDevelopmentPath=%~dp0\..\extensions\typescript-language-features --extensionTestsPath=%~dp0\..\extensions\typescript-language-features\out\test\unit %ALL_PLATFORMS_API_TESTS_EXTRA_ARGS% if %errorlevel% neq 0 exit /b %errorlevel% -call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\markdown-language-features\test-workspace --extensionDevelopmentPath=%~dp0\..\extensions\markdown-language-features --extensionTestsPath=%~dp0\..\extensions\markdown-language-features\out\test --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% +call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\markdown-language-features\test-workspace --extensionDevelopmentPath=%~dp0\..\extensions\markdown-language-features --extensionTestsPath=%~dp0\..\extensions\markdown-language-features\out\test %ALL_PLATFORMS_API_TESTS_EXTRA_ARGS% if %errorlevel% neq 0 exit /b %errorlevel% -call "%INTEGRATION_TEST_ELECTRON_PATH%" $%~dp0\..\extensions\emmet\out\test\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% . +call "%INTEGRATION_TEST_ELECTRON_PATH%" $%~dp0\..\extensions\emmet\out\test\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test %ALL_PLATFORMS_API_TESTS_EXTRA_ARGS% . if %errorlevel% neq 0 exit /b %errorlevel% -call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-notebook-tests\test --enable-proposed-api=vscode.vscode-notebook-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-notebook-tests --extensionTestsPath=%~dp0\..\extensions\vscode-notebook-tests\out --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% +call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-notebook-tests\test --enable-proposed-api=vscode.vscode-notebook-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-notebook-tests --extensionTestsPath=%~dp0\..\extensions\vscode-notebook-tests\out %ALL_PLATFORMS_API_TESTS_EXTRA_ARGS% if %errorlevel% neq 0 exit /b %errorlevel% 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%" %GITWORKSPACE% --extensionDevelopmentPath=%~dp0\..\extensions\git --extensionTestsPath=%~dp0\..\extensions\git\out\test --enable-proposed-api=vscode.git --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% +call "%INTEGRATION_TEST_ELECTRON_PATH%" %GITWORKSPACE% --extensionDevelopmentPath=%~dp0\..\extensions\git --extensionTestsPath=%~dp0\..\extensions\git\out\test --enable-proposed-api=vscode.git %ALL_PLATFORMS_API_TESTS_EXTRA_ARGS% if %errorlevel% neq 0 exit /b %errorlevel% :: Tests in commonJS (CSS, HTML) diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index b1d5e9588e3..0eeefd80bff 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -65,30 +65,31 @@ fi after_suite # Tests in the extension host -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/vscode-api-tests/testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR + +ALL_PLATFORMS_API_TESTS_EXTRA_ARGS="--disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-keytar --disable-extensions --user-data-dir=$VSCODEUSERDATADIR" + +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/vscode-api-tests/testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests $ALL_PLATFORMS_API_TESTS_EXTRA_ARGS after_suite -# TODO(deepak1556): Disable workspace test temporarily -# https://github.com/microsoft/vscode/issues/111288 -#"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR -#after_suite - -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests $ALL_PLATFORMS_API_TESTS_EXTRA_ARGS after_suite -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/markdown-language-features/test-workspace --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out $ALL_PLATFORMS_API_TESTS_EXTRA_ARGS after_suite -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/typescript-language-features/test-workspace --extensionDevelopmentPath=$ROOT/extensions/typescript-language-features --extensionTestsPath=$ROOT/extensions/typescript-language-features/out/test/unit --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/markdown-language-features/test-workspace --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test $ALL_PLATFORMS_API_TESTS_EXTRA_ARGS after_suite -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/emmet/out/test/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/typescript-language-features/test-workspace --extensionDevelopmentPath=$ROOT/extensions/typescript-language-features --extensionTestsPath=$ROOT/extensions/typescript-language-features/out/test/unit $ALL_PLATFORMS_API_TESTS_EXTRA_ARGS after_suite -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $(mktemp -d 2>/dev/null) --enable-proposed-api=vscode.git --extensionDevelopmentPath=$ROOT/extensions/git --extensionTestsPath=$ROOT/extensions/git/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/emmet/out/test/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test $ALL_PLATFORMS_API_TESTS_EXTRA_ARGS after_suite -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/vscode-notebook-tests/test --enable-proposed-api=vscode.vscode-notebook-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-notebook-tests --extensionTestsPath=$ROOT/extensions/vscode-notebook-tests/out/ --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $(mktemp -d 2>/dev/null) --enable-proposed-api=vscode.git --extensionDevelopmentPath=$ROOT/extensions/git --extensionTestsPath=$ROOT/extensions/git/out/test $ALL_PLATFORMS_API_TESTS_EXTRA_ARGS +after_suite + +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/vscode-notebook-tests/test --enable-proposed-api=vscode.vscode-notebook-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-notebook-tests --extensionTestsPath=$ROOT/extensions/vscode-notebook-tests/out/ $ALL_PLATFORMS_API_TESTS_EXTRA_ARGS after_suite # Tests in commonJS (CSS, HTML) diff --git a/src/main.js b/src/main.js index 0541fb4c4a0..2584223e1d6 100644 --- a/src/main.js +++ b/src/main.js @@ -9,14 +9,14 @@ const perf = require('./vs/base/common/performance'); perf.mark('code/didStartMain'); -const lp = require('./vs/base/node/languagePacks'); const path = require('path'); const fs = require('fs'); const os = require('os'); +const { getNLSConfiguration } = require('./vs/base/node/languagePacks'); const bootstrap = require('./bootstrap'); const bootstrapNode = require('./bootstrap-node'); -const paths = require('./paths'); -/** @type {Partial & { applicationName: string}} */ +const { getDefaultUserDataPath } = require('./vs/base/node/userDataPath'); +/** @type {Partial} */ const product = require('../product.json'); const { app, protocol, crashReporter } = require('electron'); @@ -84,7 +84,7 @@ let nlsConfigurationPromise = undefined; const metaDataFile = path.join(__dirname, 'nls.metadata.json'); const locale = getUserDefinedLocale(argvConfig); if (locale) { - nlsConfigurationPromise = lp.getNLSConfiguration(product.commit, userDataPath, metaDataFile, locale); + nlsConfigurationPromise = getNLSConfiguration(product.commit, userDataPath, metaDataFile, locale); } // Load our code once ready @@ -405,7 +405,7 @@ function getUserDataPath(cliArgs) { return path.join(portable.portableDataPath, 'user-data'); } - return path.resolve(cliArgs['user-data-dir'] || paths.getDefaultUserDataPath()); + return path.resolve(cliArgs['user-data-dir'] || getDefaultUserDataPath()); } /** @@ -560,7 +560,7 @@ async function resolveNlsConfiguration() { // See above the comment about the loader and case sensitiviness appLocale = appLocale.toLowerCase(); - nlsConfiguration = await lp.getNLSConfiguration(product.commit, userDataPath, metaDataFile, appLocale); + nlsConfiguration = await getNLSConfiguration(product.commit, userDataPath, metaDataFile, appLocale); if (!nlsConfiguration) { nlsConfiguration = { locale: appLocale, availableLanguages: {} }; } diff --git a/src/paths.js b/src/paths.js deleted file mode 100644 index 2042123d726..00000000000 --- a/src/paths.js +++ /dev/null @@ -1,48 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -//@ts-check -'use strict'; - -const pkg = require('../package.json'); -const path = require('path'); -const os = require('os'); - -/** - * @returns {string} - */ -function getDefaultUserDataPath() { - - // Support global VSCODE_APPDATA environment variable - let appDataPath = process.env['VSCODE_APPDATA']; - - // Otherwise check per platform - if (!appDataPath) { - switch (process.platform) { - case 'win32': - appDataPath = process.env['APPDATA']; - if (!appDataPath) { - const userProfile = process.env['USERPROFILE']; - if (typeof userProfile !== 'string') { - throw new Error('Windows: Unexpected undefined %USERPROFILE% environment variable'); - } - appDataPath = path.join(userProfile, 'AppData', 'Roaming'); - } - break; - case 'darwin': - appDataPath = path.join(os.homedir(), 'Library', 'Application Support'); - break; - case 'linux': - appDataPath = process.env['XDG_CONFIG_HOME'] || path.join(os.homedir(), '.config'); - break; - default: - throw new Error('Platform not supported'); - } - } - - return path.join(appDataPath, pkg.name); -} - -exports.getDefaultUserDataPath = getDefaultUserDataPath; diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index 81457733d22..5378627e57a 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -383,5 +383,16 @@ export function renderMarkdownAsPlaintext(markdown: IMarkdownString) { if (value.length > 100_000) { value = `${value.substr(0, 100_000)}…`; } - return sanitizeRenderedMarkdown({ isTrusted: false }, marked.parse(value, { renderer })).toString(); + + const unescapeInfo = new Map([ + ['"', '"'], + ['&', '&'], + [''', '\''], + ['<', '<'], + ['>', '>'], + ]); + + const html = marked.parse(value, { renderer }).replace(/&(#\d+|[a-zA-Z]+);/g, m => unescapeInfo.get(m) ?? m); + + return sanitizeRenderedMarkdown({ isTrusted: false }, html).toString(); } diff --git a/src/vs/base/browser/ui/actionbar/actionViewItems.ts b/src/vs/base/browser/ui/actionbar/actionViewItems.ts index 3d8ddb9c78c..9abeda1c547 100644 --- a/src/vs/base/browser/ui/actionbar/actionViewItems.ts +++ b/src/vs/base/browser/ui/actionbar/actionViewItems.ts @@ -14,7 +14,7 @@ import { EventType as TouchEventType, Gesture } from 'vs/base/browser/touch'; import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; import { DataTransfers } from 'vs/base/browser/dnd'; import { isFirefox } from 'vs/base/browser/browser'; -import { $, addDisposableListener, append, EventHelper, EventLike, EventType, removeTabIndexAndUpdateFocus } from 'vs/base/browser/dom'; +import { $, addDisposableListener, append, EventHelper, EventLike, EventType } from 'vs/base/browser/dom'; export interface IBaseActionViewItemOptions { draggable?: boolean; @@ -356,7 +356,6 @@ export class ActionViewItem extends BaseActionViewItem { if (this.label) { this.label.setAttribute('aria-disabled', 'true'); this.label.classList.add('disabled'); - removeTabIndexAndUpdateFocus(this.label); } if (this.element) { diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index 4ba146296b9..0dd80a02478 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -35,7 +35,6 @@ export interface IActionBarOptions { readonly triggerKeys?: ActionTrigger; readonly allowContextMenu?: boolean; readonly preventLoopNavigation?: boolean; - readonly ignoreOrientationForPreviousAndNextKey?: boolean; } export interface IActionOptions extends IActionViewItemOptions { @@ -119,22 +118,22 @@ export class ActionBar extends Disposable implements IActionRunner { switch (this._orientation) { case ActionsOrientation.HORIZONTAL: - previousKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.LeftArrow, KeyCode.UpArrow] : [KeyCode.LeftArrow]; - nextKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.RightArrow, KeyCode.DownArrow] : [KeyCode.RightArrow]; + previousKeys = [KeyCode.LeftArrow]; + nextKeys = [KeyCode.RightArrow]; break; case ActionsOrientation.HORIZONTAL_REVERSE: - previousKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.RightArrow, KeyCode.DownArrow] : [KeyCode.RightArrow]; - nextKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.LeftArrow, KeyCode.UpArrow] : [KeyCode.LeftArrow]; + previousKeys = [KeyCode.RightArrow]; + nextKeys = [KeyCode.LeftArrow]; this.domNode.className += ' reverse'; break; case ActionsOrientation.VERTICAL: - previousKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.LeftArrow, KeyCode.UpArrow] : [KeyCode.UpArrow]; - nextKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.RightArrow, KeyCode.DownArrow] : [KeyCode.DownArrow]; + previousKeys = [KeyCode.UpArrow]; + nextKeys = [KeyCode.DownArrow]; this.domNode.className += ' vertical'; break; case ActionsOrientation.VERTICAL_REVERSE: - previousKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.RightArrow, KeyCode.DownArrow] : [KeyCode.DownArrow]; - nextKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.LeftArrow, KeyCode.UpArrow] : [KeyCode.UpArrow]; + previousKeys = [KeyCode.DownArrow]; + nextKeys = [KeyCode.UpArrow]; this.domNode.className += ' vertical reverse'; break; } @@ -223,13 +222,13 @@ export class ActionBar extends Disposable implements IActionRunner { // Some action bars should not be focusable at times // When an action bar is not focusable make sure to make all the elements inside it not focusable - // When an action bar is focusable again, make sure the first enabled item can be focused + // When an action bar is focusable again, make sure the first item can be focused setFocusable(focusable: boolean): void { this.focusable = focusable; if (this.focusable) { - const firstEnabled = this.viewItems.find(vi => vi instanceof BaseActionViewItem && vi.isEnabled()); - if (firstEnabled instanceof BaseActionViewItem) { - firstEnabled.setFocusable(true); + const first = this.viewItems.find(vi => vi instanceof BaseActionViewItem); + if (first instanceof BaseActionViewItem) { + first.setFocusable(true); } } else { this.viewItems.forEach(vi => { @@ -318,7 +317,7 @@ export class ActionBar extends Disposable implements IActionRunner { item.setActionContext(this.context); item.render(actionViewItemElement); - if (this.focusable && this.viewItems.every(i => !i.isEnabled()) && item instanceof BaseActionViewItem && item.isEnabled()) { + if (this.focusable && this.viewItems.length === 0 && item instanceof BaseActionViewItem) { // We need to allow for the first enabled item to be focused on using tab navigation #106441 item.setFocusable(true); } diff --git a/src/vs/base/browser/ui/button/button.ts b/src/vs/base/browser/ui/button/button.ts index ebfe8d59f17..473eb2a2ec4 100644 --- a/src/vs/base/browser/ui/button/button.ts +++ b/src/vs/base/browser/ui/button/button.ts @@ -12,7 +12,7 @@ import { Event as BaseEvent, Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { Gesture, EventType as TouchEventType } from 'vs/base/browser/touch'; import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; -import { addDisposableListener, IFocusTracker, EventType, EventHelper, trackFocus, reset, removeTabIndexAndUpdateFocus } from 'vs/base/browser/dom'; +import { addDisposableListener, IFocusTracker, EventType, EventHelper, trackFocus, reset } from 'vs/base/browser/dom'; import { IContextMenuProvider } from 'vs/base/browser/contextmenu'; import { Action, IAction, IActionRunner } from 'vs/base/common/actions'; import { CSSIcon, Codicon } from 'vs/base/common/codicons'; @@ -214,7 +214,6 @@ export class Button extends Disposable implements IButton { } else { this._element.classList.add('disabled'); this._element.setAttribute('aria-disabled', String(true)); - removeTabIndexAndUpdateFocus(this._element); } } diff --git a/src/vs/base/browser/ui/checkbox/checkbox.ts b/src/vs/base/browser/ui/checkbox/checkbox.ts index cd9e4a2718c..d9319424fc5 100644 --- a/src/vs/base/browser/ui/checkbox/checkbox.ts +++ b/src/vs/base/browser/ui/checkbox/checkbox.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./checkbox'; -import * as DOM from 'vs/base/browser/dom'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { Widget } from 'vs/base/browser/ui/widget'; import { Color } from 'vs/base/common/color'; @@ -76,6 +75,26 @@ export class CheckboxActionViewItem extends BaseActionViewItem { } } + focus(): void { + if (this.checkbox) { + this.checkbox.domNode.tabIndex = 0; + this.checkbox.focus(); + } + } + + blur(): void { + if (this.checkbox) { + this.checkbox.domNode.tabIndex = -1; + this.checkbox.domNode.blur(); + } + } + + setFocusable(focusable: boolean): void { + if (this.checkbox) { + this.checkbox.domNode.tabIndex = focusable ? 0 : -1; + } + } + dispose(): void { this.disposables.dispose(); super.dispose(); @@ -191,12 +210,10 @@ export class Checkbox extends Widget { } enable(): void { - this.domNode.tabIndex = 0; this.domNode.setAttribute('aria-disabled', String(false)); } disable(): void { - DOM.removeTabIndexAndUpdateFocus(this.domNode); this.domNode.setAttribute('aria-disabled', String(true)); } } diff --git a/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts index 353b766ef1d..5b46a52a549 100644 --- a/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts +++ b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts @@ -172,12 +172,14 @@ export class ActionWithDropdownActionViewItem extends ActionViewItem { const menuActionsProvider = { getActions: () => { const actionsProvider = (this.options).menuActionsOrProvider; - return [this._action, ...(Array.isArray(actionsProvider) ? actionsProvider : actionsProvider.getActions())]; + return [this._action, ...(Array.isArray(actionsProvider) + ? actionsProvider + : (actionsProvider as IActionProvider).getActions()) // TODO: microsoft/TypeScript#42768 + ]; } }; this.dropdownMenuActionViewItem = new DropdownMenuActionViewItem(this._register(new Action('dropdownAction', undefined)), menuActionsProvider, this.contextMenuProvider, { classNames: ['dropdown', ...Codicon.dropDownButton.classNamesArray, ...(this.options).menuActionClassNames || []] }); this.dropdownMenuActionViewItem.render(this.element); } } - } diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 81caae42707..d399ef27bd7 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -761,6 +761,11 @@ export class DefaultStyleController implements IStyleController { `); } + if (styles.listInactiveFocusForeground) { + content.push(`.monaco-list${suffix} .monaco-list-row.focused { color: ${styles.listInactiveFocusForeground}; }`); + content.push(`.monaco-list${suffix} .monaco-list-row.focused:hover { color: ${styles.listInactiveFocusForeground}; }`); // overwrite :hover style in this case! + } + if (styles.listInactiveFocusBackground) { content.push(`.monaco-list${suffix} .monaco-list-row.focused { background-color: ${styles.listInactiveFocusBackground}; }`); content.push(`.monaco-list${suffix} .monaco-list-row.focused:hover { background-color: ${styles.listInactiveFocusBackground}; }`); // overwrite :hover style in this case! @@ -776,7 +781,7 @@ 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) .monaco-list-row:hover:not(.selected):not(.focused) { background-color: ${styles.listHoverBackground}; }`); } if (styles.listHoverForeground) { @@ -867,6 +872,7 @@ export interface IListStyles { listFocusAndSelectionForeground?: Color; listInactiveSelectionBackground?: Color; listInactiveSelectionForeground?: Color; + listInactiveFocusForeground?: Color; listInactiveFocusBackground?: Color; listHoverBackground?: Color; listHoverForeground?: Color; diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index 947ba2708b0..025428f4046 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -8,7 +8,7 @@ import * as strings from 'vs/base/common/strings'; import { IActionRunner, IAction, SubmenuAction, Separator, IActionViewItemProvider, EmptySubmenuAction } from 'vs/base/common/actions'; import { ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { ResolvedKeybinding, KeyCode } from 'vs/base/common/keyCodes'; -import { EventType, EventHelper, EventLike, removeTabIndexAndUpdateFocus, isAncestor, addDisposableListener, append, $, clearNode, createStyleSheet, isInShadowDOM, getActiveElement, Dimension, IDomNodePagePosition } from 'vs/base/browser/dom'; +import { EventType, EventHelper, EventLike, isAncestor, addDisposableListener, append, $, clearNode, createStyleSheet, isInShadowDOM, getActiveElement, Dimension, IDomNodePagePosition } from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { RunOnceScheduler } from 'vs/base/common/async'; import { DisposableStore } from 'vs/base/common/lifecycle'; @@ -617,20 +617,23 @@ class BaseMenuActionViewItem extends BaseActionViewItem { if (this.getAction().enabled) { if (this.element) { this.element.classList.remove('disabled'); + this.element.removeAttribute('aria-disabled'); } if (this.item) { this.item.classList.remove('disabled'); + this.item.removeAttribute('aria-disabled'); this.item.tabIndex = 0; } } else { if (this.element) { this.element.classList.add('disabled'); + this.element.setAttribute('aria-disabled', 'true'); } if (this.item) { this.item.classList.add('disabled'); - removeTabIndexAndUpdateFocus(this.item); + this.item.setAttribute('aria-disabled', 'true'); } } } diff --git a/src/vs/base/browser/ui/menu/menubar.css b/src/vs/base/browser/ui/menu/menubar.css index bfa79a1c523..e6aa5f4ddbe 100644 --- a/src/vs/base/browser/ui/menu/menubar.css +++ b/src/vs/base/browser/ui/menu/menubar.css @@ -42,7 +42,7 @@ } .menubar .menubar-menu-items-holder { - position: absolute; + position: fixed; left: 0px; opacity: 1; z-index: 2000; diff --git a/src/vs/base/browser/ui/sash/sash.ts b/src/vs/base/browser/ui/sash/sash.ts index f82138b8626..bceaf6e4e9b 100644 --- a/src/vs/base/browser/ui/sash/sash.ts +++ b/src/vs/base/browser/ui/sash/sash.ts @@ -398,13 +398,21 @@ export class Sash extends Disposable { })); } - private static onMouseEnter(sash: Sash): void { + private static onMouseEnter(sash: Sash, fromLinkedSash: boolean = false): void { sash.hoverDelayer.trigger(() => sash.el.classList.add('hover')); + + if (!fromLinkedSash && sash.linkedSash) { + Sash.onMouseEnter(sash.linkedSash, true); + } } - private static onMouseLeave(sash: Sash): void { + private static onMouseLeave(sash: Sash, fromLinkedSash: boolean = false): void { sash.hoverDelayer.cancel(); sash.el.classList.remove('hover'); + + if (!fromLinkedSash && sash.linkedSash) { + Sash.onMouseLeave(sash.linkedSash, true); + } } layout(): void { diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index 841f2ee44b0..9a2fab22e13 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -736,8 +736,8 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.Enter).on(e => this.onEnter(e), this)); this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.Escape).on(e => this.onEscape(e), this)); - this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.UpArrow).on(this.onUpArrow, this)); - this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.DownArrow).on(this.onDownArrow, this)); + this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.UpArrow).on(e => this.onUpArrow(e), this)); + this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.DownArrow).on(e => this.onDownArrow(e), this)); this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.PageDown).on(this.onPageDown, this)); this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.PageUp).on(this.onPageUp, this)); this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.Home).on(this.onHome, this)); @@ -916,8 +916,9 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi } // List navigation - have to handle a disabled option (jump over) - private onDownArrow(): void { + private onDownArrow(e: StandardKeyboardEvent): void { if (this.selected < this.options.length - 1) { + dom.EventHelper.stop(e, true); // Skip disabled options const nextOptionDisabled = this.options[this.selected + 1].isDisabled; @@ -937,8 +938,9 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi } } - private onUpArrow(): void { + private onUpArrow(e: StandardKeyboardEvent): void { if (this.selected > 0) { + dom.EventHelper.stop(e, true); // Skip disabled options const previousOptionDisabled = this.options[this.selected - 1].isDisabled; if (previousOptionDisabled && this.selected > 1) { diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 4df19745cfe..af081547c85 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -961,7 +961,7 @@ export interface IAbstractTreeOptionsUpdate extends ITreeRendererOptions { readonly filterOnType?: boolean; readonly smoothScrolling?: boolean; readonly horizontalScrolling?: boolean; - readonly expandOnlyOnDoubleClick?: boolean; + readonly expandOnDoubleClick?: boolean; readonly expandOnlyOnTwistieClick?: boolean | ((e: any) => boolean); // e is T } @@ -1121,7 +1121,7 @@ class TreeNodeListMouseController extends MouseController< return super.onViewPointer(e); } - if (this.tree.expandOnlyOnDoubleClick && e.browserEvent.detail !== 2 && !onTwistie) { + if (!this.tree.expandOnDoubleClick && e.browserEvent.detail === 2) { return super.onViewPointer(e); } @@ -1129,6 +1129,7 @@ class TreeNodeListMouseController extends MouseController< const model = ((this.tree as any).model as ITreeModel); // internal const location = model.getNodeLocation(node); const recursive = e.browserEvent.altKey; + this.tree.setFocus([location]); model.setCollapsed(location, undefined, recursive); if (expandOnlyOnTwistieClick && onTwistie) { @@ -1142,7 +1143,7 @@ class TreeNodeListMouseController extends MouseController< protected onDoubleClick(e: IListMouseEvent>): void { const onTwistie = (e.browserEvent.target as HTMLElement).classList.contains('monaco-tl-twistie'); - if (onTwistie) { + if (onTwistie || !this.tree.expandOnDoubleClick) { return; } @@ -1262,8 +1263,8 @@ export abstract class AbstractTree implements IDisposable get filterOnType(): boolean { return !!this._options.filterOnType; } get onDidChangeTypeFilterPattern(): Event { return this.typeFilterController ? this.typeFilterController.onDidChangePattern : Event.None; } - get expandOnlyOnDoubleClick(): boolean { return this._options.expandOnlyOnDoubleClick ?? false; } - get expandOnlyOnTwistieClick(): boolean | ((e: T) => boolean) { return typeof this._options.expandOnlyOnTwistieClick === 'undefined' ? false : this._options.expandOnlyOnTwistieClick; } + get expandOnDoubleClick(): boolean { return typeof this._options.expandOnDoubleClick === 'undefined' ? true : this._options.expandOnDoubleClick; } + get expandOnlyOnTwistieClick(): boolean | ((e: T) => boolean) { return typeof this._options.expandOnlyOnTwistieClick === 'undefined' ? true : this._options.expandOnlyOnTwistieClick; } private readonly _onDidUpdateOptions = new Emitter>(); readonly onDidUpdateOptions: Event> = this._onDidUpdateOptions.event; diff --git a/src/vs/base/browser/ui/tree/media/tree.css b/src/vs/base/browser/ui/tree/media/tree.css index 06f775d6246..2701b559d5e 100644 --- a/src/vs/base/browser/ui/tree/media/tree.css +++ b/src/vs/base/browser/ui/tree/media/tree.css @@ -56,6 +56,10 @@ overflow: hidden; } +.monaco-tl-twistie::before { + border-radius: 20px; +} + .monaco-tl-twistie.collapsed::before { transform: rotate(-90deg); } diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index 0e08876e537..685cbaa95e4 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -1048,3 +1048,85 @@ export class LRUCache extends LinkedMap { } } } + +type IndexRecord = [fn: (value: V) => R, map: Map]; + +/** + * Map that supports multiple indicies whose keys are derived from the value. + */ +export class IndexedSet implements Set { + public get size() { + return this.source.size; + } + + private source = new Set(); + private readonly indexes: IndexRecord[] = []; + + /** + * Creates a map that maintains a copy of the items in the set, indexed + * by the given 'indexer' function. + */ + index(indexer: (value: V) => R) { + const map = new Map(); + for (const value of this.source) { + map.set(indexer(value), value); + } + + this.indexes.push([indexer, map]); + return map as ReadonlyMap; + } + + add(value: V): this { + this.source.add(value); + for (const [index, map] of this.indexes) { + map.set(index(value), value); + } + + return this; + } + + clear(): void { + this.source.clear(); + for (const [, map] of this.indexes) { + map.clear(); + } + } + + delete(value: V): boolean { + if (!this.source.delete(value)) { + return false; + } + + for (const [index, map] of this.indexes) { + map.delete(index(value)); + } + + return true; + } + + forEach(callbackfn: (value: V, value2: V, set: Set) => void, thisArg?: any): void { + this.source.forEach(callbackfn, thisArg); + } + + has(value: V): boolean { + return this.source.has(value); + } + + [Symbol.toStringTag]: string; + + values(): IterableIterator { + return this.source.values(); + } + + entries(): IterableIterator<[V, V]> { + return this.source.entries(); + } + + keys(): IterableIterator { + return this.source.keys(); + } + + [Symbol.iterator](): IterableIterator { + return this.source.values(); + } +} diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index a409e7722ab..402d5149722 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -64,6 +64,8 @@ export namespace Schemas { export const vscodeSettings = 'vscode-settings'; + export const vscodeWorkspaceTrust = 'vscode-workspace-trust'; + export const webviewPanel = 'webview-panel'; /** diff --git a/src/vs/base/common/types.ts b/src/vs/base/common/types.ts index da93a17e08a..a91a3cfd174 100644 --- a/src/vs/base/common/types.ts +++ b/src/vs/base/common/types.ts @@ -8,7 +8,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; /** * @returns whether the provided parameter is a JavaScript Array or not. */ -export function isArray(array: T | {}): array is T extends readonly any[] ? (unknown extends T ? never : readonly any[]) : any[] { +export function isArray(array: any): array is any[] { return Array.isArray(array); } diff --git a/src/vs/base/node/languagePacks.js b/src/vs/base/node/languagePacks.js index f47191157c1..5c14fade878 100644 --- a/src/vs/base/node/languagePacks.js +++ b/src/vs/base/node/languagePacks.js @@ -3,256 +3,257 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/// + //@ts-check -'use strict'; - -/** - * @param {NodeRequire} nodeRequire - * @param {typeof import('path')} path - * @param {typeof import('fs')} fs - * @param {typeof import('../common/performance')} perf - */ -function factory(nodeRequire, path, fs, perf) { +(function () { + 'use strict'; /** - * @param {string} file - * @returns {Promise} + * @param {NodeRequire} nodeRequire + * @param {typeof import('path')} path + * @param {typeof import('fs')} fs + * @param {typeof import('../common/performance')} perf */ - function exists(file) { - return new Promise(c => fs.exists(file, c)); - } + function factory(nodeRequire, path, fs, perf) { - /** - * @param {string} file - * @returns {Promise} - */ - function touch(file) { - return new Promise((c, e) => { const d = new Date(); fs.utimes(file, d, d, err => err ? e(err) : c()); }); - } - - /** - * @param {string} dir - * @returns {Promise} - */ - function mkdirp(dir) { - return new Promise((c, e) => fs.mkdir(dir, { recursive: true }, err => (err && err.code !== 'EEXIST') ? e(err) : c(dir))); - } - - /** - * @param {string} location - * @returns {Promise} - */ - function rimraf(location) { - return new Promise((c, e) => fs.rmdir(location, { recursive: true }, err => (err && err.code !== 'ENOENT') ? e(err) : c())); - } - - /** - * @param {string} file - * @returns {Promise} - */ - function readFile(file) { - return new Promise((c, e) => fs.readFile(file, 'utf8', (err, data) => err ? e(err) : c(data))); - } - - /** - * @param {string} file - * @param {string} content - * @returns {Promise} - */ - function writeFile(file, content) { - return new Promise((c, e) => fs.writeFile(file, content, 'utf8', err => err ? e(err) : c())); - } - - /** - * @param {string} userDataPath - * @returns {object} - */ - function getLanguagePackConfigurations(userDataPath) { - const configFile = path.join(userDataPath, 'languagepacks.json'); - try { - return nodeRequire(configFile); - } catch (err) { - // Do nothing. If we can't read the file we have no - // language pack config. + /** + * @param {string} file + * @returns {Promise} + */ + function exists(file) { + return new Promise(c => fs.exists(file, c)); } - return undefined; - } - /** - * @param {object} config - * @param {string} locale - */ - function resolveLanguagePackLocale(config, locale) { - try { - while (locale) { - if (config[locale]) { - return locale; - } else { - const index = locale.lastIndexOf('-'); - if (index > 0) { - locale = locale.substring(0, index); + /** + * @param {string} file + * @returns {Promise} + */ + function touch(file) { + return new Promise((c, e) => { const d = new Date(); fs.utimes(file, d, d, err => err ? e(err) : c()); }); + } + + /** + * @param {string} dir + * @returns {Promise} + */ + function mkdirp(dir) { + return new Promise((c, e) => fs.mkdir(dir, { recursive: true }, err => (err && err.code !== 'EEXIST') ? e(err) : c(dir))); + } + + /** + * @param {string} location + * @returns {Promise} + */ + function rimraf(location) { + return new Promise((c, e) => fs.rmdir(location, { recursive: true }, err => (err && err.code !== 'ENOENT') ? e(err) : c())); + } + + /** + * @param {string} file + * @returns {Promise} + */ + function readFile(file) { + return new Promise((c, e) => fs.readFile(file, 'utf8', (err, data) => err ? e(err) : c(data))); + } + + /** + * @param {string} file + * @param {string} content + * @returns {Promise} + */ + function writeFile(file, content) { + return new Promise((c, e) => fs.writeFile(file, content, 'utf8', err => err ? e(err) : c())); + } + + /** + * @param {string} userDataPath + * @returns {object} + */ + function getLanguagePackConfigurations(userDataPath) { + const configFile = path.join(userDataPath, 'languagepacks.json'); + try { + return nodeRequire(configFile); + } catch (err) { + // Do nothing. If we can't read the file we have no + // language pack config. + } + return undefined; + } + + /** + * @param {object} config + * @param {string} locale + */ + function resolveLanguagePackLocale(config, locale) { + try { + while (locale) { + if (config[locale]) { + return locale; } else { - return undefined; + const index = locale.lastIndexOf('-'); + if (index > 0) { + locale = locale.substring(0, index); + } else { + return undefined; + } } } + } catch (err) { + console.error('Resolving language pack configuration failed.', err); } - } catch (err) { - console.error('Resolving language pack configuration failed.', err); - } - return undefined; - } - - /** - * @param {string} commit - * @param {string} userDataPath - * @param {string} metaDataFile - * @param {string} locale - */ - function getNLSConfiguration(commit, userDataPath, metaDataFile, locale) { - if (locale === 'pseudo') { - return Promise.resolve({ locale: locale, availableLanguages: {}, pseudo: true }); + return undefined; } - if (process.env['VSCODE_DEV']) { - return Promise.resolve({ locale: locale, availableLanguages: {} }); - } - - // We have a built version so we have extracted nls file. Try to find - // the right file to use. - - // Check if we have an English or English US locale. If so fall to default since that is our - // English translation (we don't ship *.nls.en.json files) - if (locale && (locale === 'en' || locale === 'en-us')) { - return Promise.resolve({ locale: locale, availableLanguages: {} }); - } - - const initialLocale = locale; - - perf.mark('code/willGenerateNls'); - - const defaultResult = function (locale) { - perf.mark('code/didGenerateNls'); - return Promise.resolve({ locale: locale, availableLanguages: {} }); - }; - try { - if (!commit) { - return defaultResult(initialLocale); + /** + * @param {string} commit + * @param {string} userDataPath + * @param {string} metaDataFile + * @param {string} locale + */ + function getNLSConfiguration(commit, userDataPath, metaDataFile, locale) { + if (locale === 'pseudo') { + return Promise.resolve({ locale: locale, availableLanguages: {}, pseudo: true }); } - const configs = getLanguagePackConfigurations(userDataPath); - if (!configs) { - return defaultResult(initialLocale); + + if (process.env['VSCODE_DEV']) { + return Promise.resolve({ locale: locale, availableLanguages: {} }); } - locale = resolveLanguagePackLocale(configs, locale); - if (!locale) { - return defaultResult(initialLocale); + + // We have a built version so we have extracted nls file. Try to find + // the right file to use. + + // Check if we have an English or English US locale. If so fall to default since that is our + // English translation (we don't ship *.nls.en.json files) + if (locale && (locale === 'en' || locale === 'en-us')) { + return Promise.resolve({ locale: locale, availableLanguages: {} }); } - const packConfig = configs[locale]; - let mainPack; - if (!packConfig || typeof packConfig.hash !== 'string' || !packConfig.translations || typeof (mainPack = packConfig.translations['vscode']) !== 'string') { - return defaultResult(initialLocale); - } - return exists(mainPack).then(fileExists => { - if (!fileExists) { + + const initialLocale = locale; + + perf.mark('code/willGenerateNls'); + + const defaultResult = function (locale) { + perf.mark('code/didGenerateNls'); + return Promise.resolve({ locale: locale, availableLanguages: {} }); + }; + try { + if (!commit) { return defaultResult(initialLocale); } - const packId = packConfig.hash + '.' + locale; - const cacheRoot = path.join(userDataPath, 'clp', packId); - const coreLocation = path.join(cacheRoot, commit); - const translationsConfigFile = path.join(cacheRoot, 'tcf.json'); - const corruptedFile = path.join(cacheRoot, 'corrupted.info'); - const result = { - locale: initialLocale, - availableLanguages: { '*': locale }, - _languagePackId: packId, - _translationsConfigFile: translationsConfigFile, - _cacheRoot: cacheRoot, - _resolvedLanguagePackCoreLocation: coreLocation, - _corruptedFile: corruptedFile - }; - return exists(corruptedFile).then(corrupted => { - // The nls cache directory is corrupted. - let toDelete; - if (corrupted) { - toDelete = rimraf(cacheRoot); - } else { - toDelete = Promise.resolve(undefined); + const configs = getLanguagePackConfigurations(userDataPath); + if (!configs) { + return defaultResult(initialLocale); + } + locale = resolveLanguagePackLocale(configs, locale); + if (!locale) { + return defaultResult(initialLocale); + } + const packConfig = configs[locale]; + let mainPack; + if (!packConfig || typeof packConfig.hash !== 'string' || !packConfig.translations || typeof (mainPack = packConfig.translations['vscode']) !== 'string') { + return defaultResult(initialLocale); + } + return exists(mainPack).then(fileExists => { + if (!fileExists) { + return defaultResult(initialLocale); } - return toDelete.then(() => { - return exists(coreLocation).then(fileExists => { - if (fileExists) { - // We don't wait for this. No big harm if we can't touch - touch(coreLocation).catch(() => { }); - perf.mark('code/didGenerateNls'); - return result; - } - return mkdirp(coreLocation).then(() => { - return Promise.all([readFile(metaDataFile), readFile(mainPack)]); - }).then(values => { - const metadata = JSON.parse(values[0]); - const packData = JSON.parse(values[1]).contents; - const bundles = Object.keys(metadata.bundles); - const writes = []; - for (const bundle of bundles) { - const modules = metadata.bundles[bundle]; - const target = Object.create(null); - for (const module of modules) { - const keys = metadata.keys[module]; - const defaultMessages = metadata.messages[module]; - const translations = packData[module]; - let targetStrings; - if (translations) { - targetStrings = []; - for (let i = 0; i < keys.length; i++) { - const elem = keys[i]; - const key = typeof elem === 'string' ? elem : elem.key; - let translatedMessage = translations[key]; - if (translatedMessage === undefined) { - translatedMessage = defaultMessages[i]; - } - targetStrings.push(translatedMessage); - } - } else { - targetStrings = defaultMessages; - } - target[module] = targetStrings; - } - writes.push(writeFile(path.join(coreLocation, bundle.replace(/\//g, '!') + '.nls.json'), JSON.stringify(target))); + const packId = packConfig.hash + '.' + locale; + const cacheRoot = path.join(userDataPath, 'clp', packId); + const coreLocation = path.join(cacheRoot, commit); + const translationsConfigFile = path.join(cacheRoot, 'tcf.json'); + const corruptedFile = path.join(cacheRoot, 'corrupted.info'); + const result = { + locale: initialLocale, + availableLanguages: { '*': locale }, + _languagePackId: packId, + _translationsConfigFile: translationsConfigFile, + _cacheRoot: cacheRoot, + _resolvedLanguagePackCoreLocation: coreLocation, + _corruptedFile: corruptedFile + }; + return exists(corruptedFile).then(corrupted => { + // The nls cache directory is corrupted. + let toDelete; + if (corrupted) { + toDelete = rimraf(cacheRoot); + } else { + toDelete = Promise.resolve(undefined); + } + return toDelete.then(() => { + return exists(coreLocation).then(fileExists => { + if (fileExists) { + // We don't wait for this. No big harm if we can't touch + touch(coreLocation).catch(() => { }); + perf.mark('code/didGenerateNls'); + return result; } - writes.push(writeFile(translationsConfigFile, JSON.stringify(packConfig.translations))); - return Promise.all(writes); - }).then(() => { - perf.mark('code/didGenerateNls'); - return result; - }).catch(err => { - console.error('Generating translation files failed.', err); - return defaultResult(locale); + return mkdirp(coreLocation).then(() => { + return Promise.all([readFile(metaDataFile), readFile(mainPack)]); + }).then(values => { + const metadata = JSON.parse(values[0]); + const packData = JSON.parse(values[1]).contents; + const bundles = Object.keys(metadata.bundles); + const writes = []; + for (const bundle of bundles) { + const modules = metadata.bundles[bundle]; + const target = Object.create(null); + for (const module of modules) { + const keys = metadata.keys[module]; + const defaultMessages = metadata.messages[module]; + const translations = packData[module]; + let targetStrings; + if (translations) { + targetStrings = []; + for (let i = 0; i < keys.length; i++) { + const elem = keys[i]; + const key = typeof elem === 'string' ? elem : elem.key; + let translatedMessage = translations[key]; + if (translatedMessage === undefined) { + translatedMessage = defaultMessages[i]; + } + targetStrings.push(translatedMessage); + } + } else { + targetStrings = defaultMessages; + } + target[module] = targetStrings; + } + writes.push(writeFile(path.join(coreLocation, bundle.replace(/\//g, '!') + '.nls.json'), JSON.stringify(target))); + } + writes.push(writeFile(translationsConfigFile, JSON.stringify(packConfig.translations))); + return Promise.all(writes); + }).then(() => { + perf.mark('code/didGenerateNls'); + return result; + }).catch(err => { + console.error('Generating translation files failed.', err); + return defaultResult(locale); + }); }); }); }); }); - }); - } catch (err) { - console.error('Generating translation files failed.', err); - return defaultResult(locale); + } catch (err) { + console.error('Generating translation files failed.', err); + return defaultResult(locale); + } } + + return { + getNLSConfiguration + }; } - return { - getNLSConfiguration - }; -} - - -// @ts-ignore -if (typeof define === 'function') { - // amd - // @ts-ignore - define(['path', 'fs', 'vs/base/common/performance'], function (path, fs, perf) { return factory(require.__$__nodeRequire, path, fs, perf); }); -} else if (typeof module === 'object' && typeof module.exports === 'object') { - const path = require('path'); - const fs = require('fs'); - const perf = require('../common/performance'); - module.exports = factory(require, path, fs, perf); -} else { - throw new Error('Unknown context'); -} + if (typeof define === 'function') { + // amd + define(['require', 'path', 'fs', 'vs/base/common/performance'], function (require, /** @type {typeof import('path')} */ path, /** @type {typeof import('fs')} */ fs, /** @type {typeof import('../common/performance')} */ perf) { return factory(require.__$__nodeRequire, path, fs, perf); }); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + const path = require('path'); + const fs = require('fs'); + const perf = require('../common/performance'); + module.exports = factory(require, path, fs, perf); + } else { + throw new Error('Unknown context'); + } +}()); diff --git a/test/ui/splitview/server.js b/src/vs/base/node/userDataPath.d.ts similarity index 50% rename from test/ui/splitview/server.js rename to src/vs/base/node/userDataPath.d.ts index 67f25363671..bc09b834a7e 100644 --- a/test/ui/splitview/server.js +++ b/src/vs/base/node/userDataPath.d.ts @@ -3,17 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -const fs = require('mz/fs'); -const path = require('path'); -const Koa = require('koa'); -const _ = require('koa-route'); -const serve = require('koa-static'); -const mount = require('koa-mount'); - -const app = new Koa(); - -app.use(serve('public')); -app.use(mount('/static', serve('../../out'))); - -app.listen(3000); -console.log('http://localhost:3000'); \ No newline at end of file +/** + * Returns the user data path to use. + */ +export function getDefaultUserDataPath(): string; diff --git a/src/vs/base/node/userDataPath.js b/src/vs/base/node/userDataPath.js new file mode 100644 index 00000000000..93384cb8f4e --- /dev/null +++ b/src/vs/base/node/userDataPath.js @@ -0,0 +1,72 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// + +//@ts-check +(function () { + 'use strict'; + + /** + * @param {typeof import('path')} path + * @param {typeof import('os')} os + * @param {string} productName + */ + function factory(path, os, productName) { + + function getDefaultUserDataPath() { + + // Support global VSCODE_APPDATA environment variable + let appDataPath = process.env['VSCODE_APPDATA']; + + // Otherwise check per platform + if (!appDataPath) { + switch (process.platform) { + case 'win32': + appDataPath = process.env['APPDATA']; + if (!appDataPath) { + const userProfile = process.env['USERPROFILE']; + if (typeof userProfile !== 'string') { + throw new Error('Windows: Unexpected undefined %USERPROFILE% environment variable'); + } + appDataPath = path.join(userProfile, 'AppData', 'Roaming'); + } + break; + case 'darwin': + appDataPath = path.join(os.homedir(), 'Library', 'Application Support'); + break; + case 'linux': + appDataPath = process.env['XDG_CONFIG_HOME'] || path.join(os.homedir(), '.config'); + break; + default: + throw new Error('Platform not supported'); + } + } + + return path.join(appDataPath, productName); + } + + return { + getDefaultUserDataPath + }; + } + + if (typeof define === 'function') { + define(['require', 'path', 'os', 'vs/base/common/network', 'vs/base/common/resources'], function (require, /** @type {typeof import('path')} */ path, /** @type {typeof import('os')} */ os, /** @type {typeof import('../common/network')} */ network, /** @type {typeof import("../common/resources")} */ resources) { + const rootPath = resources.dirname(network.FileAccess.asFileUri('', require)); + const pkg = require.__$__nodeRequire(resources.joinPath(rootPath, 'package.json').fsPath); + + return factory(path, os, pkg.name); + }); // amd + } else if (typeof module === 'object' && typeof module.exports === 'object') { + const pkg = require('../../../../package.json'); + const path = require('path'); + const os = require('os'); + + module.exports = factory(path, os, pkg.name); // commonjs + } else { + throw new Error('Unknown context'); + } +}()); diff --git a/src/vs/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts index 038816012f3..2ab5e7a478d 100644 --- a/src/vs/base/parts/quickinput/browser/quickInput.ts +++ b/src/vs/base/parts/quickinput/browser/quickInput.ts @@ -56,7 +56,7 @@ export interface IQuickInputStyles { countBadge: ICountBadgetyles; button: IButtonStyles; progressBar: IProgressBarStyles; - list: IListStyles & { listInactiveFocusForeground?: Color; pickerGroupBorder?: Color; pickerGroupForeground?: Color; }; + list: IListStyles & { pickerGroupBorder?: Color; pickerGroupForeground?: Color; }; } export interface IQuickInputWidgetStyles { @@ -1706,10 +1706,6 @@ export class QuickInputController extends Disposable { this.ui.list.style(this.styles.list); const content: string[] = []; - if (this.styles.list.listInactiveFocusForeground) { - content.push(`.monaco-list .monaco-list-row.focused { color: ${this.styles.list.listInactiveFocusForeground}; }`); - content.push(`.monaco-list .monaco-list-row.focused:hover { color: ${this.styles.list.listInactiveFocusForeground}; }`); // overwrite :hover style in this case! - } if (this.styles.list.pickerGroupBorder) { content.push(`.quick-input-list .quick-input-list-entry { border-top-color: ${this.styles.list.pickerGroupBorder}; }`); } diff --git a/src/vs/base/parts/storage/common/storage.ts b/src/vs/base/parts/storage/common/storage.ts index c2f5ff5a5cd..a2c28e17070 100644 --- a/src/vs/base/parts/storage/common/storage.ts +++ b/src/vs/base/parts/storage/common/storage.ts @@ -84,11 +84,13 @@ export class Storage extends Disposable implements IStorage { private cache = new Map(); - private readonly flushDelayer = this._register(new ThrottledDelayer(Storage.DEFAULT_FLUSH_DELAY)); + private readonly flushDelayer = new ThrottledDelayer(Storage.DEFAULT_FLUSH_DELAY); private pendingDeletes = new Set(); private pendingInserts = new Map(); + private pendingClose: Promise | undefined = undefined; + private readonly whenFlushedCallbacks: Function[] = []; constructor( @@ -256,10 +258,15 @@ export class Storage extends Disposable implements IStorage { } async close(): Promise { - if (this.state === StorageState.Closed) { - return; // return if already closed + if (!this.pendingClose) { + this.pendingClose = this.doClose(); } + return this.pendingClose; + } + + private async doClose(): Promise { + // Update state this.state = StorageState.Closed; @@ -312,6 +319,13 @@ export class Storage extends Disposable implements IStorage { return new Promise(resolve => this.whenFlushedCallbacks.push(resolve)); } + + dispose(): void { + this.flushDelayer.cancel(); // workaround https://github.com/microsoft/vscode/issues/116777 + this.flushDelayer.dispose(); + + super.dispose(); + } } export class InMemoryStorageDatabase implements IStorageDatabase { diff --git a/src/vs/base/parts/storage/test/node/storage.test.ts b/src/vs/base/parts/storage/test/node/storage.test.ts index 46193801f71..990e531905e 100644 --- a/src/vs/base/parts/storage/test/node/storage.test.ts +++ b/src/vs/base/parts/storage/test/node/storage.test.ts @@ -104,6 +104,7 @@ flakySuite('Storage Library', function () { strictEqual(deletePromiseResolved, true); await storage.close(); + await storage.close(); // it is ok to call this multiple times }); test('external changes', async () => { diff --git a/src/vs/base/test/common/map.test.ts b/src/vs/base/test/common/map.test.ts index f157455531a..0c9fc51e097 100644 --- a/src/vs/base/test/common/map.test.ts +++ b/src/vs/base/test/common/map.test.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { ResourceMap, TernarySearchTree, PathIterator, StringIterator, LinkedMap, Touch, LRUCache, UriIterator, ConfigKeysIterator } from 'vs/base/common/map'; -import { URI } from 'vs/base/common/uri'; +import { ConfigKeysIterator, IndexedSet, LinkedMap, LRUCache, PathIterator, ResourceMap, StringIterator, TernarySearchTree, Touch, UriIterator } from 'vs/base/common/map'; import { extUriIgnorePathCase } from 'vs/base/common/resources'; +import { URI } from 'vs/base/common/uri'; suite('Map', () => { @@ -1020,4 +1020,30 @@ suite('Map', () => { assert.strictEqual(map.get(windowsFile), 'true'); assert.strictEqual(map.get(uncFile), 'true'); }); + + test('IndexedSet - add', () => { + const i = new IndexedSet(); + i.add(2); + const map = i.index(v => v ** 2); + assert.deepStrictEqual(map, new Map([[4, 2]])); + + i.add(3); + assert.deepStrictEqual(map, new Map([[9, 3], [4, 2]])); + }); + + test('IndexedSet - delete', () => { + const i = new IndexedSet(); + const map = i.index(v => v ** 2); + i.add(2); + i.delete(2); + assert.deepStrictEqual(map, new Map([])); + }); + + test('IndexedSet - clear', () => { + const i = new IndexedSet(); + const map = i.index(v => v ** 2); + i.add(2); + i.clear(); + assert.deepStrictEqual(map, new Map([])); + }); }); diff --git a/src/vs/base/node/paths.ts b/src/vs/base/test/node/userDataPath.test.ts similarity index 56% rename from src/vs/base/node/paths.ts rename to src/vs/base/test/node/userDataPath.test.ts index eaf03e6e400..74d0ae4f26e 100644 --- a/src/vs/base/node/paths.ts +++ b/src/vs/base/test/node/userDataPath.test.ts @@ -3,9 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { FileAccess } from 'vs/base/common/network'; +import * as assert from 'assert'; +import { getDefaultUserDataPath } from 'vs/base/node/userDataPath'; -const pathsPath = FileAccess.asFileUri('paths', require).fsPath; -const paths = require.__$__nodeRequire<{ getDefaultUserDataPath(): string }>(pathsPath); +suite('User data path', () => { -export const getDefaultUserDataPath = paths.getDefaultUserDataPath; + test('getDefaultUserDataPath', () => { + const path = getDefaultUserDataPath(); + assert.ok(path.length > 0); + }); +}); diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcess.js b/src/vs/code/electron-browser/sharedProcess/sharedProcess.js index 2bb0a0bb730..50d8d3f7f8f 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcess.js +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcess.js @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ //@ts-check -'use strict'; - (function () { + 'use strict'; + const bootstrap = bootstrapLib(); const bootstrapWindow = bootstrapWindowLib(); @@ -38,5 +38,4 @@ } //#endregion - }()); diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 1e4f3c62849..f60f74463ca 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -58,8 +58,7 @@ import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { LoggerService } from 'vs/platform/log/node/loggerService'; import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog'; import { UserDataAutoSyncService } from 'vs/platform/userDataSync/electron-sandbox/userDataAutoSyncService'; -import { NativeStorageService } from 'vs/platform/storage/node/storageService'; -import { GlobalStorageDatabaseChannelClient } from 'vs/platform/storage/node/storageIpc'; +import { NativeStorageService2 } from 'vs/platform/storage/electron-sandbox/storageService2'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService'; import { UserDataSyncResourceEnablementService } from 'vs/platform/userDataSync/common/userDataSyncResourceEnablementService'; @@ -172,8 +171,8 @@ class SharedProcessMain extends Disposable { await configurationService.initialize(); - // Storage - const storageService = new NativeStorageService(new GlobalStorageDatabaseChannelClient(mainProcessService.getChannel('storage')), logService, environmentService); + // Storage (global access only) + const storageService = new NativeStorageService2(undefined, mainProcessService, environmentService); services.set(IStorageService, storageService); await storageService.initialize(); diff --git a/src/vs/code/electron-browser/workbench/workbench.js b/src/vs/code/electron-browser/workbench/workbench.js index 64082146a6d..0c52f3cda25 100644 --- a/src/vs/code/electron-browser/workbench/workbench.js +++ b/src/vs/code/electron-browser/workbench/workbench.js @@ -6,9 +6,9 @@ /// //@ts-check -'use strict'; - (function () { + 'use strict'; + const bootstrapWindow = bootstrapWindowLib(); // Add a perf entry right from the top @@ -179,5 +179,4 @@ } //#endregion - }()); diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index ef4909c7d6b..d6a819dee64 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -37,7 +37,7 @@ import product from 'vs/platform/product/common/product'; import { ProxyAuthHandler } from 'vs/code/electron-main/auth'; import { FileProtocolHandler } from 'vs/code/electron-main/protocol'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IWindowsMainService, ICodeWindow, OpenContext } from 'vs/platform/windows/electron-main/windows'; +import { IWindowsMainService, ICodeWindow, OpenContext, WindowError } from 'vs/platform/windows/electron-main/windows'; import { URI } from 'vs/base/common/uri'; import { hasWorkspaceFileExtension, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; @@ -58,8 +58,8 @@ import { joinPath } from 'vs/base/common/resources'; import { localize } from 'vs/nls'; import { Schemas } from 'vs/base/common/network'; import { SnapUpdateService } from 'vs/platform/update/electron-main/updateService.snap'; -import { IStorageMainService, StorageMainService } from 'vs/platform/storage/node/storageMainService'; -import { GlobalStorageDatabaseChannel } from 'vs/platform/storage/node/storageIpc'; +import { IStorageMainService, StorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; +import { StorageDatabaseChannel } from 'vs/platform/storage/electron-main/storageIpc'; import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService'; import { IBackupMainService } from 'vs/platform/backup/electron-main/backup'; import { WorkspacesHistoryMainService, IWorkspacesHistoryMainService } from 'vs/platform/workspaces/electron-main/workspacesHistoryMainService'; @@ -102,7 +102,7 @@ export class CodeApplication extends Disposable { private readonly userEnv: IProcessEnvironment, @IInstantiationService private readonly mainInstantiationService: IInstantiationService, @ILogService private readonly logService: ILogService, - @IEnvironmentMainService private readonly environmentService: IEnvironmentMainService, + @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @IConfigurationService private readonly configurationService: IConfigurationService, @IStateService private readonly stateService: IStateService, @@ -168,7 +168,7 @@ export class CodeApplication extends Disposable { event.preventDefault(); }); app.on('remote-get-current-web-contents', event => { - if (this.environmentService.args.driver) { + if (this.environmentMainService.args.driver) { return; // the driver needs access to web contents } @@ -190,7 +190,7 @@ export class CodeApplication extends Disposable { } const srcUri = uri.fsPath.toLowerCase(); - const rootUri = URI.file(this.environmentService.appRoot).fsPath.toLowerCase(); + const rootUri = URI.file(this.environmentMainService.appRoot).fsPath.toLowerCase(); return srcUri.startsWith(rootUri + sep); }; @@ -255,7 +255,7 @@ export class CodeApplication extends Disposable { runningTimeout = setTimeout(() => { this.windowsMainService?.open({ context: OpenContext.DOCK /* can also be opening from finder while app is running */, - cli: this.environmentService.args, + cli: this.environmentMainService.args, urisToOpen: macOpenFileURIs, gotoLineMode: false, preferNewWindow: true /* dropping on the dock or opening from finder prefers to open in a new window */ @@ -328,7 +328,7 @@ export class CodeApplication extends Disposable { args = window.config; env = { ...process.env, ...window.config.userEnv }; } else { - args = this.environmentService.args; + args = this.environmentMainService.args; env = process.env; } @@ -376,7 +376,7 @@ export class CodeApplication extends Disposable { } } - if (typeof path !== 'string' || !isAbsolute(path) || !isEqualOrParent(path, this.environmentService.cachedLanguagesPath, !isLinux)) { + if (typeof path !== 'string' || !isAbsolute(path) || !isEqualOrParent(path, this.environmentMainService.cachedLanguagesPath, !isLinux)) { return undefined; } @@ -388,7 +388,7 @@ export class CodeApplication extends Disposable { // take only the message and stack property const friendlyError = { - message: err.message, + message: `[uncaught exception in main]: ${err.message}`, stack: err.stack }; @@ -404,8 +404,8 @@ export class CodeApplication extends Disposable { async startup(): Promise { this.logService.debug('Starting VS Code'); - this.logService.debug(`from: ${this.environmentService.appRoot}`); - this.logService.debug('args:', this.environmentService.args); + this.logService.debug(`from: ${this.environmentMainService.appRoot}`); + this.logService.debug('args:', this.environmentMainService.args); // Make sure we associate the program with the app user model id // This will help Windows to associate the running program with @@ -448,10 +448,10 @@ export class CodeApplication extends Disposable { const appInstantiationService = await this.initServices(machineId, sharedProcess, sharedProcessReady); // Create driver - if (this.environmentService.driverHandle) { - const server = await serveDriver(mainProcessElectronServer, this.environmentService.driverHandle, this.environmentService, appInstantiationService); + if (this.environmentMainService.driverHandle) { + const server = await serveDriver(mainProcessElectronServer, this.environmentMainService.driverHandle, this.environmentMainService, appInstantiationService); - this.logService.info('Driver started at:', this.environmentService.driverHandle); + this.logService.info('Driver started at:', this.environmentMainService.driverHandle); this._register(server); } @@ -465,10 +465,10 @@ export class CodeApplication extends Disposable { const windows = appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor, mainProcessElectronServer, fileProtocolHandler)); // Post Open Windows Tasks - appInstantiationService.invokeFunction(accessor => this.afterWindowOpen(accessor)); + appInstantiationService.invokeFunction(accessor => this.afterWindowOpen(accessor, sharedProcess)); // Tracing: Stop tracing after windows are ready if enabled - if (this.environmentService.args.trace) { + if (this.environmentMainService.args.trace) { appInstantiationService.invokeFunction(accessor => this.stopTracingEventually(accessor, windows)); } } @@ -509,7 +509,7 @@ export class CodeApplication extends Disposable { // Spawn shared process after the first window has opened and 3s have passed this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => { this._register(new RunOnceScheduler(async () => { - sharedProcess.spawn(await resolveShellEnv(this.logService, this.environmentService.args, process.env)); + sharedProcess.spawn(await resolveShellEnv(this.logService, this.environmentMainService.args, process.env)); }, 3000)).schedule(); }); @@ -580,23 +580,21 @@ export class CodeApplication extends Disposable { services.set(IExtensionUrlTrustService, new SyncDescriptor(ExtensionUrlTrustService)); // Storage - const storageMainService = new StorageMainService(this.logService, this.environmentService); - services.set(IStorageMainService, storageMainService); - this.lifecycleMainService.onWillShutdown(e => e.join(storageMainService.close())); + services.set(IStorageMainService, new SyncDescriptor(StorageMainService)); // Backups - const backupMainService = new BackupMainService(this.environmentService, this.configurationService, this.logService); + const backupMainService = new BackupMainService(this.environmentMainService, this.configurationService, this.logService); services.set(IBackupMainService, backupMainService); // URL handling services.set(IURLService, new SyncDescriptor(NativeURLService)); // Telemetry - if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) { + if (!this.environmentMainService.isExtensionDevelopment && !this.environmentMainService.args['disable-telemetry'] && !!product.enableTelemetry) { const channel = getDelayedChannel(sharedProcessReady.then(client => client.getChannel('telemetryAppender'))); const appender = new TelemetryAppenderClient(channel); - const commonProperties = resolveCommonProperties(this.fileService, release(), process.arch, product.commit, product.version, machineId, product.msftInternalDomains, this.environmentService.installSourcePath); - const piiPaths = [this.environmentService.appRoot, this.environmentService.extensionsPath]; + const commonProperties = resolveCommonProperties(this.fileService, release(), process.arch, product.commit, product.version, machineId, product.msftInternalDomains, this.environmentMainService.installSourcePath); + const piiPaths = [this.environmentMainService.appRoot, this.environmentMainService.extensionsPath]; const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths, sendErrorTelemetry: true }; services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config])); @@ -666,7 +664,7 @@ export class CodeApplication extends Disposable { mainProcessElectronServer.registerChannel('webview', webviewChannel); // Storage (main & shared process) - const storageChannel = this._register(new GlobalStorageDatabaseChannel(this.logService, accessor.get(IStorageMainService))); + const storageChannel = this._register(new StorageDatabaseChannel(this.logService, accessor.get(IStorageMainService))); mainProcessElectronServer.registerChannel('storage', storageChannel); sharedProcessClient.then(client => client.registerChannel('storage', storageChannel)); @@ -699,7 +697,7 @@ export class CodeApplication extends Disposable { const pendingWindowOpenablesFromProtocolLinks: IWindowOpenable[] = []; const pendingProtocolLinksToHandle = [ // Windows/Linux: protocol handler invokes CLI with --open-url - ...this.environmentService.args['open-url'] ? this.environmentService.args._urls || [] : [], + ...this.environmentMainService.args['open-url'] ? this.environmentMainService.args._urls || [] : [], // macOS: open-url events ...((global).getOpenUrls() || []) as string[] @@ -736,7 +734,7 @@ export class CodeApplication extends Disposable { // or open new windows. The URL handler will be invoked from // protocol invocations outside of VSCode. const app = this; - const environmentService = this.environmentService; + const environmentService = this.environmentMainService; urlService.registerHandler({ async handleURL(uri: URI, options?: IOpenURLOptions): Promise { @@ -791,10 +789,10 @@ export class CodeApplication extends Disposable { urlService.registerHandler(new URLHandlerChannelClient(urlHandlerChannel)); // Watch Electron URLs and forward them to the UrlService - this._register(new ElectronURLListener(pendingProtocolLinksToHandle, urlService, windowsMainService, this.environmentService)); + this._register(new ElectronURLListener(pendingProtocolLinksToHandle, urlService, windowsMainService, this.environmentMainService)); // Open our first window - const args = this.environmentService.args; + const args = this.environmentMainService.args; const macOpenFiles: string[] = (global).macOpenFiles; const context = isLaunchedFromCli(process.env) ? OpenContext.CLI : OpenContext.DESKTOP; const hasCliArgs = args._.length; @@ -864,7 +862,7 @@ export class CodeApplication extends Disposable { mnemonicButtonLabel(localize({ key: 'cancel', comment: ['&& denotes a mnemonic'] }, "&&No")), ], cancelId: 1, - message: localize('confirmOpenMessage', "An external application wants to open '{0}' in {1}. Do you want to open this file or folder?", getPathLabel(uri.fsPath, this.environmentService), product.nameShort), + message: localize('confirmOpenMessage', "An external application wants to open '{0}' in {1}. Do you want to open this file or folder?", getPathLabel(uri.fsPath, this.environmentMainService), product.nameShort), detail: localize('confirmOpenDetail', "If you did not initiate this request, it may represent an attempted attack on your system. Unless you took an explicit action to initiate this request, you should press 'No'"), noLink: true }); @@ -928,11 +926,28 @@ export class CodeApplication extends Disposable { return { fileUri: URI.file(path) }; } - private async afterWindowOpen(accessor: ServicesAccessor): Promise { + private async afterWindowOpen(accessor: ServicesAccessor, sharedProcess: SharedProcess): Promise { // Signal phase: after window open this.lifecycleMainService.phase = LifecycleMainPhase.AfterWindowOpen; + // Observe shared process for errors + const telemetryService = accessor.get(ITelemetryService); + this._register(sharedProcess.onDidError(e => { + + // Logging + onUnexpectedError(new Error(e.message)); + + // Telemetry + type SharedProcessErrorClassification = { + type: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + }; + type SharedProcessErrorEvent = { + type: WindowError; + }; + telemetryService.publicLog2('sharedprocesserror', { type: e.type }); + })); + // Windows: install mutex const win32MutexName = product.win32MutexName; if (isWindows && win32MutexName) { @@ -960,13 +975,13 @@ export class CodeApplication extends Disposable { } // Start to fetch shell environment (if needed) after window has opened - resolveShellEnv(this.logService, this.environmentService.args, process.env); + resolveShellEnv(this.logService, this.environmentMainService.args, process.env); // If enable-crash-reporter argv is undefined then this is a fresh start, // based on telemetry.enableCrashreporter settings, generate a UUID which // will be used as crash reporter id and also update the json file. try { - const argvContent = await this.fileService.readFile(this.environmentService.argvResource); + const argvContent = await this.fileService.readFile(this.environmentMainService.argvResource); const argvString = argvContent.value.toString(); const argvJSON = JSON.parse(stripComments(argvString)); if (argvJSON['enable-crash-reporter'] === undefined) { @@ -984,7 +999,7 @@ export class CodeApplication extends Disposable { ]; const newArgvString = argvString.substring(0, argvString.length - 2).concat(',\n', additionalArgvContent.join('\n')); - await this.fileService.writeFile(this.environmentService.argvResource, VSBuffer.fromString(newArgvString)); + await this.fileService.writeFile(this.environmentMainService.argvResource, VSBuffer.fromString(newArgvString)); } } catch (error) { this.logService.error(error); @@ -1004,7 +1019,7 @@ export class CodeApplication extends Disposable { recordingStopped = true; // only once - const path = await contentTracing.stopRecording(joinPath(this.environmentService.userHome, `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`).fsPath); + const path = await contentTracing.stopRecording(joinPath(this.environmentMainService.userHome, `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`).fsPath); if (!timeout) { dialogMainService.showMessageBox({ diff --git a/src/vs/code/electron-main/auth.ts b/src/vs/code/electron-main/auth.ts index b9669f983c7..70df692cf81 100644 --- a/src/vs/code/electron-main/auth.ts +++ b/src/vs/code/electron-main/auth.ts @@ -201,7 +201,7 @@ export class ProxyAuthHandler extends Disposable { const proxyAuthResponseHandler = async (event: ElectronEvent, channel: string, reply: Credentials & { remember: boolean } | undefined /* canceled */) => { if (channel === payload.replyChannel) { this.logService.trace(`auth#doResolveProxyCredentials - exit - received credentials from window ${window.id}`); - window.win.webContents.off('ipc-message', proxyAuthResponseHandler); + window.win?.webContents.off('ipc-message', proxyAuthResponseHandler); // We got credentials from the window if (reply) { @@ -229,7 +229,7 @@ export class ProxyAuthHandler extends Disposable { } }; - window.win.webContents.on('ipc-message', proxyAuthResponseHandler); + window.win?.webContents.on('ipc-message', proxyAuthResponseHandler); }); // Remember credentials for the session in case diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 0ccc3092bd4..7cbdd078f09 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -181,9 +181,9 @@ class CodeMain { return [new InstantiationService(services, true), instanceEnvironment, environmentService, configurationService, stateService, bufferLogService]; } - private patchEnvironment(environmentService: IEnvironmentMainService): IProcessEnvironment { + private patchEnvironment(environmentMainService: IEnvironmentMainService): IProcessEnvironment { const instanceEnvironment: IProcessEnvironment = { - VSCODE_IPC_HOOK: environmentService.mainIPCHandle + VSCODE_IPC_HOOK: environmentMainService.mainIPCHandle }; ['VSCODE_NLS_CONFIG', 'VSCODE_PORTABLE'].forEach(key => { @@ -198,16 +198,16 @@ class CodeMain { return instanceEnvironment; } - private initServices(environmentService: IEnvironmentMainService, configurationService: ConfigurationService, stateService: StateService): Promise { + private initServices(environmentMainService: IEnvironmentMainService, configurationService: ConfigurationService, stateService: StateService): Promise { // Environment service (paths) const environmentServiceInitialization = Promise.all([ - environmentService.extensionsPath, - environmentService.nodeCachedDataDir, - environmentService.logsPath, - environmentService.globalStorageHome.fsPath, - environmentService.workspaceStorageHome.fsPath, - environmentService.backupHome + environmentMainService.extensionsPath, + environmentMainService.nodeCachedDataDir, + environmentMainService.logsPath, + environmentMainService.globalStorageHome.fsPath, + environmentMainService.workspaceStorageHome.fsPath, + environmentMainService.backupHome ].map(path => path ? promises.mkdir(path, { recursive: true }) : undefined)); // Configuration service @@ -219,14 +219,14 @@ class CodeMain { return Promise.all([environmentServiceInitialization, configurationServiceInitialization, stateServiceInitialization]); } - private async doStartup(args: NativeParsedArgs, logService: ILogService, environmentService: IEnvironmentMainService, lifecycleMainService: ILifecycleMainService, instantiationService: IInstantiationService, retry: boolean): Promise { + private async doStartup(args: NativeParsedArgs, logService: ILogService, environmentMainService: IEnvironmentMainService, lifecycleMainService: ILifecycleMainService, instantiationService: IInstantiationService, retry: boolean): Promise { // Try to setup a server for running. If that succeeds it means // we are the first instance to startup. Otherwise it is likely // that another instance is already running. let mainProcessNodeIpcServer: NodeIPCServer; try { - mainProcessNodeIpcServer = await nodeIPCServe(environmentService.mainIPCHandle); + mainProcessNodeIpcServer = await nodeIPCServe(environmentMainService.mainIPCHandle); once(lifecycleMainService.onWillShutdown)(() => mainProcessNodeIpcServer.dispose()); } catch (error) { @@ -235,7 +235,7 @@ class CodeMain { if (error.code !== 'EADDRINUSE') { // Show a dialog for errors that can be resolved by the user - this.handleStartupDataDirError(environmentService, error); + this.handleStartupDataDirError(environmentMainService, error); // Any other runtime error is just printed to the console throw error; @@ -244,7 +244,7 @@ class CodeMain { // there's a running instance, let's connect to it let client: NodeIPCClient; try { - client = await nodeIPCConnect(environmentService.mainIPCHandle, 'main'); + client = await nodeIPCConnect(environmentMainService.mainIPCHandle, 'main'); } catch (error) { // Handle unexpected connection errors by showing a dialog to the user @@ -263,18 +263,18 @@ class CodeMain { // let's delete it, since we can't connect to it and then // retry the whole thing try { - unlinkSync(environmentService.mainIPCHandle); + unlinkSync(environmentMainService.mainIPCHandle); } catch (error) { logService.warn('Could not delete obsolete instance handle', error); throw error; } - return this.doStartup(args, logService, environmentService, lifecycleMainService, instantiationService, false); + return this.doStartup(args, logService, environmentMainService, lifecycleMainService, instantiationService, false); } // Tests from CLI require to be the only instance currently - if (environmentService.extensionTestsLocationURI && !environmentService.debugExtensionHost.break) { + if (environmentMainService.extensionTestsLocationURI && !environmentMainService.debugExtensionHost.break) { const msg = 'Running extension tests from the command line is currently only supported if no other instance of Code is running.'; logService.error(msg); client.dispose(); @@ -344,9 +344,9 @@ class CodeMain { return mainProcessNodeIpcServer; } - private handleStartupDataDirError(environmentService: IEnvironmentMainService, error: NodeJS.ErrnoException): void { + private handleStartupDataDirError(environmentMainService: IEnvironmentMainService, error: NodeJS.ErrnoException): void { if (error.code === 'EACCES' || error.code === 'EPERM') { - const directories = coalesce([environmentService.userDataPath, environmentService.extensionsPath, XDG_RUNTIME_DIR]).map(folder => getPathLabel(folder, environmentService)); + const directories = coalesce([environmentMainService.userDataPath, environmentMainService.extensionsPath, XDG_RUNTIME_DIR]).map(folder => getPathLabel(folder, environmentMainService)); this.showStartupWarningDialog( localize('startupDataDirError', "Unable to write program user data."), @@ -369,9 +369,9 @@ class CodeMain { }); } - private async windowsAllowSetForegroundWindow(launchService: ILaunchMainService, logService: ILogService): Promise { + private async windowsAllowSetForegroundWindow(launchMainService: ILaunchMainService, logService: ILogService): Promise { if (isWindows) { - const processId = await launchService.getMainProcessId(); + const processId = await launchMainService.getMainProcessId(); logService.trace('Sending some foreground love to the running instance:', processId); diff --git a/src/vs/code/electron-main/protocol.ts b/src/vs/code/electron-main/protocol.ts index 20644f2c9b9..3e535155c76 100644 --- a/src/vs/code/electron-main/protocol.ts +++ b/src/vs/code/electron-main/protocol.ts @@ -48,10 +48,10 @@ export class FileProtocolHandler extends Disposable { } injectWindowsMainService(windowsMainService: IWindowsMainService): void { - this._register(windowsMainService.onWindowReady(window => { + this._register(windowsMainService.onDidSignalReadyWindow(window => { if (window.config?.extensionDevelopmentPath || window.config?.extensionTestsPath) { const disposables = new DisposableStore(); - disposables.add(Event.any(window.onClose, window.onDestroy)(() => disposables.dispose())); + disposables.add(Event.any(window.onDidClose, window.onDidDestroy)(() => disposables.dispose())); // Allow access to extension development path if (window.config.extensionDevelopmentPath) { diff --git a/src/vs/code/electron-sandbox/issue/issueReporter.js b/src/vs/code/electron-sandbox/issue/issueReporter.js index 2c3529754b3..a51159e580e 100644 --- a/src/vs/code/electron-sandbox/issue/issueReporter.js +++ b/src/vs/code/electron-sandbox/issue/issueReporter.js @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ //@ts-check -'use strict'; - (function () { + 'use strict'; + const bootstrapWindow = bootstrapWindowLib(); // Load issue reporter into window diff --git a/src/vs/code/electron-sandbox/processExplorer/processExplorer.js b/src/vs/code/electron-sandbox/processExplorer/processExplorer.js index 3b84f3acf06..36fb6ee5530 100644 --- a/src/vs/code/electron-sandbox/processExplorer/processExplorer.js +++ b/src/vs/code/electron-sandbox/processExplorer/processExplorer.js @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ //@ts-check -'use strict'; - (function () { + 'use strict'; + const bootstrapWindow = bootstrapWindowLib(); // Load process explorer into window diff --git a/src/vs/code/electron-sandbox/workbench/workbench.js b/src/vs/code/electron-sandbox/workbench/workbench.js index c5cb1debb26..1946d7f5e3b 100644 --- a/src/vs/code/electron-sandbox/workbench/workbench.js +++ b/src/vs/code/electron-sandbox/workbench/workbench.js @@ -6,9 +6,9 @@ /// //@ts-check -'use strict'; - (function () { + 'use strict'; + const bootstrapWindow = bootstrapWindowLib(); // Add a perf entry right from the top @@ -76,5 +76,4 @@ } //#endregion - }()); diff --git a/src/vs/editor/browser/core/keybindingCancellation.ts b/src/vs/editor/browser/core/keybindingCancellation.ts index 1f260f9b6cd..206fbeac885 100644 --- a/src/vs/editor/browser/core/keybindingCancellation.ts +++ b/src/vs/editor/browser/core/keybindingCancellation.ts @@ -12,6 +12,7 @@ import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cance import { LinkedList } from 'vs/base/common/linkedList'; import { createDecorator, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { localize } from 'vs/nls'; const IEditorCancellationTokens = createDecorator('IEditorCancelService'); @@ -22,7 +23,7 @@ interface IEditorCancellationTokens { cancel(editor: ICodeEditor): void; } -const ctxCancellableOperation = new RawContextKey('cancellableOperation', false); +const ctxCancellableOperation = new RawContextKey('cancellableOperation', false, localize('cancellableOperation', 'Whether the editor runs a cancellable operation, e.g. like \'Peek References\'')); registerSingleton(IEditorCancellationTokens, class implements IEditorCancellationTokens { diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index c67cb67e60c..6256e77b6ec 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -1151,6 +1151,9 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } else { result.wordWrapOverride1 = this._diffWordWrap; } + if (options.originalAriaLabel) { + result.ariaLabel = options.originalAriaLabel; + } result.readOnly = !this._originalIsEditable; result.extraEditorClassName = 'original-in-monaco-diff-editor'; return { @@ -1164,6 +1167,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE private _adjustOptionsForRightHandSide(options: Readonly): editorBrowser.IEditorConstructionOptions { const result = this._adjustOptionsForSubEditor(options); + if (options.modifiedAriaLabel) { + result.ariaLabel = options.modifiedAriaLabel; + } + result.wordWrapOverride1 = this._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 bce85536162..fd183e4fd8c 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -690,6 +690,14 @@ export interface IDiffEditorOptions extends IEditorOptions { * Control the wrapping of the diff editor. */ diffWordWrap?: 'off' | 'on' | 'inherit'; + /** + * Aria label for original editor. + */ + originalAriaLabel?: string; + /** + * Aria label for modifed editor. + */ + modifiedAriaLabel?: string; } //#endregion diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index f4b946213dd..3731b7dee38 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -266,7 +266,7 @@ export interface HoverProvider { } /** - * An evaluatable expression represents additional information for an expression in a document. Evaluatable expression are + * An evaluatable expression represents additional information for an expression in a document. Evaluatable expressions are * evaluated by a debugger or runtime and their result is rendered in a tooltip-like widget. * @internal */ @@ -275,15 +275,16 @@ export interface EvaluatableExpression { * The range to which this expression applies. */ range: IRange; - /* + /** * This expression overrides the expression extracted from the range. */ expression?: string; } + /** - * The hover provider interface defines the contract between extensions and - * the [hover](https://code.visualstudio.com/docs/editor/intellisense)-feature. + * The evaluatable expression provider interface defines the contract between extensions and + * the debug hover. * @internal */ export interface EvaluatableExpressionProvider { @@ -295,6 +296,73 @@ export interface EvaluatableExpressionProvider { provideEvaluatableExpression(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; } +/** + * An open ended information bag passed to the inline value provider. + * A minimal context containes just the document location where the debugger has stopped. + * @internal + */ +export interface InlineValueContext { + stoppedLocation: Range; +} + +/** + * Provide inline value as text. + * @internal + */ +export interface InlineValueText { + type: 'text'; + text: string; + range: IRange; +} + +/** + * Provide inline value through a variable lookup. + * @internal + */ +export interface InlineValueVariableLookup { + type: 'variable'; + variableName: string; + caseSensitiveLookup: boolean; + range: IRange; +} + +/** + * Provide inline value through an expression evaluation. + * @internal + */ +export interface InlineValueExpression { + type: 'expression'; + expression: string; + range: IRange; +} + +/** + * Inline value information can be provided by different means: + * - directly as a text value (class InlineValueText). + * - as a name to use for a variable lookup (class InlineValueVariableLookup) + * - as an evaluatable expression (class InlineValueEvaluatableExpression) + * The InlineValue types combines all inline value types into one type. + * @internal + */ +export type InlineValue = InlineValueText | InlineValueVariableLookup | InlineValueExpression; + +/** + * The inline values provider interface defines the contract between extensions and + * the debugger's inline values feature. + * @internal + */ +export interface InlineValuesProvider { + /** + */ + onDidChangeInlineValues?: Event | undefined; + /** + * Provide the "inline values" for the given range and document. Multiple hovers at the same + * position will be merged by the editor. A hover can have a range which defaults + * to the word range at the position when omitted. + */ + provideInlineValues(model: model.ITextModel, viewPort: Range, context: InlineValueContext, token: CancellationToken): ProviderResult; +} + export const enum CompletionItemKind { Method, Function, @@ -1745,6 +1813,11 @@ export const HoverProviderRegistry = new LanguageFeatureRegistry( */ export const EvaluatableExpressionProviderRegistry = new LanguageFeatureRegistry(); +/** + * @internal + */ +export const InlineValuesProviderRegistry = new LanguageFeatureRegistry(); + /** * @internal */ diff --git a/src/vs/editor/common/modes/languageFeatureRegistry.ts b/src/vs/editor/common/modes/languageFeatureRegistry.ts index b5cd1ca4a75..7dec322f53f 100644 --- a/src/vs/editor/common/modes/languageFeatureRegistry.ts +++ b/src/vs/editor/common/modes/languageFeatureRegistry.ts @@ -9,7 +9,7 @@ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { LRUCache } from 'vs/base/common/map'; import { MovingAverage } from 'vs/base/common/numbers'; import { ITextModel } from 'vs/editor/common/model'; -import { LanguageSelector, score } from 'vs/editor/common/modes/languageSelector'; +import { LanguageFilter, LanguageSelector, score } from 'vs/editor/common/modes/languageSelector'; import { shouldSynchronizeModel } from 'vs/editor/common/services/modelService'; interface Entry { @@ -25,7 +25,7 @@ function isExclusive(selector: LanguageSelector): boolean { } else if (Array.isArray(selector)) { return selector.every(isExclusive); } else { - return !!selector.exclusive; + return !!(selector as LanguageFilter).exclusive; // TODO: microsoft/TypeScript#42768 } } diff --git a/src/vs/editor/common/modes/languageSelector.ts b/src/vs/editor/common/modes/languageSelector.ts index eff5d35ab30..02826877fdc 100644 --- a/src/vs/editor/common/modes/languageSelector.ts +++ b/src/vs/editor/common/modes/languageSelector.ts @@ -55,7 +55,7 @@ export function score(selector: LanguageSelector | undefined, candidateUri: URI, } else if (selector) { // filter -> select accordingly, use defaults for scheme - const { language, pattern, scheme, hasAccessToAllModels } = selector; + const { language, pattern, scheme, hasAccessToAllModels } = selector as LanguageFilter; // TODO: microsoft/TypeScript#42768 if (!candidateIsSynchronized && !hasAccessToAllModels) { return 0; diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index ee10d9b8003..7b401019e06 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -118,6 +118,10 @@ class ModesContentComputer implements IHoverComputer { const maxColumn = model.getLineMaxColumn(lineNumber); const lineDecorations = this._editor.getLineDecorations(lineNumber).filter((d) => { + if (d.options.isWholeLine) { + return true; + } + const startColumn = (d.range.startLineNumber === lineNumber) ? d.range.startColumn : 1; const endColumn = (d.range.endLineNumber === lineNumber) ? d.range.endColumn : maxColumn; if (startColumn > hoverRange.startColumn || hoverRange.endColumn > endColumn) { diff --git a/src/vs/editor/contrib/message/messageController.ts b/src/vs/editor/contrib/message/messageController.ts index 319f0d521d7..d6f3357d3cd 100644 --- a/src/vs/editor/contrib/message/messageController.ts +++ b/src/vs/editor/contrib/message/messageController.ts @@ -24,7 +24,7 @@ export class MessageController implements IEditorContribution { public static readonly ID = 'editor.contrib.messageController'; - static readonly MESSAGE_VISIBLE = new RawContextKey('messageVisible', false); + static readonly MESSAGE_VISIBLE = new RawContextKey('messageVisible', false, nls.localize('messageVisible', 'Whether the editor is currently showing an inline message')); static get(editor: ICodeEditor): MessageController { return editor.getContribution(MessageController.ID); diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 884207922f2..94135e09b63 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -21,7 +21,7 @@ import { Context as SuggestContext, CompletionItem } from './suggest'; import { CompletionModel } from './completionModel'; import { attachListStyler } from 'vs/platform/theme/common/styler'; import { IThemeService, IColorTheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { registerColor, editorWidgetBackground, listFocusBackground, activeContrastBorder, listHighlightForeground, editorForeground, editorWidgetBorder, focusBorder, textLinkForeground, textCodeBlockBackground } from 'vs/platform/theme/common/colorRegistry'; +import { registerColor, editorWidgetBackground, quickInputListFocusBackground, activeContrastBorder, listHighlightForeground, editorForeground, editorWidgetBorder, focusBorder, textLinkForeground, textCodeBlockBackground } from 'vs/platform/theme/common/colorRegistry'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { TimeoutTimer, CancelablePromise, createCancelablePromise, disposableTimeout } from 'vs/base/common/async'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -40,7 +40,7 @@ import { clamp } from 'vs/base/common/numbers'; export const editorSuggestWidgetBackground = registerColor('editorSuggestWidget.background', { dark: editorWidgetBackground, light: editorWidgetBackground, hc: editorWidgetBackground }, nls.localize('editorSuggestWidgetBackground', 'Background color of the suggest widget.')); export const editorSuggestWidgetBorder = registerColor('editorSuggestWidget.border', { dark: editorWidgetBorder, light: editorWidgetBorder, hc: editorWidgetBorder }, nls.localize('editorSuggestWidgetBorder', 'Border color of the suggest widget.')); export const editorSuggestWidgetForeground = registerColor('editorSuggestWidget.foreground', { dark: editorForeground, light: editorForeground, hc: editorForeground }, nls.localize('editorSuggestWidgetForeground', 'Foreground color of the suggest widget.')); -export const editorSuggestWidgetSelectedBackground = registerColor('editorSuggestWidget.selectedBackground', { dark: listFocusBackground, light: listFocusBackground, hc: listFocusBackground }, nls.localize('editorSuggestWidgetSelectedBackground', 'Background color of the selected entry in the suggest widget.')); +export const editorSuggestWidgetSelectedBackground = registerColor('editorSuggestWidget.selectedBackground', { dark: quickInputListFocusBackground, light: quickInputListFocusBackground, hc: quickInputListFocusBackground }, nls.localize('editorSuggestWidgetSelectedBackground', 'Background color of the selected entry in the suggest widget.')); export const editorSuggestWidgetHighlightForeground = registerColor('editorSuggestWidget.highlightForeground', { dark: listHighlightForeground, light: listHighlightForeground, hc: listHighlightForeground }, nls.localize('editorSuggestWidgetHighlightForeground', 'Color of the match highlights in the suggest widget.')); const enum State { diff --git a/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts b/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts index 37055a85416..a4286aa63af 100644 --- a/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts +++ b/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts @@ -52,7 +52,7 @@ class TestGlobalStyleSheet extends GlobalStyleSheet { suite('Decoration Render Options', () => { let options: IDecorationRenderOptions = { - gutterIconPath: URI.parse('https://github.com/microsoft/vscode/blob/master/resources/linux/code.png'), + gutterIconPath: URI.parse('https://github.com/microsoft/vscode/blob/main/resources/linux/code.png'), gutterIconSize: 'contain', backgroundColor: 'red', borderColor: 'yellow' @@ -79,7 +79,7 @@ suite('Decoration Render Options', () => { const s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet); s.registerDecorationType('example', options); const sheet = readStyleSheet(styleSheet); - assert(sheet.indexOf(`{background:url('https://github.com/microsoft/vscode/blob/master/resources/linux/code.png') center center no-repeat;background-size:contain;}`) >= 0); + assert(sheet.indexOf(`{background:url('https://github.com/microsoft/vscode/blob/main/resources/linux/code.png') center center no-repeat;background-size:contain;}`) >= 0); assert(sheet.indexOf(`{background-color:red;border-color:yellow;box-sizing: border-box;}`) >= 0); }); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 5b0421be3f5..7b729f29901 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3247,6 +3247,14 @@ declare namespace monaco.editor { * Control the wrapping of the diff editor. */ diffWordWrap?: 'off' | 'on' | 'inherit'; + /** + * Aria label for original editor. + */ + originalAriaLabel?: string; + /** + * Aria label for modifed editor. + */ + modifiedAriaLabel?: string; } /** @@ -4080,12 +4088,12 @@ declare namespace monaco.editor { accessibilitySupport: IEditorOption; accessibilityPageSize: IEditorOption; ariaLabel: IEditorOption; - autoClosingBrackets: IEditorOption; - autoClosingOvertype: IEditorOption; - autoClosingQuotes: IEditorOption; + autoClosingBrackets: IEditorOption; + autoClosingOvertype: IEditorOption; + autoClosingQuotes: IEditorOption; autoIndent: IEditorOption; automaticLayout: IEditorOption; - autoSurround: IEditorOption; + autoSurround: IEditorOption; stickyTabStops: IEditorOption; codeLens: IEditorOption; codeLensFontFamily: IEditorOption; diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 9de9273c464..1e2d2df6c2f 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -321,34 +321,30 @@ export class ExecuteCommandAction extends Action { export class SubmenuItemAction extends SubmenuAction { - readonly item: ISubmenuItem; - constructor( - item: ISubmenuItem, - menuService: IMenuService, - contextKeyService: IContextKeyService, - options?: IMenuActionOptions + readonly item: ISubmenuItem, + private readonly _menuService: IMenuService, + private readonly _contextKeyService: IContextKeyService, + private readonly _options?: IMenuActionOptions ) { + super(`submenuitem.${item.submenu.id}`, typeof item.title === 'string' ? item.title : item.title.value, [], 'submenu'); + } + + get actions(): readonly IAction[] { const result: IAction[] = []; - const menu = menuService.createMenu(item.submenu, contextKeyService); - const groups = menu.getActions(options); + const menu = this._menuService.createMenu(this.item.submenu, this._contextKeyService); + const groups = menu.getActions(this._options); menu.dispose(); - - for (let group of groups) { - const [, actions] = group; - + for (const [, actions] of groups) { if (actions.length > 0) { result.push(...actions); result.push(new Separator()); } } - if (result.length) { result.pop(); // remove last separator } - - super(`submenuitem.${item.submenu.id}`, typeof item.title === 'string' ? item.title : item.title.value, result, 'submenu'); - this.item = item; + return result; } } diff --git a/src/vs/platform/backup/electron-main/backupMainService.ts b/src/vs/platform/backup/electron-main/backupMainService.ts index ac5e6cd2eb1..ac5bb248927 100644 --- a/src/vs/platform/backup/electron-main/backupMainService.ts +++ b/src/vs/platform/backup/electron-main/backupMainService.ts @@ -38,12 +38,12 @@ export class BackupMainService implements IBackupMainService { private readonly backupPathComparer = { isEqual: (pathA: string, pathB: string) => isEqual(pathA, pathB, !isLinux) }; constructor( - @IEnvironmentMainService environmentService: IEnvironmentMainService, + @IEnvironmentMainService environmentMainService: IEnvironmentMainService, @IConfigurationService private readonly configurationService: IConfigurationService, @ILogService private readonly logService: ILogService ) { - this.backupHome = environmentService.backupHome; - this.workspacesJsonPath = environmentService.backupWorkspacesPath; + this.backupHome = environmentMainService.backupHome; + this.workspacesJsonPath = environmentMainService.backupWorkspacesPath; } async initialize(): Promise { diff --git a/src/vs/platform/contextkey/browser/contextKeyService.ts b/src/vs/platform/contextkey/browser/contextKeyService.ts index 00e2bde4875..e40016315b3 100644 --- a/src/vs/platform/contextkey/browser/contextKeyService.ts +++ b/src/vs/platform/contextkey/browser/contextKeyService.ts @@ -8,6 +8,7 @@ import { Iterable } from 'vs/base/common/iterator'; import { IDisposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { TernarySearchTree } from 'vs/base/common/map'; import { distinct } from 'vs/base/common/objects'; +import { localize } from 'vs/nls'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContext, IContextKey, IContextKeyChangeEvent, IContextKeyService, IContextKeyServiceTarget, IReadableSet, SET_CONTEXT_COMMAND_ID, ContextKeyExpression, RawContextKey, ContextKeyInfo } from 'vs/platform/contextkey/common/contextkey'; @@ -578,6 +579,17 @@ CommandsRegistry.registerCommand(SET_CONTEXT_COMMAND_ID, function (accessor, con accessor.get(IContextKeyService).createKey(String(contextKey), contextValue); }); +CommandsRegistry.registerCommand({ + id: 'getContextKeyInfo', + handler() { + return [...RawContextKey.all()].sort((a, b) => a.key.localeCompare(b.key)); + }, + description: { + description: localize('getContextKeyInfo', "A command that returns information about context keys"), + args: [] + } +}); + CommandsRegistry.registerCommand('_generateContextKeyInfo', function () { const result: ContextKeyInfo[] = []; const seen = new Set(); diff --git a/src/vs/platform/contextkey/common/contextkey.ts b/src/vs/platform/contextkey/common/contextkey.ts index 22405c003f8..af2326a7565 100644 --- a/src/vs/platform/contextkey/common/contextkey.ts +++ b/src/vs/platform/contextkey/common/contextkey.ts @@ -1259,7 +1259,7 @@ export class ContextKeyOrExpr implements IContextKeyExpression { export interface ContextKeyInfo { readonly key: string; - readonly type: string; + readonly type?: string; readonly description?: string; } @@ -1273,12 +1273,16 @@ export class RawContextKey extends ContextKeyDefinedExpr { private readonly _defaultValue: T | undefined; - constructor(readonly key: string, defaultValue: T | undefined, description?: string) { + constructor(readonly key: string, defaultValue: T | undefined, metaOrHide?: string | true | { type: string, description: string }) { super(key); this._defaultValue = defaultValue; // collect all context keys into a central place - RawContextKey._info.push({ key, description, type: typeof defaultValue }); + if (typeof metaOrHide === 'object') { + RawContextKey._info.push({ ...metaOrHide, key }); + } else if (metaOrHide !== true) { + RawContextKey._info.push({ key, description: metaOrHide, type: defaultValue !== null && defaultValue !== undefined ? typeof defaultValue : undefined }); + } } public bindTo(target: IContextKeyService): IContextKey { diff --git a/src/vs/platform/debug/electron-main/extensionHostDebugIpc.ts b/src/vs/platform/debug/electron-main/extensionHostDebugIpc.ts index f0ac255cb2a..9d37b27980d 100644 --- a/src/vs/platform/debug/electron-main/extensionHostDebugIpc.ts +++ b/src/vs/platform/debug/electron-main/extensionHostDebugIpc.ts @@ -43,7 +43,12 @@ export class ElectronExtensionHostDebugBroadcastChannel extends Extens return {}; } - const debug = codeWindow.win.webContents.debugger; + const win = codeWindow.win; + if (!win) { + return {}; + } + + const debug = win.webContents.debugger; let listeners = debug.isAttached() ? Infinity : 0; const server = createServer(listener => { @@ -61,7 +66,7 @@ export class ElectronExtensionHostDebugBroadcastChannel extends Extens const onMessage = (_event: Event, method: string, params: unknown, sessionId?: string) => writeMessage(({ method, params, sessionId })); - codeWindow.win.on('close', () => { + win.on('close', () => { debug.removeListener('message', onMessage); listener.end(); closed = true; @@ -103,7 +108,7 @@ export class ElectronExtensionHostDebugBroadcastChannel extends Extens }); await new Promise(r => server.listen(0, r)); - codeWindow.win.on('close', () => server.close()); + win.on('close', () => server.close()); return { rendererDebugPort: (server.address() as AddressInfo).port }; } diff --git a/src/vs/platform/driver/electron-main/driver.ts b/src/vs/platform/driver/electron-main/driver.ts index 62d7cb313e6..f2f76df80c3 100644 --- a/src/vs/platform/driver/electron-main/driver.ts +++ b/src/vs/platform/driver/electron-main/driver.ts @@ -62,7 +62,7 @@ export class Driver implements IDriver, IWindowDriverRegistry { await this.whenUnfrozen(windowId); const window = this.windowsMainService.getWindowById(windowId); - if (!window) { + if (!window?.win) { throw new Error('Invalid window'); } const webContents = window.win.webContents; @@ -101,7 +101,7 @@ export class Driver implements IDriver, IWindowDriverRegistry { } const window = this.windowsMainService.getWindowById(windowId); - if (!window) { + if (!window?.win) { throw new Error('Invalid window'); } const webContents = window.win.webContents; @@ -207,10 +207,10 @@ export class Driver implements IDriver, IWindowDriverRegistry { export async function serve( windowServer: IPCServer, handle: string, - environmentService: IEnvironmentMainService, + environmentMainService: IEnvironmentMainService, instantiationService: IInstantiationService ): Promise { - const verbose = environmentService.driverVerbose; + const verbose = environmentMainService.driverVerbose; const driver = instantiationService.createInstance(Driver, windowServer, { verbose }); const windowDriverRegistryChannel = new WindowDriverRegistryChannel(driver); diff --git a/src/vs/platform/environment/common/argv.ts b/src/vs/platform/environment/common/argv.ts index 019410e6a30..d36a3dfc49c 100644 --- a/src/vs/platform/environment/common/argv.ts +++ b/src/vs/platform/environment/common/argv.ts @@ -63,6 +63,7 @@ export interface NativeParsedArgs { 'export-default-configuration'?: string; 'install-source'?: string; 'disable-updates'?: boolean; + 'disable-keytar'?: boolean; 'disable-crash-reporter'?: boolean; 'crash-reporter-directory'?: string; 'crash-reporter-id'?: string; diff --git a/src/vs/platform/environment/electron-main/environmentMainService.ts b/src/vs/platform/environment/electron-main/environmentMainService.ts index 35b09d89d39..49acc89b969 100644 --- a/src/vs/platform/environment/electron-main/environmentMainService.ts +++ b/src/vs/platform/environment/electron-main/environmentMainService.ts @@ -36,6 +36,7 @@ export interface IEnvironmentMainService extends INativeEnvironmentService { sandbox: boolean; driverVerbose: boolean; disableUpdates: boolean; + disableKeytar: boolean; } export class EnvironmentMainService extends NativeEnvironmentService implements IEnvironmentMainService { @@ -61,6 +62,9 @@ export class EnvironmentMainService extends NativeEnvironmentService implements @memoize get disableUpdates(): boolean { return !!this._args['disable-updates']; } + @memoize + get disableKeytar(): boolean { return !!this._args['disable-keytar']; } + @memoize get nodeCachedDataDir(): string | undefined { return process.env['VSCODE_NODE_CACHED_DATA_DIR'] || undefined; } } diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 7b6a60b5728..6f532262ae0 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -95,6 +95,7 @@ export const OPTIONS: OptionDescriptions> = { 'skip-release-notes': { type: 'boolean' }, 'disable-telemetry': { type: 'boolean' }, 'disable-updates': { type: 'boolean' }, + 'disable-keytar': { type: 'boolean' }, 'disable-crash-reporter': { type: 'boolean' }, 'crash-reporter-directory': { type: 'string' }, 'crash-reporter-id': { type: 'string' }, diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index f5c79cc4f0d..b9bf96a83e4 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as os from 'os'; +import { homedir, tmpdir } from 'os'; +import product from 'vs/platform/product/common/product'; import { IDebugParams, IExtensionHostDebugParams, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; -import * as paths from 'vs/base/node/paths'; -import * as path from 'vs/base/common/path'; -import * as resources from 'vs/base/common/resources'; +import { getDefaultUserDataPath } from 'vs/base/node/userDataPath'; +import { dirname, join, normalize, resolve } from 'vs/base/common/path'; +import { joinPath } from 'vs/base/common/resources'; import { memoize } from 'vs/base/common/decorators'; -import product from 'vs/platform/product/common/product'; import { toLocalISOString } from 'vs/base/common/date'; import { FileAccess } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; @@ -22,46 +22,46 @@ export class NativeEnvironmentService implements INativeEnvironmentService { get args(): NativeParsedArgs { return this._args; } @memoize - get appRoot(): string { return path.dirname(FileAccess.asFileUri('', require).fsPath); } + get appRoot(): string { return dirname(FileAccess.asFileUri('', require).fsPath); } readonly logsPath: string; @memoize - get userHome(): URI { return URI.file(os.homedir()); } + get userHome(): URI { return URI.file(homedir()); } @memoize get userDataPath(): string { const vscodePortable = process.env['VSCODE_PORTABLE']; if (vscodePortable) { - return path.join(vscodePortable, 'user-data'); + return join(vscodePortable, 'user-data'); } return parseUserDataDir(this._args, process); } @memoize - get appSettingsHome(): URI { return URI.file(path.join(this.userDataPath, 'User')); } + get appSettingsHome(): URI { return URI.file(join(this.userDataPath, 'User')); } @memoize - get tmpDir(): URI { return URI.file(os.tmpdir()); } + get tmpDir(): URI { return URI.file(tmpdir()); } @memoize get userRoamingDataHome(): URI { return this.appSettingsHome; } @memoize - get settingsResource(): URI { return resources.joinPath(this.userRoamingDataHome, 'settings.json'); } + get settingsResource(): URI { return joinPath(this.userRoamingDataHome, 'settings.json'); } @memoize - get userDataSyncHome(): URI { return resources.joinPath(this.userRoamingDataHome, 'sync'); } + get userDataSyncHome(): URI { return joinPath(this.userRoamingDataHome, 'sync'); } @memoize - get userDataSyncLogResource(): URI { return URI.file(path.join(this.logsPath, 'userDataSync.log')); } + get userDataSyncLogResource(): URI { return URI.file(join(this.logsPath, 'userDataSync.log')); } @memoize get sync(): 'on' | 'off' | undefined { return this.args.sync; } @memoize - get machineSettingsResource(): URI { return resources.joinPath(URI.file(path.join(this.userDataPath, 'Machine')), 'settings.json'); } + get machineSettingsResource(): URI { return joinPath(URI.file(join(this.userDataPath, 'Machine')), 'settings.json'); } @memoize get globalStorageHome(): URI { return URI.joinPath(this.appSettingsHome, 'globalStorage'); } @@ -70,32 +70,32 @@ export class NativeEnvironmentService implements INativeEnvironmentService { get workspaceStorageHome(): URI { return URI.joinPath(this.appSettingsHome, 'workspaceStorage'); } @memoize - get keybindingsResource(): URI { return resources.joinPath(this.userRoamingDataHome, 'keybindings.json'); } + get keybindingsResource(): URI { return joinPath(this.userRoamingDataHome, 'keybindings.json'); } @memoize - get keyboardLayoutResource(): URI { return resources.joinPath(this.userRoamingDataHome, 'keyboardLayout.json'); } + get keyboardLayoutResource(): URI { return joinPath(this.userRoamingDataHome, 'keyboardLayout.json'); } @memoize get argvResource(): URI { const vscodePortable = process.env['VSCODE_PORTABLE']; if (vscodePortable) { - return URI.file(path.join(vscodePortable, 'argv.json')); + return URI.file(join(vscodePortable, 'argv.json')); } - return resources.joinPath(this.userHome, product.dataFolderName, 'argv.json'); + return joinPath(this.userHome, product.dataFolderName, 'argv.json'); } @memoize - get snippetsHome(): URI { return resources.joinPath(this.userRoamingDataHome, 'snippets'); } + get snippetsHome(): URI { return joinPath(this.userRoamingDataHome, 'snippets'); } @memoize get isExtensionDevelopment(): boolean { return !!this._args.extensionDevelopmentPath; } @memoize - get untitledWorkspacesHome(): URI { return URI.file(path.join(this.userDataPath, 'Workspaces')); } + get untitledWorkspacesHome(): URI { return URI.file(join(this.userDataPath, 'Workspaces')); } @memoize - get installSourcePath(): string { return path.join(this.userDataPath, 'installSource'); } + get installSourcePath(): string { return join(this.userDataPath, 'installSource'); } @memoize get builtinExtensionsPath(): string { @@ -103,7 +103,7 @@ export class NativeEnvironmentService implements INativeEnvironmentService { if (fromArgs) { return fromArgs; } else { - return path.normalize(path.join(FileAccess.asFileUri('', require).fsPath, '..', 'extensions')); + return normalize(join(FileAccess.asFileUri('', require).fsPath, '..', 'extensions')); } } @@ -112,7 +112,7 @@ export class NativeEnvironmentService implements INativeEnvironmentService { if (fromArgs) { return fromArgs; } else { - return path.join(this.userDataPath, 'CachedExtensionVSIXs'); + return join(this.userDataPath, 'CachedExtensionVSIXs'); } } @@ -131,10 +131,10 @@ export class NativeEnvironmentService implements INativeEnvironmentService { const vscodePortable = process.env['VSCODE_PORTABLE']; if (vscodePortable) { - return path.join(vscodePortable, 'extensions'); + return join(vscodePortable, 'extensions'); } - return resources.joinPath(this.userHome, product.dataFolderName, 'extensions').fsPath; + return joinPath(this.userHome, product.dataFolderName, 'extensions').fsPath; } @memoize @@ -145,7 +145,7 @@ export class NativeEnvironmentService implements INativeEnvironmentService { if (/^[^:/?#]+?:\/\//.test(p)) { return URI.parse(p); } - return URI.file(path.normalize(p)); + return URI.file(normalize(p)); }); } return undefined; @@ -158,7 +158,7 @@ export class NativeEnvironmentService implements INativeEnvironmentService { if (/^[^:/?#]+?:\/\//.test(s)) { return URI.parse(s); } - return URI.file(path.normalize(s)); + return URI.file(normalize(s)); } return undefined; } @@ -188,7 +188,7 @@ export class NativeEnvironmentService implements INativeEnvironmentService { get logLevel(): string | undefined { return this._args.log; } @memoize - get serviceMachineIdResource(): URI { return resources.joinPath(URI.file(this.userDataPath), 'machineid'); } + get serviceMachineIdResource(): URI { return joinPath(URI.file(this.userDataPath), 'machineid'); } get crashReporterId(): string | undefined { return this._args['crash-reporter-id']; } get crashReporterDirectory(): string | undefined { return this._args['crash-reporter-directory']; } @@ -196,13 +196,13 @@ export class NativeEnvironmentService implements INativeEnvironmentService { get driverHandle(): string | undefined { return this._args['driver']; } @memoize - get telemetryLogResource(): URI { return URI.file(path.join(this.logsPath, 'telemetry.log')); } + get telemetryLogResource(): URI { return URI.file(join(this.logsPath, 'telemetry.log')); } get disableTelemetry(): boolean { return !!this._args['disable-telemetry']; } constructor(protected _args: NativeParsedArgs) { if (!_args.logsPath) { const key = toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, ''); - _args.logsPath = path.join(this.userDataPath, 'logs', key); + _args.logsPath = join(this.userDataPath, 'logs', key); } this.logsPath = _args.logsPath; } @@ -231,15 +231,15 @@ export function parsePathArg(arg: string | undefined, process: NodeJS.Process): // Determine if the arg is relative or absolute, if relative use the original CWD // (VSCODE_CWD), not the potentially overridden one (process.cwd()). - const resolved = path.resolve(arg); + const resolved = resolve(arg); - if (path.normalize(arg) === resolved) { + if (normalize(arg) === resolved) { return resolved; } - return path.resolve(process.env['VSCODE_CWD'] || process.cwd(), arg); + return resolve(process.env['VSCODE_CWD'] || process.cwd(), arg); } export function parseUserDataDir(args: NativeParsedArgs, process: NodeJS.Process): string { - return parsePathArg(args['user-data-dir'], process) || path.resolve(paths.getDefaultUserDataPath()); + return parsePathArg(args['user-data-dir'], process) || resolve(getDefaultUserDataPath()); } diff --git a/src/vs/platform/files/common/fileService.ts b/src/vs/platform/files/common/fileService.ts index ab2a5236895..3dfc15e444a 100644 --- a/src/vs/platform/files/common/fileService.ts +++ b/src/vs/platform/files/common/fileService.ts @@ -458,7 +458,7 @@ export class FileService extends Disposable implements IFileService { try { // if the etag is provided, we await the result of the validation - // due to the likelyhood of hitting a NOT_MODIFIED_SINCE result. + // due to the likelihood of hitting a NOT_MODIFIED_SINCE result. // otherwise, we let it run in parallel to the file reading for // optimal startup performance. if (options && typeof options.etag === 'string' && options.etag !== ETAG_DISABLED) { diff --git a/src/vs/platform/issue/electron-main/issueMainService.ts b/src/vs/platform/issue/electron-main/issueMainService.ts index f6efb42c42d..f79bb21926c 100644 --- a/src/vs/platform/issue/electron-main/issueMainService.ts +++ b/src/vs/platform/issue/electron-main/issueMainService.ts @@ -38,7 +38,7 @@ export class IssueMainService implements ICommonIssueService { constructor( private machineId: string, private userEnv: IProcessEnvironment, - @IEnvironmentMainService private readonly environmentService: IEnvironmentMainService, + @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @ILaunchMainService private readonly launchMainService: ILaunchMainService, @ILogService private readonly logService: ILogService, @IDiagnosticsService private readonly diagnosticsService: IDiagnosticsService, @@ -271,7 +271,7 @@ export class IssueMainService implements ICommonIssueService { this._processExplorerWindow.setMenuBarVisibility(false); const windowConfiguration = { - appRoot: this.environmentService.appRoot, + appRoot: this.environmentMainService.appRoot, windowId: this._processExplorerWindow.id, userEnv: this.userEnv, machineId: this.machineId, @@ -397,13 +397,13 @@ export class IssueMainService implements ICommonIssueService { } const windowConfiguration = { - appRoot: this.environmentService.appRoot, + appRoot: this.environmentMainService.appRoot, windowId: this._issueWindow.id, machineId: this.machineId, userEnv: this.userEnv, data, features, - disableExtensions: this.environmentService.disableExtensions, + disableExtensions: this.environmentMainService.disableExtensions, os: { type: os.type(), arch: os.arch(), diff --git a/src/vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService.ts b/src/vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService.ts index f28a15dca5c..f11db7ea10a 100644 --- a/src/vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService.ts +++ b/src/vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService.ts @@ -3,11 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nativeKeymap from 'native-keymap'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IKeyboardLayoutData, IKeyboardLayoutMainService as ICommonKeyboardLayoutMainService } from 'vs/platform/keyboardLayout/common/keyboardLayoutMainService'; import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import * as nativeKeymap from 'native-keymap'; +import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; export const IKeyboardLayoutMainService = createDecorator('keyboardLayoutMainService'); @@ -23,10 +24,17 @@ export class KeyboardLayoutMainService extends Disposable implements ICommonKeyb private _initPromise: Promise | null; private _keyboardLayoutData: IKeyboardLayoutData | null; - constructor() { + constructor( + @ILifecycleMainService lifecycleMainService: ILifecycleMainService + ) { super(); this._initPromise = null; this._keyboardLayoutData = null; + + // perf: automatically trigger initialize after windows + // have opened so that we can do this work in parallel + // to the window load. + lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => this._initialize()); } private _initialize(): Promise { diff --git a/src/vs/platform/launch/electron-main/launchMainService.ts b/src/vs/platform/launch/electron-main/launchMainService.ts index 0ec1d10331c..c613f990407 100644 --- a/src/vs/platform/launch/electron-main/launchMainService.ts +++ b/src/vs/platform/launch/electron-main/launchMainService.ts @@ -21,6 +21,7 @@ import { IMainProcessInfo, IWindowInfo } from 'vs/platform/launch/common/launch' import { isLaunchedFromCli } from 'vs/platform/environment/node/argvHelper'; import { CancellationToken } from 'vs/base/common/cancellation'; import { isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { assertIsDefined } from 'vs/base/common/types'; export const ID = 'launchMainService'; export const ILaunchMainService = createDecorator(ID); @@ -293,8 +294,9 @@ export class LaunchMainService implements ILaunchMainService { private codeWindowToInfo(window: ICodeWindow): IWindowInfo { const folderURIs = this.getFolderURIs(window); + const win = assertIsDefined(window.win); - return this.browserWindowToInfo(window.win, folderURIs, window.remoteAuthority); + return this.browserWindowToInfo(win, folderURIs, window.remoteAuthority); } private browserWindowToInfo(window: BrowserWindow, folderURIs: URI[] = [], remoteAuthority?: string): IWindowInfo { diff --git a/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts index 29a7082b742..82f80545895 100644 --- a/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts +++ b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts @@ -11,9 +11,11 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { ICodeWindow } from 'vs/platform/windows/electron-main/windows'; import { handleVetos } from 'vs/platform/lifecycle/common/lifecycle'; import { isMacintosh, isWindows } from 'vs/base/common/platform'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Promises, Barrier, timeout } from 'vs/base/common/async'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; +import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { assertIsDefined } from 'vs/base/common/types'; export const ILifecycleMainService = createDecorator('lifecycleMainService'); @@ -24,6 +26,11 @@ export const enum UnloadReason { LOAD = 4 } +export interface IWindowLoadEvent { + window: ICodeWindow; + workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined; +} + export interface IWindowUnloadEvent { window: ICodeWindow; reason: UnloadReason; @@ -72,16 +79,27 @@ export interface ILifecycleMainService { readonly onWillShutdown: Event; /** - * An event that fires before a window closes. This event is fired after any veto has been dealt - * with so that listeners know for sure that the window will close without veto. + * An event that fires when a window is loading. This can either be a window opening for the + * first time or a window reloading or changing to another URL. */ - readonly onBeforeWindowClose: Event; + readonly onWillLoadWindow: Event; /** * An event that fires before a window is about to unload. Listeners can veto this event to prevent * the window from unloading. */ - readonly onBeforeWindowUnload: Event; + readonly onBeforeUnloadWindow: Event; + + /** + * An event that fires before a window closes. This event is fired after any veto has been dealt + * with so that listeners know for sure that the window will close without veto. + */ + readonly onBeforeCloseWindow: Event; + + /** + * Make a `ICodeWindow` known to the lifecycle main service. + */ + registerWindow(window: ICodeWindow): void; /** * Reload a window. All lifecycle event handlers are triggered. @@ -147,11 +165,14 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe private readonly _onWillShutdown = this._register(new Emitter()); readonly onWillShutdown = this._onWillShutdown.event; - private readonly _onBeforeWindowClose = this._register(new Emitter()); - readonly onBeforeWindowClose = this._onBeforeWindowClose.event; + private readonly _onWillLoadWindow = this._register(new Emitter()); + readonly onWillLoadWindow = this._onWillLoadWindow.event; - private readonly _onBeforeWindowUnload = this._register(new Emitter()); - readonly onBeforeWindowUnload = this._onBeforeWindowUnload.event; + private readonly _onBeforeCloseWindow = this._register(new Emitter()); + readonly onBeforeCloseWindow = this._onBeforeCloseWindow.event; + + private readonly _onBeforeUnloadWindow = this._register(new Emitter()); + readonly onBeforeUnloadWindow = this._onBeforeUnloadWindow.event; private _quitRequested = false; get quitRequested(): boolean { return this._quitRequested; } @@ -314,12 +335,17 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe } registerWindow(window: ICodeWindow): void { + const windowListeners = new DisposableStore(); // track window count this.windowCounter++; + // Window Will Load + windowListeners.add(window.onWillLoad(e => this._onWillLoadWindow.fire({ window, workspace: e.workspace }))); + // Window Before Closing: Main -> Renderer - window.win.on('close', e => { + const win = assertIsDefined(window.win); + win.on('close', e => { // The window already acknowledged to be closed const windowId = window.id; @@ -341,9 +367,9 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe this.windowToCloseRequest.add(windowId); - // Fire onBeforeWindowClose before actually closing - this.logService.trace(`Lifecycle#onBeforeWindowClose.fire() - window ID ${windowId}`); - this._onBeforeWindowClose.fire(window); + // Fire onBeforeCloseWindow before actually closing + this.logService.trace(`Lifecycle#onBeforeCloseWindow.fire() - window ID ${windowId}`); + this._onBeforeCloseWindow.fire(window); // No veto, close window now window.close(); @@ -351,12 +377,15 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe }); // Window After Closing - window.win.on('closed', () => { + win.on('closed', () => { this.logService.trace(`Lifecycle#window.on('closed') - window ID ${window.id}`); // update window count this.windowCounter--; + // clear window listeners + windowListeners.dispose(); + // if there are no more code windows opened, fire the onWillShutdown event, unless // we are on macOS where it is perfectly fine to close the last window and // the application continues running (unless quit was actually requested) @@ -452,7 +481,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe private onBeforeUnloadWindowInMain(window: ICodeWindow, reason: UnloadReason): Promise { const vetos: (boolean | Promise)[] = []; - this._onBeforeWindowUnload.fire({ + this._onBeforeUnloadWindow.fire({ reason, window, veto(value) { diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 1d515e5bf6a..752fdb0e4e1 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -852,7 +852,6 @@ function workbenchTreeDataPreamble(treeExpandMode) === 'doubleClick') } as TOptions }; @@ -952,9 +951,6 @@ class WorkbenchTreeInternals { const horizontalScrolling = configurationService.getValue(horizontalScrollingKey); newOptions = { ...newOptions, horizontalScrolling }; } - if (e.affectsConfiguration(openModeSettingKey)) { - newOptions = { ...newOptions, expandOnlyOnDoubleClick: configurationService.getValue(openModeSettingKey) === 'doubleClick' }; - } if (e.affectsConfiguration(treeExpandMode) && options.expandOnlyOnTwistieClick === undefined) { newOptions = { ...newOptions, expandOnlyOnTwistieClick: configurationService.getValue<'singleClick' | 'doubleClick'>(treeExpandMode) === 'doubleClick' }; } @@ -1021,7 +1017,7 @@ configurationRegistry.registerConfiguration({ 'description': localize({ key: 'openModeModifier', comment: ['`singleClick` and `doubleClick` refers to a value the setting can take and should not be localized.'] - }, "Controls how to open items in trees and lists using the mouse (if supported). For parents with children in trees, this setting will control if a single click expands the parent or a double click. Note that some trees and lists might choose to ignore this setting if it is not applicable. ") + }, "Controls how to open items in trees and lists using the mouse (if supported). For parents with children in trees, this setting will control if a single click expands the parent or a double click. Note that some trees and lists might choose to ignore this setting if it is not applicable.") }, [horizontalScrollingKey]: { 'type': 'boolean', @@ -1065,8 +1061,8 @@ configurationRegistry.registerConfiguration({ [treeExpandMode]: { type: 'string', enum: ['singleClick', 'doubleClick'], - default: 'singleClick', - description: localize('expand mode', "Controls how tree folders are expanded when clicking the folder names."), + default: 'doubleClick', + description: localize('expand mode', "Controls how tree folders are expanded when clicking the folder names. Note that some trees and lists might choose to ignore this setting if it is not applicable."), } } }); diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts index 32f6b799eef..87c5b80744b 100644 --- a/src/vs/platform/menubar/electron-main/menubar.ts +++ b/src/vs/platform/menubar/electron-main/menubar.ts @@ -67,7 +67,7 @@ export class Menubar { @IUpdateService private readonly updateService: IUpdateService, @IConfigurationService private readonly configurationService: IConfigurationService, @IWindowsMainService private readonly windowsMainService: IWindowsMainService, - @IEnvironmentMainService private readonly environmentService: IEnvironmentMainService, + @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IWorkspacesHistoryMainService private readonly workspacesHistoryMainService: IWorkspacesHistoryMainService, @IStateService private readonly stateService: IStateService, @@ -168,9 +168,9 @@ export class Menubar { this.lifecycleMainService.onWillShutdown(() => this.willShutdown = true); // // Listen to some events from window service to update menu - this.windowsMainService.onWindowsCountChanged(e => this.onWindowsCountChanged(e)); - this.nativeHostMainService.onDidBlurWindow(() => this.onWindowFocusChange()); - this.nativeHostMainService.onDidFocusWindow(() => this.onWindowFocusChange()); + this.windowsMainService.onDidChangeWindowsCount(e => this.onDidChangeWindowsCount(e)); + this.nativeHostMainService.onDidBlurWindow(() => this.onDidChangeWindowFocus()); + this.nativeHostMainService.onDidFocusWindow(() => this.onDidChangeWindowFocus()); } private get currentEnableMenuBarMnemonics(): boolean { @@ -225,7 +225,7 @@ export class Menubar { } } - private onWindowsCountChanged(e: IWindowsCountChangedEvent): void { + private onDidChangeWindowsCount(e: IWindowsCountChangedEvent): void { if (!isMacintosh) { return; } @@ -237,7 +237,7 @@ export class Menubar { } } - private onWindowFocusChange(): void { + private onDidChangeWindowFocus(): void { if (!isMacintosh) { return; } @@ -499,7 +499,7 @@ export class Menubar { const openInNewWindow = this.isOptionClick(event); const success = this.windowsMainService.open({ context: OpenContext.MENU, - cli: this.environmentService.args, + cli: this.environmentMainService.args, urisToOpen: [openable], forceNewWindow: openInNewWindow, gotoLineMode: false @@ -716,7 +716,7 @@ export class Menubar { if (activeWindow) { this.logService.trace('menubar#runActionInRenderer', invocation); - if (isMacintosh && !this.environmentService.isBuilt && !activeWindow.isReady) { + if (isMacintosh && !this.environmentMainService.isBuilt && !activeWindow.isReady) { if ((invocation.type === 'commandId' && invocation.commandId === 'workbench.action.toggleDevTools') || (invocation.type !== 'commandId' && invocation.userSettingsLabel === 'alt+cmd+i')) { // prevent this action from running twice on macOS (https://github.com/microsoft/vscode/issues/62719) // we already register a keybinding in bootstrap-window.js for opening developer tools in case something diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index 406fd62d878..989cf596f6f 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -47,7 +47,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain @IWindowsMainService private readonly windowsMainService: IWindowsMainService, @IDialogMainService private readonly dialogMainService: IDialogMainService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, - @IEnvironmentMainService private readonly environmentService: IEnvironmentMainService, + @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @ITelemetryService private readonly telemetryService: ITelemetryService, @ILogService private readonly logService: ILogService ) { @@ -76,14 +76,14 @@ export class NativeHostMainService extends Disposable implements INativeHostMain //#region Events - readonly onDidOpenWindow = Event.map(this.windowsMainService.onWindowOpened, window => window.id); + readonly onDidOpenWindow = Event.map(this.windowsMainService.onDidOpenWindow, window => window.id); readonly onDidMaximizeWindow = Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-maximize', (event, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)); readonly onDidUnmaximizeWindow = Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-unmaximize', (event, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)); readonly onDidBlurWindow = Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-blur', (event, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)); readonly onDidFocusWindow = Event.any( - Event.map(Event.filter(Event.map(this.windowsMainService.onWindowsCountChanged, () => this.windowsMainService.getLastActiveWindow()), window => !!window), window => window!.id), + Event.map(Event.filter(Event.map(this.windowsMainService.onDidChangeWindowsCount, () => this.windowsMainService.getLastActiveWindow()), window => !!window), window => window!.id), Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-focus', (event, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)) ); @@ -105,7 +105,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain return windows.map(window => ({ id: window.id, workspace: window.openedWorkspace, - title: window.win.getTitle(), + title: window.win?.getTitle() ?? '', filename: window.getRepresentedFilename(), dirty: window.isDocumentEdited() })); @@ -140,7 +140,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain context: OpenContext.API, contextWindowId: windowId, urisToOpen: toOpen, - cli: this.environmentService.args, + cli: this.environmentMainService.args, forceNewWindow: options.forceNewWindow, forceReuseWindow: options.forceReuseWindow, preferNewWindow: options.preferNewWindow, @@ -176,7 +176,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain async isMaximized(windowId: number | undefined): Promise { const window = this.windowById(windowId); - if (window) { + if (window?.win) { return window.win.isMaximized(); } @@ -185,21 +185,21 @@ export class NativeHostMainService extends Disposable implements INativeHostMain async maximizeWindow(windowId: number | undefined): Promise { const window = this.windowById(windowId); - if (window) { + if (window?.win) { window.win.maximize(); } } async unmaximizeWindow(windowId: number | undefined): Promise { const window = this.windowById(windowId); - if (window) { + if (window?.win) { window.win.unmaximize(); } } async minimizeWindow(windowId: number | undefined): Promise { const window = this.windowById(windowId); - if (window) { + if (window?.win) { window.win.minimize(); } } @@ -217,7 +217,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain async setMinimumSize(windowId: number | undefined, width: number | undefined, height: number | undefined): Promise { const window = this.windowById(windowId); - if (window) { + if (window?.win) { const [windowWidth, windowHeight] = window.win.getSize(); const [minWindowWidth, minWindowHeight] = window.win.getMinimumSize(); const [newMinWindowWidth, newMinWindowHeight] = [width ?? minWindowWidth, height ?? minWindowHeight]; @@ -250,7 +250,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain private toBrowserWindow(windowId: number | undefined): BrowserWindow | undefined { const window = this.windowById(windowId); - if (window) { + if (window?.win) { return window.win; } @@ -293,7 +293,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain this.windowsMainService.open({ context: OpenContext.DIALOG, contextWindowId: windowId, - cli: this.environmentService.args, + cli: this.environmentMainService.args, urisToOpen: openable, forceNewWindow: options.forceNewWindow }); @@ -386,7 +386,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain const promptOptions = { name: product.nameLong.replace('-', ''), - icns: (isMacintosh && this.environmentService.isBuilt) ? join(dirname(this.environmentService.appRoot), `${product.nameShort}.icns`) : undefined + icns: (isMacintosh && this.environmentMainService.isBuilt) ? join(dirname(this.environmentMainService.appRoot), `${product.nameShort}.icns`) : undefined }; sudoPrompt.exec(sudoCommand.join(' '), promptOptions, (error: string, stdout: string, stderr: string) => { @@ -412,28 +412,28 @@ export class NativeHostMainService extends Disposable implements INativeHostMain // Windows if (isWindows) { - if (this.environmentService.isBuilt) { + if (this.environmentMainService.isBuilt) { return join(dirname(process.execPath), 'bin', `${product.applicationName}.cmd`); } - return join(this.environmentService.appRoot, 'scripts', 'code-cli.bat'); + return join(this.environmentMainService.appRoot, 'scripts', 'code-cli.bat'); } // Linux if (isLinux) { - if (this.environmentService.isBuilt) { + if (this.environmentMainService.isBuilt) { return join(dirname(process.execPath), 'bin', `${product.applicationName}`); } - return join(this.environmentService.appRoot, 'scripts', 'code-cli.sh'); + return join(this.environmentMainService.appRoot, 'scripts', 'code-cli.sh'); } // macOS - if (this.environmentService.isBuilt) { - return join(this.environmentService.appRoot, 'bin', 'code'); + if (this.environmentMainService.isBuilt) { + return join(this.environmentMainService.appRoot, 'bin', 'code'); } - return join(this.environmentService.appRoot, 'scripts', 'code-cli.sh'); + return join(this.environmentMainService.appRoot, 'scripts', 'code-cli.sh'); } async getOSStatistics(): Promise { @@ -505,7 +505,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain //#region macOS Touchbar async newWindowTab(): Promise { - this.windowsMainService.open({ context: OpenContext.API, cli: this.environmentService.args, forceNewTabbedWindow: true, forceEmpty: true }); + this.windowsMainService.open({ context: OpenContext.API, cli: this.environmentMainService.args, forceNewTabbedWindow: true, forceEmpty: true }); } async showPreviousWindowTab(): Promise { @@ -563,7 +563,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain async closeWindowById(currentWindowId: number | undefined, targetWindowId?: number | undefined): Promise { const window = this.windowById(targetWindowId); - if (window) { + if (window?.win) { return window.win.close(); } } @@ -573,7 +573,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain // If the user selected to exit from an extension development host window, do not quit, but just // close the window unless this is the last window that is opened. const window = this.windowsMainService.getLastActiveWindow(); - if (window?.isExtensionDevelopmentHost && this.windowsMainService.getWindowCount() > 1) { + if (window?.isExtensionDevelopmentHost && this.windowsMainService.getWindowCount() > 1 && window.win) { window.win.close(); } @@ -609,14 +609,14 @@ export class NativeHostMainService extends Disposable implements INativeHostMain async openDevTools(windowId: number | undefined, options?: OpenDevToolsOptions): Promise { const window = this.windowById(windowId); - if (window) { + if (window?.win) { window.win.webContents.openDevTools(options); } } async toggleDevTools(windowId: number | undefined): Promise { const window = this.windowById(windowId); - if (window) { + if (window?.win) { const contents = window.win.webContents; contents.toggleDevTools(); } @@ -624,7 +624,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain async sendInputEvent(windowId: number | undefined, event: MouseInputEvent): Promise { const window = this.windowById(windowId); - if (window && (event.type === 'mouseDown' || event.type === 'mouseUp')) { + if (window?.win && (event.type === 'mouseDown' || event.type === 'mouseUp')) { window.win.webContents.sendInputEvent(event); } } @@ -658,7 +658,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain private static readonly PASSWORD_CHUNK_SIZE = NativeHostMainService.MAX_PASSWORD_LENGTH - 100; async getPassword(windowId: number | undefined, service: string, account: string): Promise { - const keytar = await import('keytar'); + const keytar = await this.withKeytar(); const password = await keytar.getPassword(service, account); if (password) { @@ -686,7 +686,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } async setPassword(windowId: number | undefined, service: string, account: string, password: string): Promise { - const keytar = await import('keytar'); + const keytar = await this.withKeytar(); if (isWindows && password.length > NativeHostMainService.MAX_PASSWORD_LENGTH) { let index = 0; @@ -714,7 +714,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } async deletePassword(windowId: number | undefined, service: string, account: string): Promise { - const keytar = await import('keytar'); + const keytar = await this.withKeytar(); const didDelete = await keytar.deletePassword(service, account); if (didDelete) { @@ -725,17 +725,25 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } async findPassword(windowId: number | undefined, service: string): Promise { - const keytar = await import('keytar'); + const keytar = await this.withKeytar(); return keytar.findPassword(service); } async findCredentials(windowId: number | undefined, service: string): Promise> { - const keytar = await import('keytar'); + const keytar = await this.withKeytar(); return keytar.findCredentials(service); } + private async withKeytar(): Promise { + if (this.environmentMainService.disableKeytar) { + throw new Error('keytar has been disabled via --disable-keytar option'); + } + + return await import('keytar'); + } + //#endregion private windowById(windowId: number | undefined): ICodeWindow | undefined { diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts index 71bb6ae4cd6..2730ee7e8c6 100644 --- a/src/vs/platform/product/common/product.ts +++ b/src/vs/platform/product/common/product.ts @@ -28,7 +28,7 @@ if (isWeb || typeof require === 'undefined' || typeof require.__$__nodeRequire ! urlProtocol: 'code-oss', reportIssueUrl: 'https://github.com/microsoft/vscode/issues/new', licenseName: 'MIT', - licenseUrl: 'https://github.com/microsoft/vscode/blob/master/LICENSE.txt', + licenseUrl: 'https://github.com/microsoft/vscode/blob/main/LICENSE.txt', extensionAllowedProposedApi: [ 'ms-vscode.vscode-js-profile-flame', 'ms-vscode.vscode-js-profile-table', diff --git a/src/vs/platform/product/common/productService.ts b/src/vs/platform/product/common/productService.ts index f810e868ca7..06f3213babb 100644 --- a/src/vs/platform/product/common/productService.ts +++ b/src/vs/platform/product/common/productService.ts @@ -44,7 +44,7 @@ export interface IProductConfiguration { readonly applicationName: string; readonly urlProtocol: string; - readonly dataFolderName: string; + readonly dataFolderName: string; // location for extensions (e.g. ~/.vscode-insiders) readonly builtInExtensions?: IBuiltInExtension[]; diff --git a/src/vs/platform/quickinput/browser/quickInput.ts b/src/vs/platform/quickinput/browser/quickInput.ts index 86f5ddaf348..6c54bf00996 100644 --- a/src/vs/platform/quickinput/browser/quickInput.ts +++ b/src/vs/platform/quickinput/browser/quickInput.ts @@ -7,7 +7,7 @@ import { IQuickInputService, IQuickPickItem, IPickOptions, IInputOptions, IQuick import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IThemeService, Themable } from 'vs/platform/theme/common/themeService'; -import { inputBackground, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoForeground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningForeground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorForeground, inputValidationErrorBorder, badgeBackground, badgeForeground, contrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, progressBarBackground, widgetShadow, listFocusForeground, listFocusBackground, activeContrastBorder, pickerGroupBorder, pickerGroupForeground, quickInputForeground, quickInputBackground, quickInputTitleBackground } from 'vs/platform/theme/common/colorRegistry'; +import { inputBackground, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoForeground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningForeground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorForeground, inputValidationErrorBorder, badgeBackground, badgeForeground, contrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, progressBarBackground, widgetShadow, listFocusForeground, activeContrastBorder, pickerGroupBorder, pickerGroupForeground, quickInputForeground, quickInputBackground, quickInputTitleBackground, quickInputListFocusBackground } from 'vs/platform/theme/common/colorRegistry'; import { CancellationToken } from 'vs/base/common/cancellation'; import { computeStyles } from 'vs/platform/theme/common/styler'; import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; @@ -213,7 +213,7 @@ export class QuickInputService extends Themable implements IQuickInputService { listBackground: quickInputBackground, // Look like focused when inactive. listInactiveFocusForeground: listFocusForeground, - listInactiveFocusBackground: listFocusBackground, + listInactiveFocusBackground: quickInputListFocusBackground, listFocusOutline: activeContrastBorder, listInactiveFocusOutline: activeContrastBorder, pickerGroupBorder, diff --git a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts index c0aad5ffa10..8c40cb0f9c3 100644 --- a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts +++ b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BrowserWindow, ipcMain, Event, MessagePortMain } from 'electron'; +import { BrowserWindow, ipcMain, Event as ElectronEvent, MessagePortMain } from 'electron'; import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService'; import { Barrier } from 'vs/base/common/async'; import { ILogService } from 'vs/platform/log/common/log'; @@ -15,18 +15,23 @@ import { ISharedProcess, ISharedProcessConfiguration } from 'vs/platform/sharedP import { Disposable } from 'vs/base/common/lifecycle'; import { connect as connectMessagePort } from 'vs/base/parts/ipc/electron-main/ipc.mp'; import { assertIsDefined } from 'vs/base/common/types'; +import { Emitter, Event } from 'vs/base/common/event'; +import { WindowError } from 'vs/platform/windows/electron-main/windows'; export class SharedProcess extends Disposable implements ISharedProcess { private readonly whenSpawnedBarrier = new Barrier(); private window: BrowserWindow | undefined = undefined; - private windowCloseListener: ((event: Event) => void) | undefined = undefined; + private windowCloseListener: ((event: ElectronEvent) => void) | undefined = undefined; + + private readonly _onDidError = this._register(new Emitter<{ type: WindowError, message: string }>()); + readonly onDidError = Event.buffer(this._onDidError.event); // buffer until we have a listener! constructor( private readonly machineId: string, private userEnv: NodeJS.ProcessEnv, - @IEnvironmentMainService private readonly environmentService: IEnvironmentMainService, + @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @ILogService private readonly logService: ILogService, @IThemeMainService private readonly themeMainService: IThemeMainService @@ -164,11 +169,11 @@ export class SharedProcess extends Disposable implements ISharedProcess { const config: ISharedProcessConfiguration = { machineId: this.machineId, windowId: this.window.id, - appRoot: this.environmentService.appRoot, - nodeCachedDataDir: this.environmentService.nodeCachedDataDir, - backupWorkspacesPath: this.environmentService.backupWorkspacesPath, + appRoot: this.environmentMainService.appRoot, + nodeCachedDataDir: this.environmentMainService.nodeCachedDataDir, + backupWorkspacesPath: this.environmentMainService.backupWorkspacesPath, userEnv: this.userEnv, - args: this.environmentService.args, + args: this.environmentMainService.args, logLevel: this.logService.getLevel() }; @@ -186,7 +191,7 @@ export class SharedProcess extends Disposable implements ISharedProcess { } // Prevent the window from closing - this.windowCloseListener = (e: Event) => { + this.windowCloseListener = (e: ElectronEvent) => { this.logService.trace('SharedProcess#close prevented'); // We never allow to close the shared process unless we get explicitly disposed() @@ -201,9 +206,11 @@ export class SharedProcess extends Disposable implements ISharedProcess { this.window.on('close', this.windowCloseListener); // Crashes & Unrsponsive & Failed to load - this.window.webContents.on('render-process-gone', (event, details) => this.logService.error(`SharedProcess: crashed (detail: ${details?.reason})`)); - this.window.on('unresponsive', () => this.logService.error('SharedProcess: detected unresponsive window')); - this.window.webContents.on('did-fail-load', (event, errorCode, errorDescription) => this.logService.warn('SharedProcess: failed to load window, ', errorDescription)); + // We use `onUnexpectedError` explicitly because the error handler + // will send the error to the active window to log in devtools too + this.window.webContents.on('render-process-gone', (event, details) => this._onDidError.fire({ type: WindowError.CRASHED, message: `SharedProcess: crashed (detail: ${details?.reason})` })); + this.window.on('unresponsive', () => this._onDidError.fire({ type: WindowError.UNRESPONSIVE, message: 'SharedProcess: detected unresponsive window' })); + this.window.webContents.on('did-fail-load', (event, errorCode, errorDescription) => this._onDidError.fire({ type: WindowError.LOAD, message: `SharedProcess: failed to load window: ${errorDescription}` })); } spawn(userEnv: NodeJS.ProcessEnv): void { diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts index d7c9c986d84..5cfb899602e 100644 --- a/src/vs/platform/storage/browser/storageService.ts +++ b/src/vs/platform/storage/browser/storageService.ts @@ -3,21 +3,22 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import { Emitter } from 'vs/base/common/event'; -import { StorageScope, logStorage, IS_NEW_KEY, AbstractStorageService } from 'vs/platform/storage/common/storage'; +import { StorageScope, IS_NEW_KEY, AbstractStorageService } from 'vs/platform/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces'; import { IFileService, FileChangeType } from 'vs/platform/files/common/files'; import { IStorage, Storage, IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest } from 'vs/base/parts/storage/common/storage'; import { URI } from 'vs/base/common/uri'; import { joinPath } from 'vs/base/common/resources'; -import { runWhenIdle, RunOnceScheduler, Promises } from 'vs/base/common/async'; +import { Promises } from 'vs/base/common/async'; import { VSBuffer } from 'vs/base/common/buffer'; -import { assertIsDefined, assertAllDefined } from 'vs/base/common/types'; export class BrowserStorageService extends AbstractStorageService { + private static BROWSER_DEFAULT_FLUSH_INTERVAL = 5 * 1000; // every 5s because async operations are not permitted on shutdown + private globalStorage: IStorage | undefined; private workspaceStorage: IStorage | undefined; @@ -27,38 +28,26 @@ export class BrowserStorageService extends AbstractStorageService { private globalStorageFile: URI | undefined; private workspaceStorageFile: URI | undefined; - private initializePromise: Promise | undefined; - - private readonly periodicFlushScheduler = this._register(new RunOnceScheduler(() => this.doFlushWhenIdle(), 5000 /* every 5s */)); - private runWhenIdleDisposable: IDisposable | undefined = undefined; - get hasPendingUpdate(): boolean { return (!!this.globalStorageDatabase && this.globalStorageDatabase.hasPendingUpdate) || (!!this.workspaceStorageDatabase && this.workspaceStorageDatabase.hasPendingUpdate); } constructor( + private readonly payload: IWorkspaceInitializationPayload, @IEnvironmentService private readonly environmentService: IEnvironmentService, @IFileService private readonly fileService: IFileService ) { - super(); + super({ flushInterval: BrowserStorageService.BROWSER_DEFAULT_FLUSH_INTERVAL }); } - initialize(payload: IWorkspaceInitializationPayload): Promise { - if (!this.initializePromise) { - this.initializePromise = this.doInitialize(payload); - } - - return this.initializePromise; - } - - private async doInitialize(payload: IWorkspaceInitializationPayload): Promise { + protected async doInitialize(): Promise { // Ensure state folder exists const stateRoot = joinPath(this.environmentService.userRoamingDataHome, 'state'); await this.fileService.createFolder(stateRoot); // Workspace Storage - this.workspaceStorageFile = joinPath(stateRoot, `${payload.id}.json`); + this.workspaceStorageFile = joinPath(stateRoot, `${this.payload.id}.json`); this.workspaceStorageDatabase = this._register(new FileStorageDatabase(this.workspaceStorageFile, false /* do not watch for external changes */, this.fileService)); this.workspaceStorage = this._register(new Storage(this.workspaceStorageDatabase)); @@ -91,91 +80,31 @@ export class BrowserStorageService extends AbstractStorageService { } else if (firstWorkspaceOpen) { this.workspaceStorage.set(IS_NEW_KEY, false); } - - // In the browser we do not have support for long running unload sequences. As such, - // we cannot ask for saving state in that moment, because that would result in a - // long running operation. - // Instead, periodically ask customers to save save. The library will be clever enough - // to only save state that has actually changed. - this.periodicFlushScheduler.schedule(); } - get(key: string, scope: StorageScope, fallbackValue: string): string; - get(key: string, scope: StorageScope): string | undefined; - get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined { - return this.getStorage(scope).get(key, fallbackValue); + protected getStorage(scope: StorageScope): IStorage | undefined { + return scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage; } - getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean; - getBoolean(key: string, scope: StorageScope): boolean | undefined; - getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined { - return this.getStorage(scope).getBoolean(key, fallbackValue); - } - - getNumber(key: string, scope: StorageScope, fallbackValue: number): number; - getNumber(key: string, scope: StorageScope): number | undefined; - getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined { - return this.getStorage(scope).getNumber(key, fallbackValue); - } - - protected doStore(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void { - this.getStorage(scope).set(key, value); - } - - protected doRemove(key: string, scope: StorageScope): void { - this.getStorage(scope).delete(key); - } - - private getStorage(scope: StorageScope): IStorage { - return assertIsDefined(scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage); - } - - async logStorage(): Promise { - const [globalStorage, workspaceStorage, globalStorageFile, workspaceStorageFile] = assertAllDefined(this.globalStorage, this.workspaceStorage, this.globalStorageFile, this.workspaceStorageFile); - - const result = await Promise.all([ - globalStorage.items, - workspaceStorage.items - ]); - - return logStorage(result[0], result[1], globalStorageFile.toString(), workspaceStorageFile.toString()); + protected getLogDetails(scope: StorageScope): string | undefined { + return scope === StorageScope.GLOBAL ? this.globalStorageFile?.toString() : this.workspaceStorageFile?.toString(); } async migrate(toWorkspace: IWorkspaceInitializationPayload): Promise { throw new Error('Migrating storage is currently unsupported in Web'); } - protected async doFlush(): Promise { - await Promises.settled([ - this.getStorage(StorageScope.GLOBAL).whenFlushed(), - this.getStorage(StorageScope.WORKSPACE).whenFlushed() - ]); - } - - private doFlushWhenIdle(): void { - - // Dispose any previous idle runner - dispose(this.runWhenIdleDisposable); - - // Run when idle - this.runWhenIdleDisposable = runWhenIdle(() => { - - // this event will potentially cause new state to be stored - // since new state will only be created while the document - // has focus, one optimization is to not run this when the - // document has no focus, assuming that state has not changed - // - // another optimization is to not collect more state if we - // have a pending update already running which indicates - // that the connection is either slow or disconnected and - // thus unhealthy. - if (document.hasFocus() && !this.hasPendingUpdate) { - this.flush(); - } - - // repeat - this.periodicFlushScheduler.schedule(); - }); + protected shouldFlushWhenIdle(): boolean { + // this flush() will potentially cause new state to be stored + // since new state will only be created while the document + // has focus, one optimization is to not run this when the + // document has no focus, assuming that state has not changed + // + // another optimization is to not collect more state if we + // have a pending update already running which indicates + // that the connection is either slow or disconnected and + // thus unhealthy. + return document.hasFocus() && !this.hasPendingUpdate; } close(): void { @@ -189,13 +118,6 @@ export class BrowserStorageService extends AbstractStorageService { // get triggered in this phase. this.dispose(); } - - dispose(): void { - dispose(this.runWhenIdleDisposable); - this.runWhenIdleDisposable = undefined; - - super.dispose(); - } } export class FileStorageDatabase extends Disposable implements IStorageDatabase { diff --git a/src/vs/platform/storage/common/storage.ts b/src/vs/platform/storage/common/storage.ts index 3d176f5b6d8..faecb2618ea 100644 --- a/src/vs/platform/storage/common/storage.ts +++ b/src/vs/platform/storage/common/storage.ts @@ -5,9 +5,11 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Event, Emitter, PauseableEmitter } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, dispose, MutableDisposable } from 'vs/base/common/lifecycle'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces'; +import { InMemoryStorageDatabase, IStorage, Storage } from 'vs/base/parts/storage/common/storage'; +import { Promises, RunOnceScheduler, runWhenIdle } from 'vs/base/common/async'; export const IS_NEW_KEY = '__$__isNewStorageMarker'; const TARGET_KEY = '__$__targetStorageMarker'; @@ -218,10 +220,16 @@ interface IKeyTargets { [key: string]: StorageTarget } +export interface IStorageServiceOptions { + flushInterval: number; +} + export abstract class AbstractStorageService extends Disposable implements IStorageService { declare readonly _serviceBrand: undefined; + private static DEFAULT_FLUSH_INTERVAL = 60 * 1000; // every minute + private readonly _onDidChangeValue = this._register(new PauseableEmitter()); readonly onDidChangeValue = this._onDidChangeValue.event; @@ -231,6 +239,56 @@ export abstract class AbstractStorageService extends Disposable implements IStor private readonly _onWillSaveState = this._register(new Emitter()); readonly onWillSaveState = this._onWillSaveState.event; + private initializationPromise: Promise | undefined; + + private readonly flushWhenIdleScheduler = this._register(new RunOnceScheduler(() => this.doFlushWhenIdle(), this.options.flushInterval)); + private readonly runFlushWhenIdle = this._register(new MutableDisposable()); + + constructor(private options: IStorageServiceOptions = { flushInterval: AbstractStorageService.DEFAULT_FLUSH_INTERVAL }) { + super(); + } + + private doFlushWhenIdle(): void { + this.runFlushWhenIdle.value = runWhenIdle(() => { + if (this.shouldFlushWhenIdle()) { + this.flush(); + } + + // repeat + this.flushWhenIdleScheduler.schedule(); + }); + } + + protected shouldFlushWhenIdle(): boolean { + return true; + } + + protected stopFlushWhenIdle(): void { + dispose([this.runFlushWhenIdle, this.flushWhenIdleScheduler]); + } + + initialize(): Promise { + if (!this.initializationPromise) { + this.initializationPromise = (async () => { + + // Ask subclasses to initialize storage + await this.doInitialize(); + + // On some OS we do not get enough time to persist state on shutdown (e.g. when + // Windows restarts after applying updates). In other cases, VSCode might crash, + // so we periodically save state to reduce the chance of loosing any state. + // In the browser we do not have support for long running unload sequences. As such, + // we cannot ask for saving state in that moment, because that would result in a + // long running operation. + // Instead, periodically ask customers to save save. The library will be clever enough + // to only save state that has actually changed. + this.flushWhenIdleScheduler.schedule(); + })(); + } + + return this.initializationPromise; + } + protected emitDidChangeValue(scope: StorageScope, key: string): void { // Specially handle `TARGET_KEY` @@ -257,6 +315,24 @@ export abstract class AbstractStorageService extends Disposable implements IStor this._onWillSaveState.fire({ reason }); } + get(key: string, scope: StorageScope, fallbackValue: string): string; + get(key: string, scope: StorageScope): string | undefined; + get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined { + return this.getStorage(scope)?.get(key, fallbackValue); + } + + getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean; + getBoolean(key: string, scope: StorageScope): boolean | undefined; + getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined { + return this.getStorage(scope)?.getBoolean(key, fallbackValue); + } + + getNumber(key: string, scope: StorageScope, fallbackValue: number): number; + getNumber(key: string, scope: StorageScope): number | undefined; + getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined { + return this.getStorage(scope)?.getNumber(key, fallbackValue); + } + store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope, target: StorageTarget): void { // We remove the key for undefined/null values @@ -272,7 +348,7 @@ export abstract class AbstractStorageService extends Disposable implements IStor this.updateKeyTarget(key, scope, target); // Store actual value - this.doStore(key, value, scope); + this.getStorage(scope)?.set(key, value); }); } @@ -285,7 +361,7 @@ export abstract class AbstractStorageService extends Disposable implements IStor this.updateKeyTarget(key, scope, undefined); // Remove actual key - this.doRemove(key, scope); + this.getStorage(scope)?.delete(key); }); } @@ -326,7 +402,7 @@ export abstract class AbstractStorageService extends Disposable implements IStor if (typeof target === 'number') { if (keyTargets[key] !== target) { keyTargets[key] = target; - this.doStore(TARGET_KEY, JSON.stringify(keyTargets), scope); + this.getStorage(scope)?.set(TARGET_KEY, JSON.stringify(keyTargets)); } } @@ -334,7 +410,7 @@ export abstract class AbstractStorageService extends Disposable implements IStor else { if (typeof keyTargets[key] === 'number') { delete keyTargets[key]; - this.doStore(TARGET_KEY, JSON.stringify(keyTargets), scope); + this.getStorage(scope)?.set(TARGET_KEY, JSON.stringify(keyTargets)); } } } @@ -378,118 +454,66 @@ export abstract class AbstractStorageService extends Disposable implements IStor return this.getBoolean(IS_NEW_KEY, scope) === true; } - flush(): Promise { + async flush(): Promise { // Signal event to collect changes this._onWillSaveState.fire({ reason: WillSaveStateReason.NONE }); // Await flush - return this.doFlush(); + await Promises.settled([ + this.getStorage(StorageScope.GLOBAL)?.whenFlushed() ?? Promise.resolve(), + this.getStorage(StorageScope.WORKSPACE)?.whenFlushed() ?? Promise.resolve() + ]); + } + + async logStorage(): Promise { + const globalItems = this.getStorage(StorageScope.GLOBAL)?.items ?? new Map(); + const workspaceItems = this.getStorage(StorageScope.WORKSPACE)?.items ?? new Map(); + + return logStorage( + globalItems, + workspaceItems, + this.getLogDetails(StorageScope.GLOBAL) ?? '', + this.getLogDetails(StorageScope.WORKSPACE) ?? '' + ); } // --- abstract - abstract get(key: string, scope: StorageScope, fallbackValue: string): string; - abstract get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined; + protected abstract doInitialize(): Promise; - abstract getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean; - abstract getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined; + protected abstract getStorage(scope: StorageScope): IStorage | undefined; - abstract getNumber(key: string, scope: StorageScope, fallbackValue: number): number; - abstract getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined; - - protected abstract doStore(key: string, value: string | boolean | number, scope: StorageScope): void; - - protected abstract doRemove(key: string, scope: StorageScope): void; - - protected abstract doFlush(): Promise; + protected abstract getLogDetails(scope: StorageScope): string | undefined; abstract migrate(toWorkspace: IWorkspaceInitializationPayload): Promise; - - abstract logStorage(): void; } export class InMemoryStorageService extends AbstractStorageService { - private readonly globalCache = new Map(); - private readonly workspaceCache = new Map(); + private globalStorage = new Storage(new InMemoryStorageDatabase()); + private workspaceStorage = new Storage(new InMemoryStorageDatabase()); - private getCache(scope: StorageScope): Map { - return scope === StorageScope.GLOBAL ? this.globalCache : this.workspaceCache; + constructor() { + super(); + + this._register(this.workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key))); + this._register(this.globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key))); } - get(key: string, scope: StorageScope, fallbackValue: string): string; - get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined { - const value = this.getCache(scope).get(key); - - if (isUndefinedOrNull(value)) { - return fallbackValue; - } - - return value; + protected getStorage(scope: StorageScope): IStorage { + return scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage; } - getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean; - getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined { - const value = this.getCache(scope).get(key); - - if (isUndefinedOrNull(value)) { - return fallbackValue; - } - - return value === 'true'; + protected getLogDetails(scope: StorageScope): string | undefined { + return scope === StorageScope.GLOBAL ? 'inMemory (global)' : 'inMemory (workspace)'; } - getNumber(key: string, scope: StorageScope, fallbackValue: number): number; - getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined { - const value = this.getCache(scope).get(key); - - if (isUndefinedOrNull(value)) { - return fallbackValue; - } - - return parseInt(value, 10); - } - - protected doStore(key: string, value: string | boolean | number, scope: StorageScope): void { - - // Otherwise, convert to String and store - const valueStr = String(value); - - // Return early if value already set - const currentValue = this.getCache(scope).get(key); - if (currentValue === valueStr) { - return; - } - - // Update in cache - this.getCache(scope).set(key, valueStr); - - // Events - this.emitDidChangeValue(scope, key); - } - - protected doRemove(key: string, scope: StorageScope): void { - const wasDeleted = this.getCache(scope).delete(key); - if (!wasDeleted) { - return; // Return early if value already deleted - } - - // Events - this.emitDidChangeValue(scope, key); - } - - logStorage(): void { - logStorage(this.globalCache, this.workspaceCache, 'inMemory', 'inMemory'); - } + protected async doInitialize(): Promise { } async migrate(toWorkspace: IWorkspaceInitializationPayload): Promise { // not supported } - - async doFlush(): Promise { } - - async close(): Promise { } } export async function logStorage(global: Map, workspace: Map, globalPath: string, workspacePath: string): Promise { diff --git a/src/vs/platform/storage/common/storageIpc.ts b/src/vs/platform/storage/common/storageIpc.ts new file mode 100644 index 00000000000..a95938a3ae5 --- /dev/null +++ b/src/vs/platform/storage/common/storageIpc.ts @@ -0,0 +1,136 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest } from 'vs/base/parts/storage/common/storage'; +import { IEmptyWorkspaceIdentifier, ISerializedSingleFolderWorkspaceIdentifier, ISerializedWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; + +export type Key = string; +export type Value = string; +export type Item = [Key, Value]; + +export interface IBaseSerializableStorageRequest { + readonly workspace: ISerializedWorkspaceIdentifier | ISerializedSingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined +} + +export interface ISerializableUpdateRequest extends IBaseSerializableStorageRequest { + insert?: Item[]; + delete?: Key[]; +} + +export interface ISerializableItemsChangeEvent { + readonly changed?: Item[]; + readonly deleted?: Key[]; +} + +abstract class BaseStorageDatabaseClient extends Disposable implements IStorageDatabase { + + abstract readonly onDidChangeItemsExternal: Event; + + constructor(protected channel: IChannel, protected workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined) { + super(); + } + + async getItems(): Promise> { + const serializableRequest: IBaseSerializableStorageRequest = { workspace: this.workspace }; + const items: Item[] = await this.channel.call('getItems', serializableRequest); + + return new Map(items); + } + + updateItems(request: IUpdateRequest): Promise { + const serializableRequest: ISerializableUpdateRequest = { workspace: this.workspace }; + + if (request.insert) { + serializableRequest.insert = Array.from(request.insert.entries()); + } + + if (request.delete) { + serializableRequest.delete = Array.from(request.delete.values()); + } + + return this.channel.call('updateItems', serializableRequest); + } + + abstract close(): Promise; +} + +class GlobalStorageDatabaseClient extends BaseStorageDatabaseClient implements IStorageDatabase { + + private readonly _onDidChangeItemsExternal = this._register(new Emitter()); + readonly onDidChangeItemsExternal = this._onDidChangeItemsExternal.event; + + constructor(channel: IChannel) { + super(channel, undefined); + + this.registerListeners(); + } + + private registerListeners(): void { + this._register(this.channel.listen('onDidChangeGlobalStorage')((e: ISerializableItemsChangeEvent) => this.onDidChangeGlobalStorage(e))); + } + + private onDidChangeGlobalStorage(e: ISerializableItemsChangeEvent): void { + if (Array.isArray(e.changed) || Array.isArray(e.deleted)) { + this._onDidChangeItemsExternal.fire({ + changed: e.changed ? new Map(e.changed) : undefined, + deleted: e.deleted ? new Set(e.deleted) : undefined + }); + } + } + + async close(): Promise { + + // The global storage database is shared across all instances so + // we do not await it. However we dispose the listener for external + // changes because we no longer interested int it. + this.dispose(); + } +} + +class WorkspaceStorageDatabaseClient extends BaseStorageDatabaseClient implements IStorageDatabase { + + readonly onDidChangeItemsExternal = Event.None; // unsupported for workspace storage because we only ever write from one window + + constructor(channel: IChannel, workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier) { + super(channel, workspace); + } + + async close(): Promise { + const serializableRequest: ISerializableUpdateRequest = { workspace: this.workspace }; + + return this.channel.call('close', serializableRequest); + } +} + +export class StorageDatabaseChannelClient extends Disposable { + + private _globalStorage: GlobalStorageDatabaseClient | undefined = undefined; + get globalStorage() { + if (!this._globalStorage) { + this._globalStorage = new GlobalStorageDatabaseClient(this.channel); + } + + return this._globalStorage; + } + + private _workspaceStorage: WorkspaceStorageDatabaseClient | undefined = undefined; + get workspaceStorage() { + if (!this._workspaceStorage && this.workspace) { + this._workspaceStorage = new WorkspaceStorageDatabaseClient(this.channel, this.workspace); + } + + return this._workspaceStorage; + } + + constructor( + private channel: IChannel, + private workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined + ) { + super(); + } +} diff --git a/src/vs/platform/storage/electron-main/storageIpc.ts b/src/vs/platform/storage/electron-main/storageIpc.ts new file mode 100644 index 00000000000..f8a6224c5c7 --- /dev/null +++ b/src/vs/platform/storage/electron-main/storageIpc.ts @@ -0,0 +1,136 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; +import { ILogService } from 'vs/platform/log/common/log'; +import { ISerializableItemsChangeEvent, ISerializableUpdateRequest, IBaseSerializableStorageRequest, Key, Value } from 'vs/platform/storage/common/storageIpc'; +import { IStorageChangeEvent, IStorageMain } from 'vs/platform/storage/electron-main/storageMain'; +import { IStorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; +import { IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier, reviveIdentifier } from 'vs/platform/workspaces/common/workspaces'; + +export class StorageDatabaseChannel extends Disposable implements IServerChannel { + + private static readonly STORAGE_CHANGE_DEBOUNCE_TIME = 100; + + private readonly _onDidChangeGlobalStorage = this._register(new Emitter()); + private readonly onDidChangeGlobalStorage = this._onDidChangeGlobalStorage.event; + + constructor( + private logService: ILogService, + private storageMainService: IStorageMainService + ) { + super(); + + this.registerGlobalStorageListeners(); + } + + //#region Global Storage Change Events + + private registerGlobalStorageListeners(): void { + + // Listen for changes in global storage to send to listeners + // that are listening. Use a debouncer to reduce IPC traffic. + this._register(Event.debounce(this.storageMainService.globalStorage.onDidChangeStorage, (prev: IStorageChangeEvent[] | undefined, cur: IStorageChangeEvent) => { + if (!prev) { + prev = [cur]; + } else { + prev.push(cur); + } + + return prev; + }, StorageDatabaseChannel.STORAGE_CHANGE_DEBOUNCE_TIME)(events => { + if (events.length) { + this._onDidChangeGlobalStorage.fire(this.serializeGlobalStorageEvents(events)); + } + })); + } + + private serializeGlobalStorageEvents(events: IStorageChangeEvent[]): ISerializableItemsChangeEvent { + const changed = new Map(); + const deleted = new Set(); + events.forEach(event => { + const existing = this.storageMainService.globalStorage.get(event.key); + if (typeof existing === 'string') { + changed.set(event.key, existing); + } else { + deleted.add(event.key); + } + }); + + return { + changed: Array.from(changed.entries()), + deleted: Array.from(deleted.values()) + }; + } + + listen(_: unknown, event: string): Event { + switch (event) { + case 'onDidChangeGlobalStorage': return this.onDidChangeGlobalStorage; + } + + throw new Error(`Event not found: ${event}`); + } + + //#endregion + + async call(_: unknown, command: string, arg: IBaseSerializableStorageRequest): Promise { + const workspace = reviveIdentifier(arg.workspace); + + // Get storage to be ready + const storage = await this.withStorageInitialized(workspace); + + // handle call + switch (command) { + case 'getItems': { + return Array.from(storage.items.entries()); + } + + case 'updateItems': { + const items: ISerializableUpdateRequest = arg; + + if (items.insert) { + for (const [key, value] of items.insert) { + storage.set(key, value); + } + } + + if (items.delete) { + items.delete.forEach(key => storage.delete(key)); + } + + break; + } + + case 'close': { + + // We only allow to close workspace scoped storage because + // global storage is shared across all windows and closes + // only on shutdown. + if (workspace) { + return storage.close(); + } + + break; + } + + default: + throw new Error(`Call not found: ${command}`); + } + } + + private async withStorageInitialized(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined): Promise { + const storage = workspace ? this.storageMainService.workspaceStorage(workspace) : this.storageMainService.globalStorage; + + try { + await storage.init(); + } catch (error) { + this.logService.error(`StorageIPC#init: Unable to init ${workspace ? 'workspace' : 'global'} storage due to ${error}`); + } + + return storage; + } +} diff --git a/src/vs/platform/storage/electron-main/storageMain.ts b/src/vs/platform/storage/electron-main/storageMain.ts new file mode 100644 index 00000000000..c9a430dccef --- /dev/null +++ b/src/vs/platform/storage/electron-main/storageMain.ts @@ -0,0 +1,307 @@ +/*--------------------------------------------------------------------------------------------- + * 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 'fs'; +import { exists, writeFile } from 'vs/base/node/pfs'; +import { Event, Emitter } from 'vs/base/common/event'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { ILogService, LogLevel } from 'vs/platform/log/common/log'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { SQLiteStorageDatabase, ISQLiteStorageDatabaseLoggingOptions } from 'vs/base/parts/storage/node/storage'; +import { Storage, InMemoryStorageDatabase, StorageHint, IStorage } from 'vs/base/parts/storage/common/storage'; +import { join } from 'vs/base/common/path'; +import { IS_NEW_KEY } from 'vs/platform/storage/common/storage'; +import { currentSessionDateStorageKey, firstSessionDateStorageKey, instanceStorageKey, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; +import { generateUuid } from 'vs/base/common/uuid'; +import { IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; + +export interface IStorageMainOptions { + + /** + * If enabled, storage will not persist to disk + * but into memory. + */ + useInMemoryStorage?: boolean; +} + +/** + * Provides access to global and workspace storage from the + * electron-main side that is the owner of all storage connections. + */ +export interface IStorageMain extends IDisposable { + + /** + * Emitted whenever data is updated or deleted. + */ + readonly onDidChangeStorage: Event; + + /** + * Emitted when the storage is closed. + */ + readonly onDidCloseStorage: Event; + + /** + * Access to all cached items of this storage service. + */ + readonly items: Map; + + /** + * Required call to ensure the service can be used. + */ + init(): Promise; + + /** + * Retrieve an element stored with the given key from storage. Use + * the provided defaultValue if the element is null or undefined. + */ + get(key: string, fallbackValue: string): string; + get(key: string, fallbackValue?: string): string | undefined; + + /** + * Store a string value under the given key to storage. The value will + * be converted to a string. + */ + set(key: string, value: string | boolean | number | undefined | null): void; + + /** + * Delete an element stored under the provided key from storage. + */ + delete(key: string): void; + + /** + * Close the storage connection. + */ + close(): Promise; +} + +export interface IStorageChangeEvent { + key: string; +} + +abstract class BaseStorageMain extends Disposable implements IStorageMain { + + protected readonly _onDidChangeStorage = this._register(new Emitter()); + readonly onDidChangeStorage = this._onDidChangeStorage.event; + + private readonly _onDidCloseStorage = this._register(new Emitter()); + readonly onDidCloseStorage = this._onDidCloseStorage.event; + + private storage: IStorage = new Storage(new InMemoryStorageDatabase()); // storage is in-memory until initialized + + private initializePromise: Promise | undefined = undefined; + + constructor( + protected readonly logService: ILogService + ) { + super(); + } + + init(): Promise { + if (!this.initializePromise) { + this.initializePromise = (async () => { + try { + + // Create storage via subclasses + const storage = await this.doCreate(); + + // Replace our in-memory storage with the real + // once as soon as possible without awaiting + // the init call. + this.storage.dispose(); + this.storage = storage; + + // Re-emit storage changes via event + this._register(storage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key }))); + + // Await storage init + await this.doInit(storage); + + // Ensure we track wether storage is new or not + const isNewStorage = storage.getBoolean(IS_NEW_KEY); + if (isNewStorage === undefined) { + storage.set(IS_NEW_KEY, true); + } else if (isNewStorage) { + storage.set(IS_NEW_KEY, false); + } + } catch (error) { + this.logService.error(`StorageMain#initialize(): Unable to init storage due to ${error}`); + } + })(); + } + + return this.initializePromise; + } + + protected createLogginOptions(): ISQLiteStorageDatabaseLoggingOptions { + return { + logTrace: (this.logService.getLevel() === LogLevel.Trace) ? msg => this.logService.trace(msg) : undefined, + logError: error => this.logService.error(error) + }; + } + + protected doInit(storage: IStorage): Promise { + return storage.init(); + } + + protected abstract doCreate(): Promise; + + get items(): Map { return this.storage.items; } + + get(key: string, fallbackValue: string): string; + get(key: string, fallbackValue?: string): string | undefined; + get(key: string, fallbackValue?: string): string | undefined { + return this.storage.get(key, fallbackValue); + } + + set(key: string, value: string | boolean | number | undefined | null): Promise { + return this.storage.set(key, value); + } + + delete(key: string): Promise { + return this.storage.delete(key); + } + + async close(): Promise { + + // Ensure we are not accidentally leaving + // a pending initialized storage behind in + // case close() was called before init() + // finishes + if (this.initializePromise) { + await this.initializePromise; + } + + // Propagate to storage lib + await this.storage.close(); + + // Signal as event + this._onDidCloseStorage.fire(); + } +} + +export class GlobalStorageMain extends BaseStorageMain implements IStorageMain { + + private static readonly STORAGE_NAME = 'state.vscdb'; + + constructor( + private readonly options: IStorageMainOptions, + logService: ILogService, + private readonly environmentService: IEnvironmentService + ) { + super(logService); + } + + protected async doCreate(): Promise { + let storagePath: string; + if (this.options.useInMemoryStorage) { + storagePath = SQLiteStorageDatabase.IN_MEMORY_PATH; + } else { + storagePath = join(this.environmentService.globalStorageHome.fsPath, GlobalStorageMain.STORAGE_NAME); + } + + return new Storage(new SQLiteStorageDatabase(storagePath, { + logging: this.createLogginOptions() + })); + } + + protected async doInit(storage: IStorage): Promise { + await super.doInit(storage); + + // Apply global telemetry values as part of the initialization + this.updateTelemetryState(storage); + } + + private updateTelemetryState(storage: IStorage): void { + + // Instance UUID (once) + const instanceId = storage.get(instanceStorageKey, undefined); + if (instanceId === undefined) { + storage.set(instanceStorageKey, generateUuid()); + } + + // First session date (once) + const firstSessionDate = storage.get(firstSessionDateStorageKey, undefined); + if (firstSessionDate === undefined) { + storage.set(firstSessionDateStorageKey, new Date().toUTCString()); + } + + // Last / current session (always) + // previous session date was the "current" one at that time + // current session date is "now" + const lastSessionDate = storage.get(currentSessionDateStorageKey, undefined); + const currentSessionDate = new Date().toUTCString(); + storage.set(lastSessionDateStorageKey, typeof lastSessionDate === 'undefined' ? null : lastSessionDate); + storage.set(currentSessionDateStorageKey, currentSessionDate); + } +} + +export class WorkspaceStorageMain extends BaseStorageMain implements IStorageMain { + + private static readonly WORKSPACE_STORAGE_NAME = 'state.vscdb'; + private static readonly WORKSPACE_META_NAME = 'workspace.json'; + + constructor( + private workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier, + private readonly options: IStorageMainOptions, + logService: ILogService, + private readonly environmentService: IEnvironmentService + ) { + super(logService); + } + + protected async doCreate(): Promise { + const { storageFilePath, wasCreated } = await this.prepareWorkspaceStorageFolder(); + + return new Storage(new SQLiteStorageDatabase(storageFilePath, { + logging: this.createLogginOptions() + }), { hint: wasCreated ? StorageHint.STORAGE_DOES_NOT_EXIST : undefined }); + } + + private async prepareWorkspaceStorageFolder(): Promise<{ storageFilePath: string, wasCreated: boolean }> { + + // Return early if using inMemory storage + if (this.options.useInMemoryStorage) { + return { storageFilePath: SQLiteStorageDatabase.IN_MEMORY_PATH, wasCreated: true }; + } + + // Otherwise, ensure the storage folder exists on disk + const workspaceStorageFolderPath = join(this.environmentService.workspaceStorageHome.fsPath, this.workspace.id); + const workspaceStorageDatabasePath = join(workspaceStorageFolderPath, WorkspaceStorageMain.WORKSPACE_STORAGE_NAME); + + const storageExists = await exists(workspaceStorageFolderPath); + if (storageExists) { + return { storageFilePath: workspaceStorageDatabasePath, wasCreated: false }; + } + + // Ensure storage folder exists + await promises.mkdir(workspaceStorageFolderPath, { recursive: true }); + + // Write metadata into folder (but do not await) + this.ensureWorkspaceStorageFolderMeta(workspaceStorageFolderPath); + + return { storageFilePath: workspaceStorageDatabasePath, wasCreated: true }; + } + + private async ensureWorkspaceStorageFolderMeta(workspaceStorageFolderPath: string): Promise { + let meta: object | undefined = undefined; + if (isSingleFolderWorkspaceIdentifier(this.workspace)) { + meta = { folder: this.workspace.uri.toString() }; + } else if (isWorkspaceIdentifier(this.workspace)) { + meta = { workspace: this.workspace.configPath.toString() }; + } + + if (meta) { + try { + const workspaceStorageMetaPath = join(workspaceStorageFolderPath, WorkspaceStorageMain.WORKSPACE_META_NAME); + const storageExists = await exists(workspaceStorageMetaPath); + if (!storageExists) { + await writeFile(workspaceStorageMetaPath, JSON.stringify(meta, undefined, 2)); + } + } catch (error) { + this.logService.error(`StorageMain#ensureWorkspaceStorageFolderMeta(): Unable to create workspace storage metadata due to ${error}`); + } + } + } +} diff --git a/src/vs/platform/storage/electron-main/storageMainService.ts b/src/vs/platform/storage/electron-main/storageMainService.ts new file mode 100644 index 00000000000..842bee97c24 --- /dev/null +++ b/src/vs/platform/storage/electron-main/storageMainService.ts @@ -0,0 +1,142 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { once } from 'vs/base/common/functional'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; +import { ILogService } from 'vs/platform/log/common/log'; +import { GlobalStorageMain, IStorageMain, IStorageMainOptions, WorkspaceStorageMain } from 'vs/platform/storage/electron-main/storageMain'; +import { IWindowSettings } from 'vs/platform/windows/common/windows'; +import { IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; + +export const IStorageMainService = createDecorator('storageMainService'); + +export interface IStorageMainService { + + readonly _serviceBrand: undefined; + + /** + * Provides access to the global storage shared across all windows. + */ + readonly globalStorage: IStorageMain; + + /** + * Provides access to the workspace storage specific to a single window. + */ + workspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier): IStorageMain; +} + +export class StorageMainService extends Disposable implements IStorageMainService { + + declare readonly _serviceBrand: undefined; + + constructor( + @ILogService private readonly logService: ILogService, + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, + @IConfigurationService private readonly configurationService: IConfigurationService + ) { + super(); + + this.registerListeners(); + } + + protected getStorageOptions(): IStorageMainOptions { + return { + useInMemoryStorage: !!this.environmentService.extensionTestsLocationURI // no storage during extension tests! + }; + } + + protected enableMainWorkspaceStorage(): boolean { + return !!(this.configurationService.getValue('window')?.enableExperimentalMainProcessWorkspaceStorage); + } + + private registerListeners(): void { + + // Global Storage: Warmup when any window opens + (async () => { + await this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen); + + this.globalStorage.init(); + })(); + + // Workspace Storage: Warmup when related window with workspace loads + if (this.enableMainWorkspaceStorage()) { + this._register(this.lifecycleMainService.onWillLoadWindow(async e => { + if (e.workspace) { + this.workspaceStorage(e.workspace).init(); + } + })); + } + + // All Storage: Close when shutting down + this._register(this.lifecycleMainService.onWillShutdown(e => { + + // Global Storage + e.join(this.globalStorage.close()); + + // Workspace Storage(s) + for (const [, storage] of this.mapWorkspaceToStorage) { + e.join(storage.close()); + } + })); + } + + //#region Global Storage + + readonly globalStorage = this.createGlobalStorage(); + + private createGlobalStorage(): IStorageMain { + if (this.globalStorage) { + return this.globalStorage; // only once + } + + this.logService.trace(`StorageMainService: creating global storage`); + + const globalStorage = new GlobalStorageMain(this.getStorageOptions(), this.logService, this.environmentService); + + once(globalStorage.onDidCloseStorage)(() => { + this.logService.trace(`StorageMainService: closed global storage`); + }); + + return globalStorage; + } + + //#endregion + + + //#region Workspace Storage + + private readonly mapWorkspaceToStorage = new Map(); + + private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier): IStorageMain { + const workspaceStorage = new WorkspaceStorageMain(workspace, this.getStorageOptions(), this.logService, this.environmentService); + + return workspaceStorage; + } + + workspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier): IStorageMain { + let workspaceStorage = this.mapWorkspaceToStorage.get(workspace.id); + if (!workspaceStorage) { + this.logService.trace(`StorageMainService: creating workspace storage (${workspace.id})`); + + workspaceStorage = this.createWorkspaceStorage(workspace); + this.mapWorkspaceToStorage.set(workspace.id, workspaceStorage); + + once(workspaceStorage.onDidCloseStorage)(() => { + this.logService.trace(`StorageMainService: closed workspace storage (${workspace.id})`); + + this.mapWorkspaceToStorage.delete(workspace.id); + }); + } + + return workspaceStorage; + } + + //#endregion +} diff --git a/src/vs/platform/storage/electron-sandbox/storageService2.ts b/src/vs/platform/storage/electron-sandbox/storageService2.ts new file mode 100644 index 00000000000..f8d6e9ae7c3 --- /dev/null +++ b/src/vs/platform/storage/electron-sandbox/storageService2.ts @@ -0,0 +1,127 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { MutableDisposable } from 'vs/base/common/lifecycle'; +import { StorageScope, WillSaveStateReason, AbstractStorageService } from 'vs/platform/storage/common/storage'; +import { Storage, IStorage } from 'vs/base/parts/storage/common/storage'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier, IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces'; +import { Promises } from 'vs/base/common/async'; +import { mark } from 'vs/base/common/performance'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services'; +import { StorageDatabaseChannelClient } from 'vs/platform/storage/common/storageIpc'; +import { joinPath } from 'vs/base/common/resources'; + +export class NativeStorageService2 extends AbstractStorageService { + + // Global Storage is readonly and shared across windows + private readonly globalStorage: IStorage; + + // Workspace Storage is scoped to a window but can change + // in the current window, when entering a workspace! + private workspaceStorage: IStorage | undefined = undefined; + private workspaceStorageId: string | undefined = undefined; + private workspaceStorageDisposable = this._register(new MutableDisposable()); + + constructor( + workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined, + private readonly mainProcessService: IMainProcessService, + private readonly environmentService: IEnvironmentService + ) { + super(); + + this.globalStorage = this.createGlobalStorage(); + this.workspaceStorage = this.createWorkspaceStorage(workspace); + } + + private createGlobalStorage(): IStorage { + const storageDataBaseClient = new StorageDatabaseChannelClient(this.mainProcessService.getChannel('storage'), undefined); + + const globalStorage = new Storage(storageDataBaseClient.globalStorage); + + this._register(globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key))); + + return globalStorage; + } + + private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier): IStorage; + private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined): IStorage | undefined; + private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined): IStorage | undefined { + const storageDataBaseClient = new StorageDatabaseChannelClient(this.mainProcessService.getChannel('storage'), workspace); + + if (storageDataBaseClient.workspaceStorage) { + const workspaceStorage = new Storage(storageDataBaseClient.workspaceStorage); + + this.workspaceStorageDisposable.value = workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key)); + this.workspaceStorageId = workspace?.id; + + return workspaceStorage; + } else { + this.workspaceStorageDisposable.clear(); + this.workspaceStorageId = undefined; + + return undefined; + } + } + + protected async doInitialize(): Promise { + + // Init all storage locations + mark('code/willInitStorage'); + try { + await Promises.settled([ + this.globalStorage.init(), + this.workspaceStorage?.init() ?? Promise.resolve() + ]); + } finally { + mark('code/didInitStorage'); + } + } + + protected getStorage(scope: StorageScope): IStorage | undefined { + return scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage; + } + + protected getLogDetails(scope: StorageScope): string | undefined { + return scope === StorageScope.GLOBAL ? this.environmentService.globalStorageHome.fsPath : this.workspaceStorageId ? `${joinPath(this.environmentService.workspaceStorageHome, this.workspaceStorageId, 'state.vscdb').fsPath} [!!! Experimental Main Storage !!!]` : undefined; + } + + async close(): Promise { + + // Stop periodic scheduler and idle runner as we now collect state normally + this.stopFlushWhenIdle(); + + // Signal as event so that clients can still store data + this.emitWillSaveState(WillSaveStateReason.SHUTDOWN); + + // Do it + await Promises.settled([ + this.globalStorage.close(), + this.workspaceStorage?.close() ?? Promise.resolve() + ]); + } + + async migrate(toWorkspace: IWorkspaceInitializationPayload): Promise { + + // Keep current workspace storage items around to restore + const oldWorkspaceStorage = this.workspaceStorage; + const oldItems = oldWorkspaceStorage?.items ?? new Map(); + + // Close current which will change to new workspace storage + if (oldWorkspaceStorage) { + await oldWorkspaceStorage.close(); + oldWorkspaceStorage.dispose(); + } + + // Create new workspace storage & init + this.workspaceStorage = this.createWorkspaceStorage(toWorkspace); + await this.workspaceStorage.init(); + + // Copy over previous keys + for (const [key, value] of oldItems) { + this.workspaceStorage.set(key, value); + } + } +} diff --git a/src/vs/platform/storage/node/storageIpc.ts b/src/vs/platform/storage/node/storageIpc.ts deleted file mode 100644 index 67195995c28..00000000000 --- a/src/vs/platform/storage/node/storageIpc.ts +++ /dev/null @@ -1,214 +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 { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { Event, Emitter } from 'vs/base/common/event'; -import { IStorageChangeEvent, IStorageMainService } from 'vs/platform/storage/node/storageMainService'; -import { IUpdateRequest, IStorageDatabase, IStorageItemsChangeEvent } from 'vs/base/parts/storage/common/storage'; -import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { ILogService } from 'vs/platform/log/common/log'; -import { generateUuid } from 'vs/base/common/uuid'; -import { instanceStorageKey, firstSessionDateStorageKey, lastSessionDateStorageKey, currentSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; - -type Key = string; -type Value = string; -type Item = [Key, Value]; - -interface ISerializableUpdateRequest { - insert?: Item[]; - delete?: Key[]; -} - -interface ISerializableItemsChangeEvent { - readonly changed?: Item[]; - readonly deleted?: Key[]; -} - -export class GlobalStorageDatabaseChannel extends Disposable implements IServerChannel { - - private static readonly STORAGE_CHANGE_DEBOUNCE_TIME = 100; - - private readonly _onDidChangeItems = this._register(new Emitter()); - readonly onDidChangeItems = this._onDidChangeItems.event; - - private readonly whenReady = this.init(); - - constructor( - private logService: ILogService, - private storageMainService: IStorageMainService - ) { - super(); - } - - private async init(): Promise { - try { - await this.storageMainService.initialize(); - } catch (error) { - this.logService.error(`[storage] init(): Unable to init global storage due to ${error}`); - } - - // Apply global telemetry values as part of the initialization - // These are global across all windows and thereby should be - // written from the main process once. - this.initTelemetry(); - - // Setup storage change listeners - this.registerListeners(); - } - - private initTelemetry(): void { - const instanceId = this.storageMainService.get(instanceStorageKey, undefined); - if (instanceId === undefined) { - this.storageMainService.store(instanceStorageKey, generateUuid()); - } - - const firstSessionDate = this.storageMainService.get(firstSessionDateStorageKey, undefined); - if (firstSessionDate === undefined) { - this.storageMainService.store(firstSessionDateStorageKey, new Date().toUTCString()); - } - - const lastSessionDate = this.storageMainService.get(currentSessionDateStorageKey, undefined); // previous session date was the "current" one at that time - const currentSessionDate = new Date().toUTCString(); // current session date is "now" - this.storageMainService.store(lastSessionDateStorageKey, typeof lastSessionDate === 'undefined' ? null : lastSessionDate); - this.storageMainService.store(currentSessionDateStorageKey, currentSessionDate); - } - - private registerListeners(): void { - - // Listen for changes in global storage to send to listeners - // that are listening. Use a debouncer to reduce IPC traffic. - this._register(Event.debounce(this.storageMainService.onDidChangeStorage, (prev: IStorageChangeEvent[] | undefined, cur: IStorageChangeEvent) => { - if (!prev) { - prev = [cur]; - } else { - prev.push(cur); - } - - return prev; - }, GlobalStorageDatabaseChannel.STORAGE_CHANGE_DEBOUNCE_TIME)(events => { - if (events.length) { - this._onDidChangeItems.fire(this.serializeEvents(events)); - } - })); - } - - private serializeEvents(events: IStorageChangeEvent[]): ISerializableItemsChangeEvent { - const changed = new Map(); - const deleted = new Set(); - events.forEach(event => { - const existing = this.storageMainService.get(event.key); - if (typeof existing === 'string') { - changed.set(event.key, existing); - } else { - deleted.add(event.key); - } - }); - - return { - changed: Array.from(changed.entries()), - deleted: Array.from(deleted.values()) - }; - } - - listen(_: unknown, event: string): Event { - switch (event) { - case 'onDidChangeItems': return this.onDidChangeItems; - } - - throw new Error(`Event not found: ${event}`); - } - - async call(_: unknown, command: string, arg?: any): Promise { - - // ensure to always wait for ready - await this.whenReady; - - // handle call - switch (command) { - case 'getItems': { - return Array.from(this.storageMainService.items.entries()); - } - - case 'updateItems': { - const items: ISerializableUpdateRequest = arg; - if (items.insert) { - for (const [key, value] of items.insert) { - this.storageMainService.store(key, value); - } - } - - if (items.delete) { - items.delete.forEach(key => this.storageMainService.remove(key)); - } - - break; - } - - default: - throw new Error(`Call not found: ${command}`); - } - } -} - -export class GlobalStorageDatabaseChannelClient extends Disposable implements IStorageDatabase { - - declare readonly _serviceBrand: undefined; - - private readonly _onDidChangeItemsExternal = this._register(new Emitter()); - readonly onDidChangeItemsExternal = this._onDidChangeItemsExternal.event; - - private onDidChangeItemsOnMainListener: IDisposable | undefined; - - constructor(private channel: IChannel) { - super(); - - this.registerListeners(); - } - - private registerListeners(): void { - this.onDidChangeItemsOnMainListener = this.channel.listen('onDidChangeItems')((e: ISerializableItemsChangeEvent) => this.onDidChangeItemsOnMain(e)); - } - - private onDidChangeItemsOnMain(e: ISerializableItemsChangeEvent): void { - if (Array.isArray(e.changed) || Array.isArray(e.deleted)) { - this._onDidChangeItemsExternal.fire({ - changed: e.changed ? new Map(e.changed) : undefined, - deleted: e.deleted ? new Set(e.deleted) : undefined - }); - } - } - - async getItems(): Promise> { - const items: Item[] = await this.channel.call('getItems'); - - return new Map(items); - } - - updateItems(request: IUpdateRequest): Promise { - const serializableRequest: ISerializableUpdateRequest = Object.create(null); - - if (request.insert) { - serializableRequest.insert = Array.from(request.insert.entries()); - } - - if (request.delete) { - serializableRequest.delete = Array.from(request.delete.values()); - } - - return this.channel.call('updateItems', serializableRequest); - } - - async close(): Promise { - - // when we are about to close, we start to ignore main-side changes since we close anyway - dispose(this.onDidChangeItemsOnMainListener); - } - - dispose(): void { - super.dispose(); - - dispose(this.onDidChangeItemsOnMainListener); - } -} diff --git a/src/vs/platform/storage/node/storageMainService.ts b/src/vs/platform/storage/node/storageMainService.ts deleted file mode 100644 index 0d25d4e6554..00000000000 --- a/src/vs/platform/storage/node/storageMainService.ts +++ /dev/null @@ -1,191 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { Event, Emitter } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { ILogService, LogLevel } from 'vs/platform/log/common/log'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { SQLiteStorageDatabase, ISQLiteStorageDatabaseLoggingOptions } from 'vs/base/parts/storage/node/storage'; -import { Storage, IStorage, InMemoryStorageDatabase } from 'vs/base/parts/storage/common/storage'; -import { join } from 'vs/base/common/path'; -import { IS_NEW_KEY } from 'vs/platform/storage/common/storage'; - -export const IStorageMainService = createDecorator('storageMainService'); - -export interface IStorageMainService { - - readonly _serviceBrand: undefined; - - /** - * Emitted whenever data is updated or deleted. - */ - readonly onDidChangeStorage: Event; - - /** - * Emitted when the storage is about to persist. This is the right time - * to persist data to ensure it is stored before the application shuts - * down. - * - * Note: this event may be fired many times, not only on shutdown to prevent - * loss of state in situations where the shutdown is not sufficient to - * persist the data properly. - */ - readonly onWillSaveState: Event; - - /** - * Access to all cached items of this storage service. - */ - readonly items: Map; - - /** - * Required call to ensure the service can be used. - */ - initialize(): Promise; - - /** - * Retrieve an element stored with the given key from storage. Use - * the provided defaultValue if the element is null or undefined. - */ - get(key: string, fallbackValue: string): string; - get(key: string, fallbackValue?: string): string | undefined; - - /** - * Retrieve an element stored with the given key from storage. Use - * the provided defaultValue if the element is null or undefined. The element - * will be converted to a boolean. - */ - getBoolean(key: string, fallbackValue: boolean): boolean; - getBoolean(key: string, fallbackValue?: boolean): boolean | undefined; - - /** - * Retrieve an element stored with the given key from storage. Use - * the provided defaultValue if the element is null or undefined. The element - * will be converted to a number using parseInt with a base of 10. - */ - getNumber(key: string, fallbackValue: number): number; - getNumber(key: string, fallbackValue?: number): number | undefined; - - /** - * Store a string value under the given key to storage. The value will - * be converted to a string. - */ - store(key: string, value: string | boolean | number | undefined | null): void; - - /** - * Delete an element stored under the provided key from storage. - */ - remove(key: string): void; -} - -export interface IStorageChangeEvent { - key: string; -} - -export class StorageMainService extends Disposable implements IStorageMainService { - - declare readonly _serviceBrand: undefined; - - private static readonly STORAGE_NAME = 'state.vscdb'; - - private readonly _onDidChangeStorage = this._register(new Emitter()); - readonly onDidChangeStorage = this._onDidChangeStorage.event; - - private readonly _onWillSaveState = this._register(new Emitter()); - readonly onWillSaveState = this._onWillSaveState.event; - - get items(): Map { return this.storage.items; } - - private storage: IStorage; - - private initializePromise: Promise | undefined; - - constructor( - @ILogService private readonly logService: ILogService, - @IEnvironmentService private readonly environmentService: IEnvironmentService - ) { - super(); - - // Until the storage has been initialized, it can only be in memory - this.storage = new Storage(new InMemoryStorageDatabase()); - } - - private get storagePath(): string { - if (!!this.environmentService.extensionTestsLocationURI) { - return SQLiteStorageDatabase.IN_MEMORY_PATH; // no storage during extension tests! - } - - return join(this.environmentService.globalStorageHome.fsPath, StorageMainService.STORAGE_NAME); - } - - private createLogginOptions(): ISQLiteStorageDatabaseLoggingOptions { - return { - logTrace: (this.logService.getLevel() === LogLevel.Trace) ? msg => this.logService.trace(msg) : undefined, - logError: error => this.logService.error(error) - }; - } - - initialize(): Promise { - if (!this.initializePromise) { - this.initializePromise = this.doInitialize(); - } - - return this.initializePromise; - } - - private async doInitialize(): Promise { - this.storage.dispose(); - this.storage = new Storage(new SQLiteStorageDatabase(this.storagePath, { - logging: this.createLogginOptions() - })); - - this._register(this.storage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key }))); - - await this.storage.init(); - - // Check to see if this is the first time we are "opening" the application - const firstOpen = this.storage.getBoolean(IS_NEW_KEY); - if (firstOpen === undefined) { - this.storage.set(IS_NEW_KEY, true); - } else if (firstOpen) { - this.storage.set(IS_NEW_KEY, false); - } - } - - get(key: string, fallbackValue: string): string; - get(key: string, fallbackValue?: string): string | undefined; - get(key: string, fallbackValue?: string): string | undefined { - return this.storage.get(key, fallbackValue); - } - - getBoolean(key: string, fallbackValue: boolean): boolean; - getBoolean(key: string, fallbackValue?: boolean): boolean | undefined; - getBoolean(key: string, fallbackValue?: boolean): boolean | undefined { - return this.storage.getBoolean(key, fallbackValue); - } - - getNumber(key: string, fallbackValue: number): number; - getNumber(key: string, fallbackValue?: number): number | undefined; - getNumber(key: string, fallbackValue?: number): number | undefined { - return this.storage.getNumber(key, fallbackValue); - } - - store(key: string, value: string | boolean | number | undefined | null): Promise { - return this.storage.set(key, value); - } - - remove(key: string): Promise { - return this.storage.delete(key); - } - - close(): Promise { - - // Signal as event so that clients can still store data - this._onWillSaveState.fire(); - - // Do it - return this.storage.close(); - } -} diff --git a/src/vs/platform/storage/node/storageService.ts b/src/vs/platform/storage/node/storageService.ts index 111bbf4874c..3b33b0ba27b 100644 --- a/src/vs/platform/storage/node/storageService.ts +++ b/src/vs/platform/storage/node/storageService.ts @@ -6,7 +6,7 @@ import { promises } from 'fs'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; -import { StorageScope, WillSaveStateReason, logStorage, IS_NEW_KEY, AbstractStorageService } from 'vs/platform/storage/common/storage'; +import { StorageScope, WillSaveStateReason, IS_NEW_KEY, AbstractStorageService } from 'vs/platform/storage/common/storage'; import { SQLiteStorageDatabase, ISQLiteStorageDatabaseLoggingOptions } from 'vs/base/parts/storage/node/storage'; import { Storage, IStorageDatabase, IStorage, StorageHint } from 'vs/base/parts/storage/common/storage'; import { mark } from 'vs/base/common/performance'; @@ -15,7 +15,7 @@ import { copy, exists, writeFile } from 'vs/base/node/pfs'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces'; import { assertIsDefined } from 'vs/base/common/types'; -import { Promises, RunOnceScheduler, runWhenIdle } from 'vs/base/common/async'; +import { Promises } from 'vs/base/common/async'; export class NativeStorageService extends AbstractStorageService { @@ -28,13 +28,9 @@ export class NativeStorageService extends AbstractStorageService { private workspaceStorage: IStorage | undefined; private workspaceStorageListener: IDisposable | undefined; - private initializePromise: Promise | undefined; - - private readonly periodicFlushScheduler = this._register(new RunOnceScheduler(() => this.doFlushWhenIdle(), 60000 /* every minute */)); - private runWhenIdleDisposable: IDisposable | undefined = undefined; - constructor( private globalStorageDatabase: IStorageDatabase, + private payload: IWorkspaceInitializationPayload | undefined, @ILogService private readonly logService: ILogService, @IEnvironmentService private readonly environmentService: IEnvironmentService ) { @@ -49,26 +45,13 @@ export class NativeStorageService extends AbstractStorageService { this._register(this.globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key))); } - initialize(payload?: IWorkspaceInitializationPayload): Promise { - if (!this.initializePromise) { - this.initializePromise = this.doInitialize(payload); - } - - return this.initializePromise; - } - - private async doInitialize(payload?: IWorkspaceInitializationPayload): Promise { + protected async doInitialize(): Promise { // Init all storage locations await Promises.settled([ this.initializeGlobalStorage(), - payload ? this.initializeWorkspaceStorage(payload) : Promise.resolve() + this.payload ? this.initializeWorkspaceStorage(this.payload) : Promise.resolve() ]); - - // On some OS we do not get enough time to persist state on shutdown (e.g. when - // Windows restarts after applying updates). In other cases, VSCode might crash, - // so we periodically save state to reduce the chance of loosing any state. - this.periodicFlushScheduler.schedule(); } private initializeGlobalStorage(): Promise { @@ -170,71 +153,18 @@ export class NativeStorageService extends AbstractStorageService { } } - get(key: string, scope: StorageScope, fallbackValue: string): string; - get(key: string, scope: StorageScope): string | undefined; - get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined { - return this.getStorage(scope).get(key, fallbackValue); + protected getStorage(scope: StorageScope): IStorage | undefined { + return scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage; } - getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean; - getBoolean(key: string, scope: StorageScope): boolean | undefined; - getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined { - return this.getStorage(scope).getBoolean(key, fallbackValue); - } - - getNumber(key: string, scope: StorageScope, fallbackValue: number): number; - getNumber(key: string, scope: StorageScope): number | undefined; - getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined { - return this.getStorage(scope).getNumber(key, fallbackValue); - } - - protected doStore(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void { - this.getStorage(scope).set(key, value); - } - - protected doRemove(key: string, scope: StorageScope): void { - this.getStorage(scope).delete(key); - } - - private getStorage(scope: StorageScope): IStorage { - return assertIsDefined(scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage); - } - - protected async doFlush(): Promise { - const promises: Promise[] = []; - if (this.globalStorage) { - promises.push(this.globalStorage.whenFlushed()); - } - - if (this.workspaceStorage) { - promises.push(this.workspaceStorage.whenFlushed()); - } - - await Promises.settled(promises); - } - - private doFlushWhenIdle(): void { - - // Dispose any previous idle runner - dispose(this.runWhenIdleDisposable); - - // Run when idle - this.runWhenIdleDisposable = runWhenIdle(() => { - - // send event to collect state - this.flush(); - - // repeat - this.periodicFlushScheduler.schedule(); - }); + protected getLogDetails(scope: StorageScope): string | undefined { + return scope === StorageScope.GLOBAL ? this.environmentService.globalStorageHome.fsPath : this.workspaceStoragePath; } async close(): Promise { // Stop periodic scheduler and idle runner as we now collect state normally - this.periodicFlushScheduler.dispose(); - dispose(this.runWhenIdleDisposable); - this.runWhenIdleDisposable = undefined; + this.stopFlushWhenIdle(); // Signal as event so that clients can still store data this.emitWillSaveState(WillSaveStateReason.SHUTDOWN); @@ -246,21 +176,13 @@ export class NativeStorageService extends AbstractStorageService { ]); } - async logStorage(): Promise { - return logStorage( - this.globalStorage.items, - this.workspaceStorage ? this.workspaceStorage.items : new Map(), // Shared process storage does not has workspace storage - this.environmentService.globalStorageHome.fsPath, - this.workspaceStoragePath || ''); - } - async migrate(toWorkspace: IWorkspaceInitializationPayload): Promise { if (this.workspaceStoragePath === SQLiteStorageDatabase.IN_MEMORY_PATH) { return; // no migration needed if running in memory } // Close workspace DB to be able to copy - await this.getStorage(StorageScope.WORKSPACE).close(); + await this.workspaceStorage?.close(); // Prepare new workspace storage folder const result = await this.prepareWorkspaceStorageFolder(toWorkspace); diff --git a/src/vs/platform/storage/test/electron-browser/storage.test.ts b/src/vs/platform/storage/test/browser/storageService.test.ts similarity index 54% rename from src/vs/platform/storage/test/electron-browser/storage.test.ts rename to src/vs/platform/storage/test/browser/storageService.test.ts index 836f287788c..f3e92ff0880 100644 --- a/src/vs/platform/storage/test/electron-browser/storage.test.ts +++ b/src/vs/platform/storage/test/browser/storageService.test.ts @@ -4,25 +4,45 @@ *--------------------------------------------------------------------------------------------*/ import { strictEqual } from 'assert'; -import { FileStorageDatabase } from 'vs/platform/storage/browser/storageService'; -import { join } from 'vs/base/common/path'; -import { tmpdir } from 'os'; -import { rimraf } from 'vs/base/node/pfs'; +import { BrowserStorageService, FileStorageDatabase } from 'vs/platform/storage/browser/storageService'; import { NullLogService } from 'vs/platform/log/common/log'; import { Storage } from 'vs/base/parts/storage/common/storage'; import { URI } from 'vs/base/common/uri'; import { FileService } from 'vs/platform/files/common/fileService'; -import { getRandomTestPath } from 'vs/base/test/node/testUtils'; -import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; +import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; +import { createSuite } from 'vs/platform/storage/test/common/storageService.test'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -suite('Storage', () => { +suite('StorageService (browser)', function () { - let testDir: string; + const disposables = new DisposableStore(); + + createSuite({ + setup: async () => { + const logService = new NullLogService(); + + const fileService = disposables.add(new FileService(logService)); + + const userDataProvider = disposables.add(new InMemoryFileSystemProvider()); + disposables.add(fileService.registerProvider(Schemas.userData, userDataProvider)); + + const storageService = disposables.add(new BrowserStorageService({ id: String(Date.now()) }, { userRoamingDataHome: URI.file('/User').with({ scheme: Schemas.userData }) } as unknown as IEnvironmentService, fileService)); + + await storageService.initialize(); + + return storageService; + }, + teardown: async storage => { + disposables.clear(); + } + }); +}); + +suite('FileStorageDatabase (browser)', () => { let fileService: FileService; - let fileProvider: DiskFileSystemProvider; const disposables = new DisposableStore(); @@ -31,20 +51,18 @@ suite('Storage', () => { fileService = disposables.add(new FileService(logService)); - fileProvider = disposables.add(new DiskFileSystemProvider(logService)); - disposables.add(fileService.registerProvider(Schemas.file, fileProvider)); - - testDir = getRandomTestPath(tmpdir(), 'vsctests', 'storageservice'); + const userDataProvider = disposables.add(new InMemoryFileSystemProvider()); + disposables.add(fileService.registerProvider(Schemas.userData, userDataProvider)); }); teardown(() => { disposables.clear(); - - return rimraf(testDir); }); - test('File Based Storage', async () => { - let storage = new Storage(new FileStorageDatabase(URI.file(join(testDir, 'storage.json')), false, fileService)); + test('Basics', async () => { + const testDir = URI.file('/User/storage.json').with({ scheme: Schemas.userData }); + + let storage = new Storage(new FileStorageDatabase(testDir, false, fileService)); await storage.init(); @@ -58,7 +76,7 @@ suite('Storage', () => { await storage.close(); - storage = new Storage(new FileStorageDatabase(URI.file(join(testDir, 'storage.json')), false, fileService)); + storage = new Storage(new FileStorageDatabase(testDir, false, fileService)); await storage.init(); @@ -76,7 +94,7 @@ suite('Storage', () => { await storage.close(); - storage = new Storage(new FileStorageDatabase(URI.file(join(testDir, 'storage.json')), false, fileService)); + storage = new Storage(new FileStorageDatabase(testDir, false, fileService)); await storage.init(); diff --git a/src/vs/platform/storage/test/common/storageService.test.ts b/src/vs/platform/storage/test/common/storageService.test.ts index 182ff12af03..fd115f627de 100644 --- a/src/vs/platform/storage/test/common/storageService.test.ts +++ b/src/vs/platform/storage/test/common/storageService.test.ts @@ -4,98 +4,102 @@ *--------------------------------------------------------------------------------------------*/ import { strictEqual, ok } from 'assert'; -import { StorageScope, InMemoryStorageService, StorageTarget, IStorageValueChangeEvent, IStorageTargetChangeEvent } from 'vs/platform/storage/common/storage'; +import { StorageScope, InMemoryStorageService, StorageTarget, IStorageValueChangeEvent, IStorageTargetChangeEvent, IStorageService } from 'vs/platform/storage/common/storage'; -suite('StorageService', function () { +export function createSuite(params: { setup: () => Promise, teardown: (service: T) => Promise }): void { - test('Get Data, Integer, Boolean (global, in-memory)', () => { + let storageService: T; + + setup(async () => { + storageService = await params.setup(); + }); + + teardown(() => { + return params.teardown(storageService); + }); + + test('Get Data, Integer, Boolean (global)', () => { storeData(StorageScope.GLOBAL); }); - test('Get Data, Integer, Boolean (workspace, in-memory)', () => { + test('Get Data, Integer, Boolean (workspace)', () => { storeData(StorageScope.WORKSPACE); }); function storeData(scope: StorageScope): void { - const storage = new InMemoryStorageService(); - let storageValueChangeEvents: IStorageValueChangeEvent[] = []; - storage.onDidChangeValue(e => storageValueChangeEvents.push(e)); + storageService.onDidChangeValue(e => storageValueChangeEvents.push(e)); - strictEqual(storage.get('test.get', scope, 'foobar'), 'foobar'); - strictEqual(storage.get('test.get', scope, ''), ''); - strictEqual(storage.getNumber('test.getNumber', scope, 5), 5); - strictEqual(storage.getNumber('test.getNumber', scope, 0), 0); - strictEqual(storage.getBoolean('test.getBoolean', scope, true), true); - strictEqual(storage.getBoolean('test.getBoolean', scope, false), false); + strictEqual(storageService.get('test.get', scope, 'foobar'), 'foobar'); + strictEqual(storageService.get('test.get', scope, ''), ''); + strictEqual(storageService.getNumber('test.getNumber', scope, 5), 5); + strictEqual(storageService.getNumber('test.getNumber', scope, 0), 0); + strictEqual(storageService.getBoolean('test.getBoolean', scope, true), true); + strictEqual(storageService.getBoolean('test.getBoolean', scope, false), false); - storage.store('test.get', 'foobar', scope, StorageTarget.MACHINE); - strictEqual(storage.get('test.get', scope, (undefined)!), 'foobar'); + storageService.store('test.get', 'foobar', scope, StorageTarget.MACHINE); + strictEqual(storageService.get('test.get', scope, (undefined)!), 'foobar'); let storageValueChangeEvent = storageValueChangeEvents.find(e => e.key === 'test.get'); strictEqual(storageValueChangeEvent?.scope, scope); strictEqual(storageValueChangeEvent?.key, 'test.get'); storageValueChangeEvents = []; - storage.store('test.get', '', scope, StorageTarget.MACHINE); - strictEqual(storage.get('test.get', scope, (undefined)!), ''); + storageService.store('test.get', '', scope, StorageTarget.MACHINE); + strictEqual(storageService.get('test.get', scope, (undefined)!), ''); storageValueChangeEvent = storageValueChangeEvents.find(e => e.key === 'test.get'); strictEqual(storageValueChangeEvent!.scope, scope); strictEqual(storageValueChangeEvent!.key, 'test.get'); - storage.store('test.getNumber', 5, scope, StorageTarget.MACHINE); - strictEqual(storage.getNumber('test.getNumber', scope, (undefined)!), 5); + storageService.store('test.getNumber', 5, scope, StorageTarget.MACHINE); + strictEqual(storageService.getNumber('test.getNumber', scope, (undefined)!), 5); - storage.store('test.getNumber', 0, scope, StorageTarget.MACHINE); - strictEqual(storage.getNumber('test.getNumber', scope, (undefined)!), 0); + storageService.store('test.getNumber', 0, scope, StorageTarget.MACHINE); + strictEqual(storageService.getNumber('test.getNumber', scope, (undefined)!), 0); - storage.store('test.getBoolean', true, scope, StorageTarget.MACHINE); - strictEqual(storage.getBoolean('test.getBoolean', scope, (undefined)!), true); + storageService.store('test.getBoolean', true, scope, StorageTarget.MACHINE); + strictEqual(storageService.getBoolean('test.getBoolean', scope, (undefined)!), true); - storage.store('test.getBoolean', false, scope, StorageTarget.MACHINE); - strictEqual(storage.getBoolean('test.getBoolean', scope, (undefined)!), false); + storageService.store('test.getBoolean', false, scope, StorageTarget.MACHINE); + strictEqual(storageService.getBoolean('test.getBoolean', scope, (undefined)!), false); - strictEqual(storage.get('test.getDefault', scope, 'getDefault'), 'getDefault'); - strictEqual(storage.getNumber('test.getNumberDefault', scope, 5), 5); - strictEqual(storage.getBoolean('test.getBooleanDefault', scope, true), true); + strictEqual(storageService.get('test.getDefault', scope, 'getDefault'), 'getDefault'); + strictEqual(storageService.getNumber('test.getNumberDefault', scope, 5), 5); + strictEqual(storageService.getBoolean('test.getBooleanDefault', scope, true), true); } - test('Remove Data (global, in-memory)', () => { + test('Remove Data (global)', () => { removeData(StorageScope.GLOBAL); }); - test('Remove Data (workspace, in-memory)', () => { + test('Remove Data (workspace)', () => { removeData(StorageScope.WORKSPACE); }); function removeData(scope: StorageScope): void { - const storage = new InMemoryStorageService(); - let storageValueChangeEvents: IStorageValueChangeEvent[] = []; - storage.onDidChangeValue(e => storageValueChangeEvents.push(e)); + storageService.onDidChangeValue(e => storageValueChangeEvents.push(e)); - storage.store('test.remove', 'foobar', scope, StorageTarget.MACHINE); - strictEqual('foobar', storage.get('test.remove', scope, (undefined)!)); + storageService.store('test.remove', 'foobar', scope, StorageTarget.MACHINE); + strictEqual('foobar', storageService.get('test.remove', scope, (undefined)!)); - storage.remove('test.remove', scope); - ok(!storage.get('test.remove', scope, (undefined)!)); + storageService.remove('test.remove', scope); + ok(!storageService.get('test.remove', scope, (undefined)!)); let storageValueChangeEvent = storageValueChangeEvents.find(e => e.key === 'test.remove'); strictEqual(storageValueChangeEvent?.scope, scope); strictEqual(storageValueChangeEvent?.key, 'test.remove'); } test('Keys (in-memory)', () => { - const storage = new InMemoryStorageService(); - let storageTargetEvent: IStorageTargetChangeEvent | undefined = undefined; - storage.onDidChangeTarget(e => storageTargetEvent = e); + storageService.onDidChangeTarget(e => storageTargetEvent = e); let storageValueChangeEvent: IStorageValueChangeEvent | undefined = undefined; - storage.onDidChangeValue(e => storageValueChangeEvent = e); + storageService.onDidChangeValue(e => storageValueChangeEvent = e); // Empty for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { - strictEqual(storage.keys(scope, target).length, 0); + strictEqual(storageService.keys(scope, target).length, 0); } } @@ -105,8 +109,8 @@ suite('StorageService', function () { storageTargetEvent = Object.create(null); storageValueChangeEvent = Object.create(null); - storage.store('test.target1', 'value1', scope, target); - strictEqual(storage.keys(scope, target).length, 1); + storageService.store('test.target1', 'value1', scope, target); + strictEqual(storageService.keys(scope, target).length, 1); strictEqual(storageTargetEvent?.scope, scope); strictEqual(storageValueChangeEvent?.key, 'test.target1'); strictEqual(storageValueChangeEvent?.scope, scope); @@ -115,33 +119,33 @@ suite('StorageService', function () { storageTargetEvent = undefined; storageValueChangeEvent = Object.create(null); - storage.store('test.target1', 'otherValue1', scope, target); - strictEqual(storage.keys(scope, target).length, 1); + storageService.store('test.target1', 'otherValue1', scope, target); + strictEqual(storageService.keys(scope, target).length, 1); strictEqual(storageTargetEvent, undefined); strictEqual(storageValueChangeEvent?.key, 'test.target1'); strictEqual(storageValueChangeEvent?.scope, scope); strictEqual(storageValueChangeEvent?.target, target); - storage.store('test.target2', 'value2', scope, target); - storage.store('test.target3', 'value3', scope, target); + storageService.store('test.target2', 'value2', scope, target); + storageService.store('test.target3', 'value3', scope, target); - strictEqual(storage.keys(scope, target).length, 3); + strictEqual(storageService.keys(scope, target).length, 3); } } // Remove values for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { - const keysLength = storage.keys(scope, target).length; + const keysLength = storageService.keys(scope, target).length; - storage.store('test.target4', 'value1', scope, target); - strictEqual(storage.keys(scope, target).length, keysLength + 1); + storageService.store('test.target4', 'value1', scope, target); + strictEqual(storageService.keys(scope, target).length, keysLength + 1); storageTargetEvent = Object.create(null); storageValueChangeEvent = Object.create(null); - storage.remove('test.target4', scope); - strictEqual(storage.keys(scope, target).length, keysLength); + storageService.remove('test.target4', scope); + strictEqual(storageService.keys(scope, target).length, keysLength); strictEqual(storageTargetEvent?.scope, scope); strictEqual(storageValueChangeEvent?.key, 'test.target4'); strictEqual(storageValueChangeEvent?.scope, scope); @@ -151,48 +155,55 @@ suite('StorageService', function () { // Remove all for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { - const keys = storage.keys(scope, target); + const keys = storageService.keys(scope, target); for (const key of keys) { - storage.remove(key, scope); + storageService.remove(key, scope); } - strictEqual(storage.keys(scope, target).length, 0); + strictEqual(storageService.keys(scope, target).length, 0); } } // Adding undefined or null removes value for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { - storage.store('test.target1', 'value1', scope, target); - strictEqual(storage.keys(scope, target).length, 1); + storageService.store('test.target1', 'value1', scope, target); + strictEqual(storageService.keys(scope, target).length, 1); storageTargetEvent = Object.create(null); - storage.store('test.target1', undefined, scope, target); - strictEqual(storage.keys(scope, target).length, 0); + storageService.store('test.target1', undefined, scope, target); + strictEqual(storageService.keys(scope, target).length, 0); strictEqual(storageTargetEvent?.scope, scope); - storage.store('test.target1', '', scope, target); - strictEqual(storage.keys(scope, target).length, 1); + storageService.store('test.target1', '', scope, target); + strictEqual(storageService.keys(scope, target).length, 1); - storage.store('test.target1', null, scope, target); - strictEqual(storage.keys(scope, target).length, 0); + storageService.store('test.target1', null, scope, target); + strictEqual(storageService.keys(scope, target).length, 0); } } // Target change storageTargetEvent = undefined; - storage.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE); + storageService.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE); ok(storageTargetEvent); storageTargetEvent = undefined; - storage.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.USER); + storageService.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.USER); ok(storageTargetEvent); storageTargetEvent = undefined; - storage.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE); + storageService.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE); ok(storageTargetEvent); storageTargetEvent = undefined; - storage.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE); + storageService.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE); ok(!storageTargetEvent); // no change in target }); +} + +suite('StorageService (in-memory)', function () { + createSuite({ + setup: async () => new InMemoryStorageService(), + teardown: async () => { } + }); }); diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts new file mode 100644 index 00000000000..2a91d25b5d9 --- /dev/null +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -0,0 +1,225 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { notStrictEqual, strictEqual } from 'assert'; +import { OPTIONS, parseArgs } from 'vs/platform/environment/node/argv'; +import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { NullLogService } from 'vs/platform/log/common/log'; +import { StorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; +import { currentSessionDateStorageKey, firstSessionDateStorageKey, instanceStorageKey } from 'vs/platform/telemetry/common/telemetry'; +import { IStorageChangeEvent, IStorageMain, IStorageMainOptions } from 'vs/platform/storage/electron-main/storageMain'; +import { generateUuid } from 'vs/base/common/uuid'; +import { IS_NEW_KEY } from 'vs/platform/storage/common/storage'; +import { ILifecycleMainService, LifecycleMainPhase, ShutdownEvent, UnloadReason } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; +import { Emitter, Event } from 'vs/base/common/event'; +import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; +import { ICodeWindow } from 'vs/platform/windows/electron-main/windows'; +import { Promises } from 'vs/base/common/async'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; + +suite('StorageMainService', function () { + + class TestStorageMainService extends StorageMainService { + + protected getStorageOptions(): IStorageMainOptions { + return { + useInMemoryStorage: true + }; + } + + protected enableMainWorkspaceStorage(): boolean { + return true; + } + } + + class StorageTestLifecycleMainService implements ILifecycleMainService { + + _serviceBrand: undefined; + + onBeforeShutdown = Event.None; + + private readonly _onWillShutdown = new Emitter(); + readonly onWillShutdown = this._onWillShutdown.event; + + async fireOnWillShutdown(): Promise { + const joiners: Promise[] = []; + + this._onWillShutdown.fire({ + join(promise) { + if (promise) { + joiners.push(promise); + } + } + }); + + await Promises.settled(joiners); + } + + onWillLoadWindow = Event.None; + onBeforeCloseWindow = Event.None; + onBeforeUnloadWindow = Event.None; + + wasRestarted = false; + quitRequested = false; + + phase = LifecycleMainPhase.Ready; + + registerWindow(window: ICodeWindow): void { } + async reload(window: ICodeWindow, cli?: NativeParsedArgs): Promise { } + async unload(window: ICodeWindow, reason: UnloadReason): Promise { return true; } + relaunch(options?: { addArgs?: string[] | undefined; removeArgs?: string[] | undefined; }): void { } + async quit(fromUpdate?: boolean): Promise { return true; } + async kill(code?: number): Promise { } + async when(phase: LifecycleMainPhase): Promise { } + } + + async function testStorage(storage: IStorageMain, isGlobal: boolean): Promise { + + // Telemetry: added after init + if (isGlobal) { + strictEqual(storage.items.size, 0); + strictEqual(storage.get(instanceStorageKey), undefined); + await storage.init(); + strictEqual(typeof storage.get(instanceStorageKey), 'string'); + strictEqual(typeof storage.get(firstSessionDateStorageKey), 'string'); + strictEqual(typeof storage.get(currentSessionDateStorageKey), 'string'); + } else { + await storage.init(); + } + + let storageChangeEvent: IStorageChangeEvent | undefined = undefined; + const storageChangeListener = storage.onDidChangeStorage(e => { + storageChangeEvent = e; + }); + + let storageDidClose = false; + const storageCloseListener = storage.onDidCloseStorage(() => storageDidClose = true); + + // Basic store/get/remove + const size = storage.items.size; + + storage.set('bar', 'foo'); + strictEqual(storageChangeEvent!.key, 'bar'); + storage.set('barNumber', 55); + storage.set('barBoolean', true); + + strictEqual(storage.get('bar'), 'foo'); + strictEqual(storage.get('barNumber'), '55'); + strictEqual(storage.get('barBoolean'), 'true'); + + strictEqual(storage.items.size, size + 3); + + storage.delete('bar'); + strictEqual(storage.get('bar'), undefined); + + strictEqual(storage.items.size, size + 2); + + // IS_NEW + strictEqual(storage.get(IS_NEW_KEY), 'true'); + + // Close + await storage.close(); + + strictEqual(storageDidClose, true); + + storageChangeListener.dispose(); + storageCloseListener.dispose(); + } + + test('basics (global)', function () { + const storageMainService = new TestStorageMainService(new NullLogService(), new NativeEnvironmentService(parseArgs(process.argv, OPTIONS)), new StorageTestLifecycleMainService(), new TestConfigurationService()); + + return testStorage(storageMainService.globalStorage, true); + }); + + test('basics (workspace)', function () { + const workspace = { id: generateUuid() }; + const storageMainService = new TestStorageMainService(new NullLogService(), new NativeEnvironmentService(parseArgs(process.argv, OPTIONS)), new StorageTestLifecycleMainService(), new TestConfigurationService()); + + return testStorage(storageMainService.workspaceStorage(workspace), false); + }); + + test('storage closed onWillShutdown', async function () { + const lifecycleMainService = new StorageTestLifecycleMainService(); + const workspace = { id: generateUuid() }; + const storageMainService = new TestStorageMainService(new NullLogService(), new NativeEnvironmentService(parseArgs(process.argv, OPTIONS)), lifecycleMainService, new TestConfigurationService()); + + let workspaceStorage = storageMainService.workspaceStorage(workspace); + let didCloseWorkspaceStorage = false; + workspaceStorage.onDidCloseStorage(() => { + didCloseWorkspaceStorage = true; + }); + + let globalStorage = storageMainService.globalStorage; + let didCloseGlobalStorage = false; + globalStorage.onDidCloseStorage(() => { + didCloseGlobalStorage = true; + }); + + strictEqual(workspaceStorage, storageMainService.workspaceStorage(workspace)); // same instance as long as not closed + + await globalStorage.init(); + await workspaceStorage.init(); + + await lifecycleMainService.fireOnWillShutdown(); + + strictEqual(didCloseGlobalStorage, true); + strictEqual(didCloseWorkspaceStorage, true); + + let storage2 = storageMainService.workspaceStorage(workspace); + notStrictEqual(workspaceStorage, storage2); + + return storage2.close(); + }); + + test('storage closed before init works', async function () { + const storageMainService = new TestStorageMainService(new NullLogService(), new NativeEnvironmentService(parseArgs(process.argv, OPTIONS)), new StorageTestLifecycleMainService(), new TestConfigurationService()); + const workspace = { id: generateUuid() }; + + let workspaceStorage = storageMainService.workspaceStorage(workspace); + let didCloseWorkspaceStorage = false; + workspaceStorage.onDidCloseStorage(() => { + didCloseWorkspaceStorage = true; + }); + + let globalStorage = storageMainService.globalStorage; + let didCloseGlobalStorage = false; + globalStorage.onDidCloseStorage(() => { + didCloseGlobalStorage = true; + }); + + await globalStorage.close(); + await workspaceStorage.close(); + + strictEqual(didCloseGlobalStorage, true); + strictEqual(didCloseWorkspaceStorage, true); + }); + + test('storage closed before init awaits works', async function () { + const storageMainService = new TestStorageMainService(new NullLogService(), new NativeEnvironmentService(parseArgs(process.argv, OPTIONS)), new StorageTestLifecycleMainService(), new TestConfigurationService()); + const workspace = { id: generateUuid() }; + + let workspaceStorage = storageMainService.workspaceStorage(workspace); + let didCloseWorkspaceStorage = false; + workspaceStorage.onDidCloseStorage(() => { + didCloseWorkspaceStorage = true; + }); + + let globalStorage = storageMainService.globalStorage; + let didCloseGlobalStorage = false; + globalStorage.onDidCloseStorage(() => { + didCloseGlobalStorage = true; + }); + + globalStorage.init(); + workspaceStorage.init(); + + await globalStorage.close(); + await workspaceStorage.close(); + + strictEqual(didCloseGlobalStorage, true); + strictEqual(didCloseWorkspaceStorage, true); + }); +}); diff --git a/src/vs/platform/storage/test/node/storageService.test.ts b/src/vs/platform/storage/test/node/storageService.test.ts index fed89b3eda6..68ba513e051 100644 --- a/src/vs/platform/storage/test/node/storageService.test.ts +++ b/src/vs/platform/storage/test/node/storageService.test.ts @@ -15,40 +15,48 @@ import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; import { InMemoryStorageDatabase } from 'vs/base/parts/storage/common/storage'; import { URI } from 'vs/base/common/uri'; import { flakySuite, getRandomTestPath } from 'vs/base/test/node/testUtils'; +import { createSuite } from 'vs/platform/storage/test/common/storageService.test'; -flakySuite('NativeStorageService', function () { +flakySuite('StorageService (native)', function () { + + class StorageTestEnvironmentService extends NativeEnvironmentService { + + constructor(private workspaceStorageFolderPath: URI, private _extensionsPath: string) { + super(parseArgs(process.argv, OPTIONS)); + } + + get workspaceStorageHome(): URI { + return this.workspaceStorageFolderPath; + } + + get extensionsPath(): string { + return this._extensionsPath; + } + } let testDir: string; - setup(() => { - testDir = getRandomTestPath(tmpdir(), 'vsctests', 'storageservice'); + createSuite({ + setup: async () => { + testDir = getRandomTestPath(tmpdir(), 'vsctests', 'storageservice'); - return promises.mkdir(testDir, { recursive: true }); - }); + await promises.mkdir(testDir, { recursive: true }); - teardown(() => { - return rimraf(testDir); + const storageService = new NativeStorageService(new InMemoryStorageDatabase(), { id: String(Date.now()) }, new NullLogService(), new StorageTestEnvironmentService(URI.file(testDir), testDir)); + await storageService.initialize(); + + return storageService; + }, + teardown: async storageService => { + await storageService.close(); + + return rimraf(testDir); + } }); test('Migrate Data', async function () { - - class StorageTestEnvironmentService extends NativeEnvironmentService { - - constructor(private workspaceStorageFolderPath: URI, private _extensionsPath: string) { - super(parseArgs(process.argv, OPTIONS)); - } - - get workspaceStorageHome(): URI { - return this.workspaceStorageFolderPath; - } - - get extensionsPath(): string { - return this._extensionsPath; - } - } - - const storage = new NativeStorageService(new InMemoryStorageDatabase(), new NullLogService(), new StorageTestEnvironmentService(URI.file(testDir), testDir)); - await storage.initialize({ id: String(Date.now()) }); + const storage = new NativeStorageService(new InMemoryStorageDatabase(), { id: String(Date.now()) }, new NullLogService(), new StorageTestEnvironmentService(URI.file(testDir), testDir)); + await storage.initialize(); storage.store('bar', 'foo', StorageScope.WORKSPACE, StorageTarget.MACHINE); storage.store('barNumber', 55, StorageScope.WORKSPACE, StorageTarget.MACHINE); diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 1256437a0d7..58b90b849ff 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -288,6 +288,7 @@ export const editorWidgetResizeBorder = registerColor('editorWidget.resizeBorder export const quickInputBackground = registerColor('quickInput.background', { dark: editorWidgetBackground, light: editorWidgetBackground, hc: editorWidgetBackground }, nls.localize('pickerBackground', "Quick picker background color. The quick picker widget is the container for pickers like the command palette.")); export const quickInputForeground = registerColor('quickInput.foreground', { dark: editorWidgetForeground, light: editorWidgetForeground, hc: editorWidgetForeground }, nls.localize('pickerForeground', "Quick picker foreground color. The quick picker widget is the container for pickers like the command palette.")); export const quickInputTitleBackground = registerColor('quickInputTitle.background', { dark: new Color(new RGBA(255, 255, 255, 0.105)), light: new Color(new RGBA(0, 0, 0, 0.06)), hc: '#000000' }, nls.localize('pickerTitleBackground', "Quick picker title background color. The quick picker widget is the container for pickers like the command palette.")); +export const quickInputListFocusBackground = registerColor('quickInput.list.focusBackground', { dark: '#062F4A', light: '#D6EBFF', hc: null }, nls.localize('quickInput.listFocusBackground', "Quick picker background color for the focused item.")); export const pickerGroupForeground = registerColor('pickerGroup.foreground', { dark: '#3794FF', light: '#0066BF', hc: Color.white }, nls.localize('pickerGroupForeground', "Quick picker color for grouping labels.")); export const pickerGroupBorder = registerColor('pickerGroup.border', { dark: '#3F3F46', light: '#CCCEDB', hc: Color.white }, nls.localize('pickerGroupBorder', "Quick picker color for grouping borders.")); @@ -362,13 +363,15 @@ export const diffDiagonalFill = registerColor('diffEditor.diagonalFill', { dark: /** * List and tree colors */ -export const listFocusBackground = registerColor('list.focusBackground', { dark: '#062F4A', light: '#D6EBFF', hc: null }, nls.localize('listFocusBackground', "List/Tree background color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); +export const listFocusBackground = registerColor('list.focusBackground', { dark: null, light: null, hc: null }, nls.localize('listFocusBackground', "List/Tree background color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listFocusForeground = registerColor('list.focusForeground', { dark: null, light: null, hc: null }, nls.localize('listFocusForeground', "List/Tree foreground color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); +export const listFocusOutline = registerColor('list.focusOutline', { dark: focusBorder, light: focusBorder, hc: activeContrastBorder }, nls.localize('listFocusOutline', "List/Tree outline color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listActiveSelectionBackground = registerColor('list.activeSelectionBackground', { dark: '#094771', light: '#0060C0', hc: null }, nls.localize('listActiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listActiveSelectionForeground = registerColor('list.activeSelectionForeground', { dark: Color.white, light: Color.white, hc: null }, nls.localize('listActiveSelectionForeground', "List/Tree foreground color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listInactiveSelectionBackground = registerColor('list.inactiveSelectionBackground', { dark: '#37373D', light: '#E4E6F1', hc: null }, nls.localize('listInactiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); export const listInactiveSelectionForeground = registerColor('list.inactiveSelectionForeground', { dark: null, light: null, hc: null }, nls.localize('listInactiveSelectionForeground', "List/Tree foreground color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); export const listInactiveFocusBackground = registerColor('list.inactiveFocusBackground', { dark: null, light: null, hc: null }, nls.localize('listInactiveFocusBackground', "List/Tree background color for the focused item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); +export const listInactiveFocusOutline = registerColor('list.inactiveFocusOutline', { dark: null, light: null, hc: null }, nls.localize('listInactiveFocusOutline', "List/Tree outline color for the focused item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); export const listHoverBackground = registerColor('list.hoverBackground', { dark: '#2A2D2E', light: '#F0F0F0', hc: null }, nls.localize('listHoverBackground', "List/Tree background when hovering over items using the mouse.")); export const listHoverForeground = registerColor('list.hoverForeground', { dark: null, light: null, hc: null }, nls.localize('listHoverForeground', "List/Tree foreground when hovering over items using the mouse.")); export const listDropBackground = registerColor('list.dropBackground', { dark: listFocusBackground, light: listFocusBackground, hc: null }, nls.localize('listDropBackground', "List/Tree drag and drop background when moving items around using the mouse.")); diff --git a/src/vs/platform/theme/common/styler.ts b/src/vs/platform/theme/common/styler.ts index 90bb201972c..02c66cc4325 100644 --- a/src/vs/platform/theme/common/styler.ts +++ b/src/vs/platform/theme/common/styler.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService'; -import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, darken, listFilterWidgetOutline, listFilterWidgetNoMatchesOutline, listFilterWidgetBackground, editorWidgetBackground, treeIndentGuidesStroke, editorWidgetForeground, simpleCheckboxBackground, simpleCheckboxBorder, simpleCheckboxForeground, ColorValue, resolveColorValue, textLinkForeground, problemsWarningIconForeground, problemsErrorIconForeground, problemsInfoIconForeground, buttonSecondaryBackground, buttonSecondaryForeground, buttonSecondaryHoverBackground } from 'vs/platform/theme/common/colorRegistry'; +import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, darken, listFilterWidgetOutline, listFilterWidgetNoMatchesOutline, listFilterWidgetBackground, editorWidgetBackground, treeIndentGuidesStroke, editorWidgetForeground, simpleCheckboxBackground, simpleCheckboxBorder, simpleCheckboxForeground, ColorValue, resolveColorValue, textLinkForeground, problemsWarningIconForeground, problemsErrorIconForeground, problemsInfoIconForeground, buttonSecondaryBackground, buttonSecondaryForeground, buttonSecondaryHoverBackground, listFocusOutline, listInactiveFocusOutline } from 'vs/platform/theme/common/colorRegistry'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; import { IThemable, styleFn } from 'vs/base/common/styler'; @@ -210,6 +210,7 @@ export interface IListStyleOverrides extends IStyleOverrides { listBackground?: ColorIdentifier; listFocusBackground?: ColorIdentifier; listFocusForeground?: ColorIdentifier; + listFocusOutline?: ColorIdentifier; listActiveSelectionBackground?: ColorIdentifier; listActiveSelectionForeground?: ColorIdentifier; listFocusAndSelectionBackground?: ColorIdentifier; @@ -217,11 +218,10 @@ export interface IListStyleOverrides extends IStyleOverrides { listInactiveSelectionBackground?: ColorIdentifier; listInactiveSelectionForeground?: ColorIdentifier; listInactiveFocusBackground?: ColorIdentifier; + listInactiveFocusOutline?: ColorIdentifier; listHoverBackground?: ColorIdentifier; listHoverForeground?: ColorIdentifier; listDropBackground?: ColorIdentifier; - listFocusOutline?: ColorIdentifier; - listInactiveFocusOutline?: ColorIdentifier; listSelectionOutline?: ColorIdentifier; listHoverOutline?: ColorIdentifier; listFilterWidgetBackground?: ColorIdentifier; @@ -236,26 +236,27 @@ export function attachListStyler(widget: IThemable, themeService: IThemeService, } export const defaultListStyles: IColorMapping = { - listFocusBackground: listFocusBackground, - listFocusForeground: listFocusForeground, - listActiveSelectionBackground: darken(listActiveSelectionBackground, 0.1), - listActiveSelectionForeground: listActiveSelectionForeground, + listFocusBackground, + listFocusForeground, + listFocusOutline, + listActiveSelectionBackground, + listActiveSelectionForeground, listFocusAndSelectionBackground: listActiveSelectionBackground, listFocusAndSelectionForeground: listActiveSelectionForeground, - listInactiveSelectionBackground: listInactiveSelectionBackground, - listInactiveSelectionForeground: listInactiveSelectionForeground, - listInactiveFocusBackground: listInactiveFocusBackground, - listHoverBackground: listHoverBackground, - listHoverForeground: listHoverForeground, - listDropBackground: listDropBackground, - listFocusOutline: activeContrastBorder, + listInactiveSelectionBackground, + listInactiveSelectionForeground, + listInactiveFocusBackground, + listInactiveFocusOutline, + listHoverBackground, + listHoverForeground, + listDropBackground, listSelectionOutline: activeContrastBorder, listHoverOutline: activeContrastBorder, - listFilterWidgetBackground: listFilterWidgetBackground, - listFilterWidgetOutline: listFilterWidgetOutline, - listFilterWidgetNoMatchesOutline: listFilterWidgetNoMatchesOutline, + listFilterWidgetBackground, + listFilterWidgetOutline, + listFilterWidgetNoMatchesOutline, listMatchesShadow: widgetShadow, - treeIndentGuidesStroke: treeIndentGuidesStroke + treeIndentGuidesStroke }; export interface IButtonStyleOverrides extends IStyleOverrides { diff --git a/src/vs/platform/update/electron-main/abstractUpdateService.ts b/src/vs/platform/update/electron-main/abstractUpdateService.ts index f15dec1b8a7..2d96ce768ad 100644 --- a/src/vs/platform/update/electron-main/abstractUpdateService.ts +++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts @@ -46,7 +46,7 @@ export abstract class AbstractUpdateService implements IUpdateService { constructor( @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @IConfigurationService protected configurationService: IConfigurationService, - @IEnvironmentMainService private readonly environmentService: IEnvironmentMainService, + @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @IRequestService protected requestService: IRequestService, @ILogService protected logService: ILogService, ) { } @@ -57,11 +57,11 @@ export abstract class AbstractUpdateService implements IUpdateService { * https://github.com/microsoft/vscode/issues/89784 */ initialize(): void { - if (!this.environmentService.isBuilt) { + if (!this.environmentMainService.isBuilt) { return; // updates are never enabled when running out of sources } - if (this.environmentService.disableUpdates) { + if (this.environmentMainService.disableUpdates) { this.logService.info('update#ctor - updates are disabled by the environment'); return; } diff --git a/src/vs/platform/update/electron-main/updateService.darwin.ts b/src/vs/platform/update/electron-main/updateService.darwin.ts index ddfa1ee4a5f..8d67a459903 100644 --- a/src/vs/platform/update/electron-main/updateService.darwin.ts +++ b/src/vs/platform/update/electron-main/updateService.darwin.ts @@ -32,11 +32,11 @@ export class DarwinUpdateService extends AbstractUpdateService { @ILifecycleMainService lifecycleMainService: ILifecycleMainService, @IConfigurationService configurationService: IConfigurationService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IEnvironmentMainService environmentService: IEnvironmentMainService, + @IEnvironmentMainService environmentMainService: IEnvironmentMainService, @IRequestService requestService: IRequestService, @ILogService logService: ILogService ) { - super(lifecycleMainService, configurationService, environmentService, requestService, logService); + super(lifecycleMainService, configurationService, environmentMainService, requestService, logService); } initialize(): void { diff --git a/src/vs/platform/update/electron-main/updateService.linux.ts b/src/vs/platform/update/electron-main/updateService.linux.ts index 91565b13954..d00055b590f 100644 --- a/src/vs/platform/update/electron-main/updateService.linux.ts +++ b/src/vs/platform/update/electron-main/updateService.linux.ts @@ -23,12 +23,12 @@ export class LinuxUpdateService extends AbstractUpdateService { @ILifecycleMainService lifecycleMainService: ILifecycleMainService, @IConfigurationService configurationService: IConfigurationService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IEnvironmentMainService environmentService: IEnvironmentMainService, + @IEnvironmentMainService environmentMainService: IEnvironmentMainService, @IRequestService requestService: IRequestService, @ILogService logService: ILogService, @INativeHostMainService private readonly nativeHostMainService: INativeHostMainService ) { - super(lifecycleMainService, configurationService, environmentService, requestService, logService); + super(lifecycleMainService, configurationService, environmentMainService, requestService, logService); } protected buildUpdateFeedUrl(quality: string): string { diff --git a/src/vs/platform/update/electron-main/updateService.snap.ts b/src/vs/platform/update/electron-main/updateService.snap.ts index 1712182c365..e0d6010d06a 100644 --- a/src/vs/platform/update/electron-main/updateService.snap.ts +++ b/src/vs/platform/update/electron-main/updateService.snap.ts @@ -36,10 +36,10 @@ abstract class AbstractUpdateService2 implements IUpdateService { constructor( @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, - @IEnvironmentMainService environmentService: IEnvironmentMainService, + @IEnvironmentMainService environmentMainService: IEnvironmentMainService, @ILogService protected logService: ILogService, ) { - if (environmentService.disableUpdates) { + if (environmentMainService.disableUpdates) { this.logService.info('update#ctor - updates are disabled'); return; } @@ -140,11 +140,11 @@ export class SnapUpdateService extends AbstractUpdateService2 { private snap: string, private snapRevision: string, @ILifecycleMainService lifecycleMainService: ILifecycleMainService, - @IEnvironmentMainService environmentService: IEnvironmentMainService, + @IEnvironmentMainService environmentMainService: IEnvironmentMainService, @ILogService logService: ILogService, @ITelemetryService private readonly telemetryService: ITelemetryService ) { - super(lifecycleMainService, environmentService, logService); + super(lifecycleMainService, environmentMainService, logService); const watcher = watch(path.dirname(this.snap)); const onChange = Event.fromNodeEventEmitter(watcher, 'change', (_, fileName: string) => fileName); diff --git a/src/vs/platform/update/electron-main/updateService.win32.ts b/src/vs/platform/update/electron-main/updateService.win32.ts index c92a0931dcc..3fcc37e91d4 100644 --- a/src/vs/platform/update/electron-main/updateService.win32.ts +++ b/src/vs/platform/update/electron-main/updateService.win32.ts @@ -63,13 +63,13 @@ export class Win32UpdateService extends AbstractUpdateService { @ILifecycleMainService lifecycleMainService: ILifecycleMainService, @IConfigurationService configurationService: IConfigurationService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IEnvironmentMainService environmentService: IEnvironmentMainService, + @IEnvironmentMainService environmentMainService: IEnvironmentMainService, @IRequestService requestService: IRequestService, @ILogService logService: ILogService, @IFileService private readonly fileService: IFileService, @INativeHostMainService private readonly nativeHostMainService: INativeHostMainService ) { - super(lifecycleMainService, configurationService, environmentService, requestService, logService); + super(lifecycleMainService, configurationService, environmentMainService, requestService, logService); } initialize(): void { diff --git a/src/vs/platform/url/electron-main/electronUrlListener.ts b/src/vs/platform/url/electron-main/electronUrlListener.ts index 50ca2ea249c..e281cff7d5e 100644 --- a/src/vs/platform/url/electron-main/electronUrlListener.ts +++ b/src/vs/platform/url/electron-main/electronUrlListener.ts @@ -43,7 +43,7 @@ export class ElectronURLListener { initialUrisToHandle: { uri: URI, url: string }[], private readonly urlService: IURLService, windowsMainService: IWindowsMainService, - environmentService: IEnvironmentMainService + environmentMainService: IEnvironmentMainService ) { // the initial set of URIs we need to handle once the window is ready @@ -51,7 +51,7 @@ export class ElectronURLListener { // Windows: install as protocol handler if (isWindows) { - const windowsParameters = environmentService.isBuilt ? [] : [`"${environmentService.appRoot}"`]; + const windowsParameters = environmentMainService.isBuilt ? [] : [`"${environmentMainService.appRoot}"`]; windowsParameters.push('--open-url', '--'); app.setAsDefaultProtocolClient(product.urlProtocol, process.execPath, windowsParameters); } @@ -82,7 +82,7 @@ export class ElectronURLListener { if (isWindowReady) { this.flush(); } else { - Event.once(windowsMainService.onWindowReady)(this.flush, this, this.disposables); + Event.once(windowsMainService.onDidSignalReadyWindow)(this.flush, this, this.disposables); } } diff --git a/src/vs/platform/webview/electron-main/webviewMainService.ts b/src/vs/platform/webview/electron-main/webviewMainService.ts index f569607a424..dd3984d4ba5 100644 --- a/src/vs/platform/webview/electron-main/webviewMainService.ts +++ b/src/vs/platform/webview/electron-main/webviewMainService.ts @@ -87,7 +87,7 @@ export class WebviewMainService extends Disposable implements IWebviewManagerSer if (typeof (id as WebviewWindowId).windowId === 'number') { const { windowId } = (id as WebviewWindowId); const window = this.windowsMainService.getWindowById(windowId); - if (!window) { + if (!window?.win) { throw new Error(`Invalid windowId: ${windowId}`); } contents = window.win.webContents; diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 2ae70d36bce..3ca7fa2b745 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -114,6 +114,7 @@ export interface IWindowSettings { readonly enableMenuBarMnemonics: boolean; readonly closeWhenEmpty: boolean; readonly clickThroughInactive: boolean; + readonly enableExperimentalMainProcessWorkspaceStorage: boolean; } export function getTitleBarStyle(configurationService: IConfigurationService): 'native' | 'custom' { @@ -253,6 +254,8 @@ export interface INativeWindowConfiguration extends IWindowConfiguration, Native filesToWait?: IPathsToWaitFor; os: IOSConfiguration; + + enableExperimentalMainProcessWorkspaceStorage: boolean; } /** diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index 7fc86f531bd..a68581665d7 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -9,7 +9,7 @@ import { localize } from 'vs/nls'; import { getMarks, mark } from 'vs/base/common/performance'; import { Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; -import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display, TouchBarSegmentedControl, NativeImage, BrowserWindowConstructorOptions, SegmentedControlSegment, nativeTheme, Event, RenderProcessGoneDetails } from 'electron'; +import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display, TouchBarSegmentedControl, NativeImage, BrowserWindowConstructorOptions, SegmentedControlSegment, nativeTheme, Event } from 'electron'; import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService'; import { ILogService } from 'vs/platform/log/common/log'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -19,7 +19,7 @@ import product from 'vs/platform/product/common/product'; import { WindowMinimumSize, IWindowSettings, MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, zoomLevelToZoomFactor, INativeWindowConfiguration } from 'vs/platform/windows/common/windows'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { browserCodeLoadingCacheStrategy, isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; -import { defaultWindowState, ICodeWindow, IWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows'; +import { defaultWindowState, ICodeWindow, ILoadEvent, IWindowState, WindowError, WindowMode } from 'vs/platform/windows/electron-main/windows'; import { ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IWorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService'; import { IBackupMainService } from 'vs/platform/backup/electron-main/backup'; @@ -32,7 +32,7 @@ import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogMain import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; -import { IStorageMainService } from 'vs/platform/storage/node/storageMainService'; +import { IStorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; import { ByteSize, IFileService } from 'vs/platform/files/common/files'; import { FileAccess, Schemas } from 'vs/base/common/network'; import { isLaunchedFromCli } from 'vs/platform/environment/node/argvHelper'; @@ -48,11 +48,6 @@ interface ITouchBarSegment extends SegmentedControlSegment { id: string; } -const enum WindowError { - UNRESPONSIVE = 1, - CRASHED = 2 -} - const enum ReadyState { /** @@ -80,17 +75,17 @@ export class CodeWindow extends Disposable implements ICodeWindow { private static readonly MAX_URL_LENGTH = 2 * ByteSize.MB; // https://cs.chromium.org/chromium/src/url/url_constants.cc?l=32 - private readonly _onLoad = this._register(new Emitter()); - readonly onLoad = this._onLoad.event; + private readonly _onWillLoad = this._register(new Emitter()); + readonly onWillLoad = this._onWillLoad.event; - private readonly _onReady = this._register(new Emitter()); - readonly onReady = this._onReady.event; + private readonly _onDidSignalReady = this._register(new Emitter()); + readonly onDidSignalReady = this._onDidSignalReady.event; - private readonly _onClose = this._register(new Emitter()); - readonly onClose = this._onClose.event; + private readonly _onDidClose = this._register(new Emitter()); + readonly onDidClose = this._onDidClose.event; - private readonly _onDestroy = this._register(new Emitter()); - readonly onDestroy = this._onDestroy.event; + private readonly _onDidDestroy = this._register(new Emitter()); + readonly onDidDestroy = this._onDidDestroy.event; private hiddenTitleBarStyle: boolean | undefined; private showTimeoutHandle: NodeJS.Timeout | undefined; @@ -114,9 +109,9 @@ export class CodeWindow extends Disposable implements ICodeWindow { constructor( config: IWindowCreationOptions, @ILogService private readonly logService: ILogService, - @IEnvironmentMainService private readonly environmentService: IEnvironmentMainService, + @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @IFileService private readonly fileService: IFileService, - @IStorageMainService storageService: IStorageMainService, + @IStorageMainService storageMainService: IStorageMainService, @IConfigurationService private readonly configurationService: IConfigurationService, @IThemeMainService private readonly themeMainService: IThemeMainService, @IWorkspacesManagementMainService private readonly workspacesManagementMainService: IWorkspacesManagementMainService, @@ -158,7 +153,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { nativeWindowOpen: true, webviewTag: true, zoomFactor: zoomLevelToZoomFactor(windowConfig?.zoomLevel), - ...this.environmentService.sandbox ? + ...this.environmentMainService.sandbox ? // Sandbox { @@ -181,9 +176,9 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Linux: always // Windows: only when running out of sources, otherwise an icon is set by us on the executable if (isLinux) { - options.icon = join(this.environmentService.appRoot, 'resources/linux/code.png'); - } else if (isWindows && !this.environmentService.isBuilt) { - options.icon = join(this.environmentService.appRoot, 'resources/win32/code_150x150.png'); + options.icon = join(this.environmentMainService.appRoot, 'resources/linux/code.png'); + } else if (isWindows && !this.environmentMainService.isBuilt) { + options.icon = join(this.environmentMainService.appRoot, 'resources/win32/code_150x150.png'); } if (isMacintosh && !this.useNativeFullScreen()) { @@ -217,7 +212,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { this._id = this._win.id; // Open devtools if instructed from command line args - if (this.environmentService.args['open-devtools'] === true) { + if (this.environmentMainService.args['open-devtools'] === true) { this._win.webContents.openDevTools(); } @@ -267,9 +262,9 @@ export class CodeWindow extends Disposable implements ICodeWindow { this.createTouchBar(); // Request handling - this.marketplaceHeadersPromise = resolveMarketplaceHeaders(product.version, this.environmentService, this.fileService, { - get(key) { return storageService.get(key); }, - store(key, value) { storageService.store(key, value); } + this.marketplaceHeadersPromise = resolveMarketplaceHeaders(product.version, this.environmentMainService, this.fileService, { + get: key => storageMainService.globalStorage.get(key), + store: (key, value) => storageMainService.globalStorage.set(key, value) }); // Eventing @@ -285,7 +280,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { get id(): number { return this._id; } private _win: BrowserWindow; - get win(): BrowserWindow { return this._win; } + get win(): BrowserWindow | null { return this._win; } get hasHiddenTitleBarStyle(): boolean { return !!this.hiddenTitleBarStyle; } @@ -297,7 +292,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { setRepresentedFilename(filename: string): void { if (isMacintosh) { - this.win.setRepresentedFilename(filename); + this._win.setRepresentedFilename(filename); } else { this.representedFilename = filename; } @@ -305,7 +300,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { getRepresentedFilename(): string | undefined { if (isMacintosh) { - return this.win.getRepresentedFilename(); + return this._win.getRepresentedFilename(); } return this.representedFilename; @@ -367,7 +362,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { } // Events - this._onReady.fire(); + this._onDidSignalReady.fire(); } ready(): Promise { @@ -395,21 +390,21 @@ export class CodeWindow extends Disposable implements ICodeWindow { resolve(); } - const closeListener = this.onClose(() => handle()); - const loadListener = this.onLoad(() => handle()); + const closeListener = this.onDidClose(() => handle()); + const loadListener = this.onWillLoad(() => handle()); }); } private registerListeners(): void { // Crashes & Unrsponsive & Failed to load - this._win.webContents.on('render-process-gone', (event, details) => this.onWindowError(WindowError.CRASHED, details)); this._win.on('unresponsive', () => this.onWindowError(WindowError.UNRESPONSIVE)); - this._win.webContents.on('did-fail-load', (event, errorCode, errorDescription) => this.logService.warn('Main: failed to load workbench window, ', errorDescription)); + this._win.webContents.on('render-process-gone', (event, details) => this.onWindowError(WindowError.CRASHED, details.reason)); + this._win.webContents.on('did-fail-load', (event, errorCode, errorDescription) => this.onWindowError(WindowError.LOAD, errorDescription)); // Window close this._win.on('closed', () => { - this._onClose.fire(); + this._onDidClose.fire(); this.dispose(); }); @@ -535,21 +530,36 @@ export class CodeWindow extends Disposable implements ICodeWindow { }); // Handle configuration changes - this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated())); + this._register(this.configurationService.onDidChangeConfiguration(() => this.onConfigurationUpdated())); // Handle Workspace events - this._register(this.workspacesManagementMainService.onUntitledWorkspaceDeleted(e => this.onUntitledWorkspaceDeleted(e))); + this._register(this.workspacesManagementMainService.onDidDeleteUntitledWorkspace(e => this.onDidDeleteUntitledWorkspace(e))); // Inject headers when requests are incoming const urls = ['https://marketplace.visualstudio.com/*', 'https://*.vsassets.io/*']; - this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details, cb) => - this.marketplaceHeadersPromise.then(headers => cb({ cancel: false, requestHeaders: Object.assign(details.requestHeaders, headers) }))); + this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, async (details, cb) => { + const headers = await this.marketplaceHeadersPromise; + + cb({ cancel: false, requestHeaders: Object.assign(details.requestHeaders, headers) }); + }); } - private onWindowError(error: WindowError.UNRESPONSIVE): void; - private onWindowError(error: WindowError.CRASHED, details: RenderProcessGoneDetails): void; - private onWindowError(error: WindowError, details?: RenderProcessGoneDetails): void { - this.logService.error(error === WindowError.CRASHED ? `Main: renderer process crashed (detail: ${details?.reason})` : 'Main: detected unresponsive'); + private async onWindowError(error: WindowError.UNRESPONSIVE): Promise; + private async onWindowError(error: WindowError.CRASHED, details: string): Promise; + private async onWindowError(error: WindowError.LOAD, details: string): Promise; + private async onWindowError(type: WindowError, details?: string): Promise { + + switch (type) { + case WindowError.CRASHED: + this.logService.error(`CodeWindow: renderer process crashed (detail: ${details})`); + break; + case WindowError.UNRESPONSIVE: + this.logService.error('CodeWindow: detected unresponsive'); + break; + case WindowError.LOAD: + this.logService.error(`CodeWindow: failed to load workbench window: ${details}`); + break; + } // If we run extension tests from CLI, showing a dialog is not // very helpful in this case. Rather, we bring down the test run @@ -566,10 +576,10 @@ export class CodeWindow extends Disposable implements ICodeWindow { type WindowErrorEvent = { type: WindowError; }; - this.telemetryService.publicLog2('windowerror', { type: error }); + this.telemetryService.publicLog2('windowerror', { type }); // Unresponsive - if (error === WindowError.UNRESPONSIVE) { + if (type === WindowError.UNRESPONSIVE) { if (this.isExtensionDevelopmentHost || this.isExtensionTestHost || (this._win && this._win.webContents && this._win.webContents.isDevToolsOpened())) { // TODO@bpasero Workaround for https://github.com/microsoft/vscode/issues/56994 // In certain cases the window can report unresponsiveness because a breakpoint was hit @@ -581,63 +591,63 @@ export class CodeWindow extends Disposable implements ICodeWindow { } // Show Dialog - this.dialogMainService.showMessageBox({ + const result = await this.dialogMainService.showMessageBox({ title: product.nameLong, type: 'warning', buttons: [mnemonicButtonLabel(localize({ key: 'reopen', comment: ['&& denotes a mnemonic'] }, "&&Reopen")), mnemonicButtonLabel(localize({ key: 'wait', comment: ['&& denotes a mnemonic'] }, "&&Keep Waiting")), mnemonicButtonLabel(localize({ key: 'close', comment: ['&& denotes a mnemonic'] }, "&&Close"))], message: localize('appStalled', "The window is no longer responding"), detail: localize('appStalledDetail', "You can reopen or close the window or keep waiting."), noLink: true - }, this._win).then(result => { - if (!this._win) { - return; // Return early if the window has been going down already - } + }, this._win); - if (result.response === 0) { - this._win.webContents.forcefullyCrashRenderer(); // Calling reload() immediately after calling this method will force the reload to occur in a new process - this.reload(); - } else if (result.response === 2) { - this.destroyWindow(); - } - }); + if (!this._win) { + return; // Return early if the window has been going down already + } + + if (result.response === 0) { + this._win.webContents.forcefullyCrashRenderer(); // Calling reload() immediately after calling this method will force the reload to occur in a new process + this.reload(); + } else if (result.response === 2) { + this.destroyWindow(); + } } // Crashed - else { + else if (type === WindowError.CRASHED) { let message: string; - if (details && details.reason !== 'crashed') { - message = localize('appCrashedDetails', "The window has crashed (reason: '{0}')", details?.reason); + if (details && details !== 'crashed') { + message = localize('appCrashedDetails', "The window has crashed (reason: '{0}')", details); } else { - message = localize('appCrashed', "The window has crashed", details?.reason); + message = localize('appCrashed', "The window has crashed", details); } - this.dialogMainService.showMessageBox({ + const result = await this.dialogMainService.showMessageBox({ title: product.nameLong, type: 'warning', buttons: [mnemonicButtonLabel(localize({ key: 'reopen', comment: ['&& denotes a mnemonic'] }, "&&Reopen")), mnemonicButtonLabel(localize({ key: 'close', comment: ['&& denotes a mnemonic'] }, "&&Close"))], message, detail: localize('appCrashedDetail', "We are sorry for the inconvenience! You can reopen the window to continue where you left off."), noLink: true - }, this._win).then(result => { - if (!this._win) { - return; // Return early if the window has been going down already - } + }, this._win); - if (result.response === 0) { - this.reload(); - } else if (result.response === 1) { - this.destroyWindow(); - } - }); + if (!this._win) { + return; // Return early if the window has been going down already + } + + if (result.response === 0) { + this.reload(); + } else if (result.response === 1) { + this.destroyWindow(); + } } } private destroyWindow(): void { - this._onDestroy.fire(); // 'close' event will not be fired on destroy(), so signal crash via explicit event + this._onDidDestroy.fire(); // 'close' event will not be fired on destroy(), so signal crash via explicit event this._win.destroy(); // make sure to destroy the window as it has crashed } - private onUntitledWorkspaceDeleted(workspace: IWorkspaceIdentifier): void { + private onDidDeleteUntitledWorkspace(workspace: IWorkspaceIdentifier): void { // Make sure to update our workspace config if we detect that it // was deleted @@ -677,7 +687,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { } addTabbedWindow(window: ICodeWindow): void { - if (isMacintosh) { + if (isMacintosh && window.win) { this._win.addTabbedWindow(window.win); } } @@ -747,7 +757,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Make window visible if it did not open in N seconds because this indicates an error // Only do this when running out of sources and not when running tests - if (!this.environmentService.isBuilt && !this.environmentService.extensionTestsLocationURI) { + if (!this.environmentMainService.isBuilt && !this.environmentMainService.extensionTestsLocationURI) { this.showTimeoutHandle = setTimeout(() => { if (this._win && !this._win.isVisible() && !this._win.isMinimized()) { this._win.show(); @@ -758,7 +768,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { } // Event - this._onLoad.fire(); + this._onWillLoad.fire({ workspace: configuration.workspace }); } async reload(cli?: NativeParsedArgs): Promise { @@ -824,7 +834,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { windowConfiguration.windowId = this._win.id; windowConfiguration.sessionId = `window:${this._win.id}`; windowConfiguration.logLevel = this.logService.getLevel(); - windowConfiguration.logsPath = this.environmentService.logsPath; + windowConfiguration.logsPath = this.environmentMainService.logsPath; // Set zoomlevel const windowConfig = this.configurationService.getValue('window'); @@ -851,13 +861,15 @@ export class CodeWindow extends Disposable implements ICodeWindow { windowConfiguration.perfMarks = getMarks(); // Parts splash - windowConfiguration.partsSplashPath = join(this.environmentService.userDataPath, 'rapid_render.json'); + windowConfiguration.partsSplashPath = join(this.environmentMainService.userDataPath, 'rapid_render.json'); // OS Info windowConfiguration.os = { release: release() }; + windowConfiguration.enableExperimentalMainProcessWorkspaceStorage = !!(windowConfig?.enableExperimentalMainProcessWorkspaceStorage); + // Config (combination of process.argv and window configuration) const environment = parseArgs(process.argv, OPTIONS); const config = Object.assign(environment, windowConfiguration) as unknown as { [key: string]: unknown }; @@ -887,7 +899,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { private doGetUrl(config: object): string { let workbench: string; - if (this.environmentService.sandbox) { + if (this.environmentMainService.sandbox) { workbench = 'vs/code/electron-sandbox/workbench/workbench.html'; } else { workbench = 'vs/code/electron-browser/workbench/workbench.html'; @@ -1257,26 +1269,26 @@ export class CodeWindow extends Disposable implements ICodeWindow { const action = systemPreferences.getUserDefault('AppleActionOnDoubleClick', 'string'); switch (action) { case 'Minimize': - this.win.minimize(); + this._win.minimize(); break; case 'None': break; case 'Maximize': default: - if (this.win.isMaximized()) { - this.win.unmaximize(); + if (this._win.isMaximized()) { + this._win.unmaximize(); } else { - this.win.maximize(); + this._win.maximize(); } } } // Linux/Windows: just toggle maximize/minimized state else { - if (this.win.isMaximized()) { - this.win.unmaximize(); + if (this._win.isMaximized()) { + this._win.unmaximize(); } else { - this.win.maximize(); + this._win.maximize(); } } } diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index c309ff263f4..96f92d28946 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -60,17 +60,21 @@ export const enum WindowMode { Fullscreen } +export interface ILoadEvent { + workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined; +} + export interface ICodeWindow extends IDisposable { - readonly onLoad: Event; - readonly onReady: Event; - readonly onClose: Event; - readonly onDestroy: Event; + readonly onWillLoad: Event; + readonly onDidSignalReady: Event; + readonly onDidClose: Event; + readonly onDidDestroy: Event; readonly whenClosedOrLoaded: Promise; readonly id: number; - readonly win: BrowserWindow; + readonly win: BrowserWindow | null; /* `null` after being disposed */ readonly config: INativeWindowConfiguration | undefined; readonly openedWorkspace?: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier; @@ -121,6 +125,24 @@ export interface ICodeWindow extends IDisposable { serializeWindowState(): IWindowState; } +export const enum WindowError { + + /** + * Maps to the `unresponsive` event on a `BrowserWindow`. + */ + UNRESPONSIVE = 1, + + /** + * Maps to the `render-proces-gone` event on a `WebContents`. + */ + CRASHED = 2, + + /** + * Maps to the `did-fail-load` event on a `WebContents`. + */ + LOAD = 3 +} + export const IWindowsMainService = createDecorator('windowsMainService'); export interface IWindowsCountChangedEvent { @@ -132,11 +154,11 @@ export interface IWindowsMainService { readonly _serviceBrand: undefined; - readonly onWindowsCountChanged: Event; + readonly onDidChangeWindowsCount: Event; - readonly onWindowOpened: Event; - readonly onWindowReady: Event; - readonly onWindowDestroyed: Event; + readonly onDidOpenWindow: Event; + readonly onDidSignalReadyWindow: Event; + readonly onDidDestroyWindow: Event; open(openConfig: IOpenConfiguration): ICodeWindow[]; openEmptyWindow(openConfig: IOpenEmptyConfiguration, options?: IOpenEmptyWindowOptions): ICodeWindow[]; diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index c7625528cf4..f68cf41be11 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -14,7 +14,7 @@ import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { IStateService } from 'vs/platform/state/node/state'; import { CodeWindow } from 'vs/platform/windows/electron-main/window'; import { BrowserWindow, MessageBoxOptions, WebContents } from 'electron'; -import { ILifecycleMainService, UnloadReason, LifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; +import { ILifecycleMainService, UnloadReason, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService } from 'vs/platform/log/common/log'; import { IWindowSettings, IPath, isFileToOpen, isWorkspaceToOpen, isFolderToOpen, IWindowOpenable, IOpenEmptyWindowOptions, IAddFoldersRequest, IPathsToWaitFor, INativeWindowConfiguration, INativeOpenFileRequest } from 'vs/platform/windows/common/windows'; @@ -35,7 +35,7 @@ import { getSingleFolderWorkspaceIdentifier, getWorkspaceIdentifier, IWorkspaces import { once } from 'vs/base/common/functional'; import { Disposable } from 'vs/base/common/lifecycle'; import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogMainService'; -import { withNullAsUndefined } from 'vs/base/common/types'; +import { assertIsDefined, withNullAsUndefined } from 'vs/base/common/types'; import { isWindowsDriveLetter, toSlashes, parseLineAndColumnAware, sanitizeFilePath } from 'vs/base/common/extpath'; import { CharCode } from 'vs/base/common/charCode'; import { getPathLabel } from 'vs/base/common/labels'; @@ -118,17 +118,17 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic private static readonly WINDOWS: ICodeWindow[] = []; - private readonly _onWindowOpened = this._register(new Emitter()); - readonly onWindowOpened = this._onWindowOpened.event; + private readonly _onDidOpenWindow = this._register(new Emitter()); + readonly onDidOpenWindow = this._onDidOpenWindow.event; - private readonly _onWindowReady = this._register(new Emitter()); - readonly onWindowReady = this._onWindowReady.event; + private readonly _onDidSignalReadyWindow = this._register(new Emitter()); + readonly onDidSignalReadyWindow = this._onDidSignalReadyWindow.event; - private readonly _onWindowDestroyed = this._register(new Emitter()); - readonly onWindowDestroyed = this._onWindowDestroyed.event; + private readonly _onDidDestroyWindow = this._register(new Emitter()); + readonly onDidDestroyWindow = this._onDidDestroyWindow.event; - private readonly _onWindowsCountChanged = this._register(new Emitter()); - readonly onWindowsCountChanged = this._onWindowsCountChanged.event; + private readonly _onDidChangeWindowsCount = this._register(new Emitter()); + readonly onDidChangeWindowsCount = this._onDidChangeWindowsCount.event; private readonly windowsStateHandler = this._register(new WindowsStateHandler(this, this.stateService, this.lifecycleMainService, this.logService, this.configurationService)); @@ -137,7 +137,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic private readonly initialUserEnv: IProcessEnvironment, @ILogService private readonly logService: ILogService, @IStateService private readonly stateService: IStateService, - @IEnvironmentMainService private readonly environmentService: IEnvironmentMainService, + @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @IBackupMainService private readonly backupMainService: IBackupMainService, @IConfigurationService private readonly configurationService: IConfigurationService, @@ -155,11 +155,11 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic private registerListeners(): void { // Signal a window is ready after having entered a workspace - this._register(this.workspacesManagementMainService.onWorkspaceEntered(event => this._onWindowReady.fire(event.window))); + this._register(this.workspacesManagementMainService.onDidEnterWorkspace(event => this._onDidSignalReadyWindow.fire(event.window))); } openEmptyWindow(openConfig: IOpenEmptyConfiguration, options?: IOpenEmptyWindowOptions): ICodeWindow[] { - let cli = this.environmentService.args; + let cli = this.environmentMainService.args; const remote = options?.remoteAuthority; if (cli && (cli.remote !== remote)) { cli = { ...cli, remote }; @@ -670,7 +670,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic buttons: [localize('ok', "OK")], message: uri.scheme === Schemas.file ? localize('pathNotExistTitle', "Path does not exist") : localize('uriInvalidTitle', "URI can not be opened"), detail: uri.scheme === Schemas.file ? - localize('pathNotExistDetail', "The path '{0}' does not seem to exist anymore on disk.", getPathLabel(uri.fsPath, this.environmentService)) : + localize('pathNotExistDetail', "The path '{0}' does not seem to exist anymore on disk.", getPathLabel(uri.fsPath, this.environmentMainService)) : localize('uriInvalidDetail', "The URI '{0}' is not valid and can not be opened.", uri.toString()), noLink: true }; @@ -1135,9 +1135,9 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // Build `INativeWindowConfiguration` from config and options const configuration = { ...options.cli } as INativeWindowConfiguration; - configuration.appRoot = this.environmentService.appRoot; + configuration.appRoot = this.environmentMainService.appRoot; configuration.machineId = this.machineId; - configuration.nodeCachedDataDir = this.environmentService.nodeCachedDataDir; + configuration.nodeCachedDataDir = this.environmentMainService.nodeCachedDataDir; configuration.mainPid = process.pid; configuration.execPath = process.execPath; configuration.userEnv = { ...this.initialUserEnv, ...options.userEnv }; @@ -1157,7 +1157,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // For all other cases we first call into registerEmptyWindowBackupSync() to set it before // loading the window. if (options.emptyWindowBackupInfo) { - configuration.backupPath = join(this.environmentService.backupHome, options.emptyWindowBackupInfo.backupFolder); + configuration.backupPath = join(this.environmentMainService.backupHome, options.emptyWindowBackupInfo.backupFolder); } let window: ICodeWindow | undefined; @@ -1191,20 +1191,22 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic WindowsMainService.WINDOWS.push(createdWindow); // Indicate new window via event - this._onWindowOpened.fire(createdWindow); + this._onDidOpenWindow.fire(createdWindow); // Indicate number change via event - this._onWindowsCountChanged.fire({ oldCount: this.getWindowCount() - 1, newCount: this.getWindowCount() }); + this._onDidChangeWindowsCount.fire({ oldCount: this.getWindowCount() - 1, newCount: this.getWindowCount() }); // Window Events - once(createdWindow.onReady)(() => this._onWindowReady.fire(createdWindow)); - once(createdWindow.onClose)(() => this.onWindowClosed(createdWindow)); - once(createdWindow.onDestroy)(() => this._onWindowDestroyed.fire(createdWindow)); - createdWindow.win.webContents.removeAllListeners('devtools-reload-page'); // remove built in listener so we can handle this on our own - createdWindow.win.webContents.on('devtools-reload-page', () => this.lifecycleMainService.reload(createdWindow)); + once(createdWindow.onDidSignalReady)(() => this._onDidSignalReadyWindow.fire(createdWindow)); + once(createdWindow.onDidClose)(() => this.onWindowClosed(createdWindow)); + once(createdWindow.onDidDestroy)(() => this._onDidDestroyWindow.fire(createdWindow)); + + const webContents = assertIsDefined(createdWindow.win?.webContents); + webContents.removeAllListeners('devtools-reload-page'); // remove built in listener so we can handle this on our own + webContents.on('devtools-reload-page', () => this.lifecycleMainService.reload(createdWindow)); // Lifecycle - (this.lifecycleMainService as LifecycleMainService).registerWindow(createdWindow); + this.lifecycleMainService.registerWindow(createdWindow); } // Existing window @@ -1264,7 +1266,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic WindowsMainService.WINDOWS.splice(index, 1); // Emit - this._onWindowsCountChanged.fire({ oldCount: this.getWindowCount() + 1, newCount: this.getWindowCount() }); + this._onDidChangeWindowsCount.fire({ oldCount: this.getWindowCount() + 1, newCount: this.getWindowCount() }); } getFocusedWindow(): ICodeWindow | undefined { diff --git a/src/vs/platform/windows/electron-main/windowsStateHandler.ts b/src/vs/platform/windows/electron-main/windowsStateHandler.ts index b0e50c85ff4..75c96494b80 100644 --- a/src/vs/platform/windows/electron-main/windowsStateHandler.ts +++ b/src/vs/platform/windows/electron-main/windowsStateHandler.ts @@ -83,9 +83,9 @@ export class WindowsStateHandler extends Disposable { }); // Handle various lifecycle events around windows - this.lifecycleMainService.onBeforeWindowClose(window => this.onBeforeWindowClose(window)); + this.lifecycleMainService.onBeforeCloseWindow(window => this.onBeforeCloseWindow(window)); this.lifecycleMainService.onBeforeShutdown(() => this.onBeforeShutdown()); - this.windowsMainService.onWindowsCountChanged(e => { + this.windowsMainService.onDidChangeWindowsCount(e => { if (e.newCount - e.oldCount > 0) { // clear last closed window state when a new window opens. this helps on macOS where // otherwise closing the last window, opening a new window and then quitting would @@ -95,16 +95,16 @@ export class WindowsStateHandler extends Disposable { }); // try to save state before destroy because close will not fire - this.windowsMainService.onWindowDestroyed(window => this.onBeforeWindowClose(window)); + this.windowsMainService.onDidDestroyWindow(window => this.onBeforeCloseWindow(window)); } - // Note that onBeforeShutdown() and onBeforeWindowClose() are fired in different order depending on the OS: + // Note that onBeforeShutdown() and onBeforeCloseWindow() are fired in different order depending on the OS: // - macOS: since the app will not quit when closing the last window, you will always first get - // the onBeforeShutdown() event followed by N onBeforeWindowClose() events for each window + // the onBeforeShutdown() event followed by N onBeforeCloseWindow() events for each window // - other: on other OS, closing the last window will quit the app so the order depends on the - // user interaction: closing the last window will first trigger onBeforeWindowClose() + // user interaction: closing the last window will first trigger onBeforeCloseWindow() // and then onBeforeShutdown(). Using the quit action however will first issue onBeforeShutdown() - // and then onBeforeWindowClose(). + // and then onBeforeCloseWindow(). // // Here is the behavior on different OS depending on action taken (Electron 1.7.x): // @@ -113,27 +113,27 @@ export class WindowsStateHandler extends Disposable { // - close(1): close one window via the window close button // - closeAll: close all windows via the taskbar command // - onBeforeShutdown(N): number of windows reported in this event handler - // - onBeforeWindowClose(N, M): number of windows reported and quitRequested boolean in this event handler + // - onBeforeCloseWindow(N, M): number of windows reported and quitRequested boolean in this event handler // // macOS - // - quit(1): onBeforeShutdown(1), onBeforeWindowClose(1, true) - // - quit(2): onBeforeShutdown(2), onBeforeWindowClose(2, true), onBeforeWindowClose(2, true) + // - quit(1): onBeforeShutdown(1), onBeforeCloseWindow(1, true) + // - quit(2): onBeforeShutdown(2), onBeforeCloseWindow(2, true), onBeforeCloseWindow(2, true) // - quit(0): onBeforeShutdown(0) - // - close(1): onBeforeWindowClose(1, false) + // - close(1): onBeforeCloseWindow(1, false) // // Windows - // - quit(1): onBeforeShutdown(1), onBeforeWindowClose(1, true) - // - quit(2): onBeforeShutdown(2), onBeforeWindowClose(2, true), onBeforeWindowClose(2, true) - // - close(1): onBeforeWindowClose(2, false)[not last window] - // - close(1): onBeforeWindowClose(1, false), onBeforeShutdown(0)[last window] - // - closeAll(2): onBeforeWindowClose(2, false), onBeforeWindowClose(2, false), onBeforeShutdown(0) + // - quit(1): onBeforeShutdown(1), onBeforeCloseWindow(1, true) + // - quit(2): onBeforeShutdown(2), onBeforeCloseWindow(2, true), onBeforeCloseWindow(2, true) + // - close(1): onBeforeCloseWindow(2, false)[not last window] + // - close(1): onBeforeCloseWindow(1, false), onBeforeShutdown(0)[last window] + // - closeAll(2): onBeforeCloseWindow(2, false), onBeforeCloseWindow(2, false), onBeforeShutdown(0) // // Linux - // - quit(1): onBeforeShutdown(1), onBeforeWindowClose(1, true) - // - quit(2): onBeforeShutdown(2), onBeforeWindowClose(2, true), onBeforeWindowClose(2, true) - // - close(1): onBeforeWindowClose(2, false)[not last window] - // - close(1): onBeforeWindowClose(1, false), onBeforeShutdown(0)[last window] - // - closeAll(2): onBeforeWindowClose(2, false), onBeforeWindowClose(2, false), onBeforeShutdown(0) + // - quit(1): onBeforeShutdown(1), onBeforeCloseWindow(1, true) + // - quit(2): onBeforeShutdown(2), onBeforeCloseWindow(2, true), onBeforeCloseWindow(2, true) + // - close(1): onBeforeCloseWindow(2, false)[not last window] + // - close(1): onBeforeCloseWindow(1, false), onBeforeShutdown(0)[last window] + // - closeAll(2): onBeforeCloseWindow(2, false), onBeforeCloseWindow(2, false), onBeforeShutdown(0) // private onBeforeShutdown(): void { this.shuttingDown = true; @@ -185,7 +185,7 @@ export class WindowsStateHandler extends Disposable { } // See note on #onBeforeShutdown() for details how these events are flowing - private onBeforeWindowClose(window: ICodeWindow): void { + private onBeforeCloseWindow(window: ICodeWindow): void { if (this.lifecycleMainService.quitRequested) { return; // during quit, many windows close in parallel so let it be handled in the before-quit handler } diff --git a/src/vs/platform/windows/test/electron-main/window.test.ts b/src/vs/platform/windows/test/electron-main/windowsFinder.test.ts similarity index 95% rename from src/vs/platform/windows/test/electron-main/window.test.ts rename to src/vs/platform/windows/test/electron-main/windowsFinder.test.ts index 94ae6eea848..1fd596212bd 100644 --- a/src/vs/platform/windows/test/electron-main/window.test.ts +++ b/src/vs/platform/windows/test/electron-main/windowsFinder.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { join } from 'vs/base/common/path'; import { findWindowOnFile } from 'vs/platform/windows/electron-main/windowsFinder'; -import { ICodeWindow, IWindowState } from 'vs/platform/windows/electron-main/windows'; +import { ICodeWindow, ILoadEvent, IWindowState } from 'vs/platform/windows/electron-main/windows'; import { IWorkspaceIdentifier, toWorkspaceFolders } from 'vs/platform/workspaces/common/workspaces'; import { URI } from 'vs/base/common/uri'; import { getPathFromAmdModule } from 'vs/base/common/amd'; @@ -32,13 +32,13 @@ suite('WindowsFinder', () => { function createTestCodeWindow(options: { lastFocusTime: number, openedFolderUri?: URI, openedWorkspace?: IWorkspaceIdentifier }): ICodeWindow { return new class implements ICodeWindow { - onLoad: Event = Event.None; - onReady: Event = Event.None; - onClose: Event = Event.None; - onDestroy: Event = Event.None; + onWillLoad: Event = Event.None; + onDidSignalReady: Event = Event.None; + onDidClose: Event = Event.None; + onDidDestroy: Event = Event.None; whenClosedOrLoaded: Promise = Promise.resolve(); id: number = -1; - win: Electron.BrowserWindow = undefined!; + win: Electron.BrowserWindow = null!; config: INativeWindowConfiguration | undefined; openedWorkspace = options.openedFolderUri ? { id: '', uri: options.openedFolderUri } : options.openedWorkspace; backupPath?: string | undefined; diff --git a/src/vs/platform/workspace/common/workspaceTrust.ts b/src/vs/platform/workspace/common/workspaceTrust.ts index 56f4fae82dc..629eddaf9a0 100644 --- a/src/vs/platform/workspace/common/workspaceTrust.ts +++ b/src/vs/platform/workspace/common/workspaceTrust.ts @@ -37,6 +37,11 @@ export interface IWorkspaceTrustModel { setFolderTrustState(folder: URI, trustState: WorkspaceTrustState): void; getFolderTrustState(folder: URI): WorkspaceTrustState; + + setTrustedFolders(folders: URI[]): void; + setUntrustedFolders(folders: URI[]): void; + + getTrustStateInfo(): IWorkspaceTrustStateInfo; } export interface IWorkspaceTrustRequest { diff --git a/src/vs/platform/workspaces/common/workspaces.ts b/src/vs/platform/workspaces/common/workspaces.ts index 66413f2e4e3..8be3cea72d7 100644 --- a/src/vs/platform/workspaces/common/workspaces.ts +++ b/src/vs/platform/workspaces/common/workspaces.ts @@ -45,7 +45,7 @@ export interface IWorkspacesService { getWorkspaceIdentifier(workspacePath: URI): Promise; // Workspaces History - readonly onRecentlyOpenedChange: Event; + readonly onDidChangeRecentlyOpened: Event; addRecentlyOpened(recents: IRecent[]): Promise; removeRecentlyOpened(workspaces: URI[]): Promise; clearRecentlyOpened(): Promise; @@ -116,6 +116,10 @@ export interface ISingleFolderWorkspaceIdentifier extends IBaseWorkspaceIdentifi uri: URI; } +export interface ISerializedSingleFolderWorkspaceIdentifier extends IBaseWorkspaceIdentifier { + uri: UriComponents; +} + export function isSingleFolderWorkspaceIdentifier(obj: unknown): obj is ISingleFolderWorkspaceIdentifier { const singleFolderIdentifier = obj as ISingleFolderWorkspaceIdentifier | undefined; @@ -133,6 +137,10 @@ export interface IWorkspaceIdentifier extends IBaseWorkspaceIdentifier { configPath: URI; } +export interface ISerializedWorkspaceIdentifier extends IBaseWorkspaceIdentifier { + configPath: UriComponents; +} + export function toWorkspaceIdentifier(workspace: IWorkspace): IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined { // Multi root @@ -161,13 +169,28 @@ export function isWorkspaceIdentifier(obj: unknown): obj is IWorkspaceIdentifier return typeof workspaceIdentifier?.id === 'string' && URI.isUri(workspaceIdentifier.configPath); } -export function reviveIdentifier(identifier: { id: string, uri?: UriComponents, configPath?: UriComponents } | undefined): IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined { - if (identifier?.uri) { - return { id: identifier.id, uri: URI.revive(identifier.uri) }; +export function reviveIdentifier(identifier: undefined): undefined; +export function reviveIdentifier(identifier: ISerializedWorkspaceIdentifier): IWorkspaceIdentifier; +export function reviveIdentifier(identifier: ISerializedSingleFolderWorkspaceIdentifier): ISingleFolderWorkspaceIdentifier; +export function reviveIdentifier(identifier: IEmptyWorkspaceIdentifier): IEmptyWorkspaceIdentifier; +export function reviveIdentifier(identifier: ISerializedWorkspaceIdentifier | ISerializedSingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined): IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined; +export function reviveIdentifier(identifier: ISerializedWorkspaceIdentifier | ISerializedSingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined): IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined { + + // Single Folder + const singleFolderIdentifierCandidate = identifier as ISerializedSingleFolderWorkspaceIdentifier | undefined; + if (singleFolderIdentifierCandidate?.uri) { + return { id: singleFolderIdentifierCandidate.id, uri: URI.revive(singleFolderIdentifierCandidate.uri) }; } - if (identifier?.configPath) { - return { id: identifier.id, configPath: URI.revive(identifier.configPath) }; + // Multi folder + const workspaceIdentifierCandidate = identifier as ISerializedWorkspaceIdentifier | undefined; + if (workspaceIdentifierCandidate?.configPath) { + return { id: workspaceIdentifierCandidate.id, configPath: URI.revive(workspaceIdentifierCandidate.configPath) }; + } + + // Empty + if (identifier?.id) { + return { id: identifier.id }; } return undefined; @@ -177,9 +200,9 @@ export function isUntitledWorkspace(path: URI, environmentService: IEnvironmentS return extUriBiasedIgnorePathCase.isEqualOrParent(path, environmentService.untitledWorkspacesHome); } -export interface IEmptyWorkspaceInitializationPayload extends IBaseWorkspaceIdentifier { } +export interface IEmptyWorkspaceIdentifier extends IBaseWorkspaceIdentifier { } -export type IWorkspaceInitializationPayload = IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceInitializationPayload; +export type IWorkspaceInitializationPayload = IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier; //#endregion diff --git a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts index bd16a20bbb8..22cec5f95ad 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts @@ -30,7 +30,7 @@ export interface IWorkspacesHistoryMainService { readonly _serviceBrand: undefined; - readonly onRecentlyOpenedChange: CommonEvent; + readonly onDidChangeRecentlyOpened: CommonEvent; addRecentlyOpened(recents: IRecent[]): void; getRecentlyOpened(include?: ICodeWindow): IRecentlyOpened; @@ -57,8 +57,8 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa declare readonly _serviceBrand: undefined; - private readonly _onRecentlyOpenedChange = this._register(new Emitter()); - readonly onRecentlyOpenedChange: CommonEvent = this._onRecentlyOpenedChange.event; + private readonly _onDidChangeRecentlyOpened = this._register(new Emitter()); + readonly onDidChangeRecentlyOpened: CommonEvent = this._onDidChangeRecentlyOpened.event; private readonly macOSRecentDocumentsUpdater = this._register(new ThrottledDelayer(800)); @@ -66,7 +66,7 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa @IStateService private readonly stateService: IStateService, @ILogService private readonly logService: ILogService, @IWorkspacesManagementMainService private readonly workspacesManagementMainService: IWorkspacesManagementMainService, - @IEnvironmentMainService private readonly environmentService: IEnvironmentMainService, + @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService ) { super(); @@ -80,7 +80,7 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => this.handleWindowsJumpList()); // Add to history when entering workspace - this._register(this.workspacesManagementMainService.onWorkspaceEntered(event => this.addRecentlyOpened([{ workspace: event.workspace }]))); + this._register(this.workspacesManagementMainService.onDidEnterWorkspace(event => this.addRecentlyOpened([{ workspace: event.workspace }]))); } private handleWindowsJumpList(): void { @@ -89,7 +89,7 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa } this.updateWindowsJumpList(); - this._register(this.onRecentlyOpenedChange(() => this.updateWindowsJumpList())); + this._register(this.onDidChangeRecentlyOpened(() => this.updateWindowsJumpList())); } addRecentlyOpened(recentToAdd: IRecent[]): void { @@ -139,7 +139,7 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa } this.saveRecentlyOpened({ workspaces, files }); - this._onRecentlyOpenedChange.fire(); + this._onDidChangeRecentlyOpened.fire(); // Schedule update to recent documents on macOS dock if (isMacintosh) { @@ -165,7 +165,7 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa if (workspaces.length !== mru.workspaces.length || files.length !== mru.files.length) { this.saveRecentlyOpened({ files, workspaces }); - this._onRecentlyOpenedChange.fire(); + this._onDidChangeRecentlyOpened.fire(); // Schedule update to recent documents on macOS dock if (isMacintosh) { @@ -238,7 +238,7 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa app.clearRecentDocuments(); // Event - this._onRecentlyOpenedChange.fire(); + this._onDidChangeRecentlyOpened.fire(); } getRecentlyOpened(include?: ICodeWindow): IRecentlyOpened { @@ -402,7 +402,7 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa } // Workspace: Untitled - if (extUriBiasedIgnorePathCase.isEqualOrParent(workspace.configPath, this.environmentService.userHome)) { + if (extUriBiasedIgnorePathCase.isEqualOrParent(workspace.configPath, this.environmentMainService.userHome)) { return { title: localize('untitledWorkspace', "Untitled (Workspace)"), description: '' }; } diff --git a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts index 5328a07dd5e..67a5e54db3f 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts @@ -50,7 +50,7 @@ export class WorkspacesMainService implements AddFirstParameterToFunctions { return this.workspacesHistoryMainService.getRecentlyOpened(this.windowsMainService.getWindowById(windowId)); diff --git a/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts index c9d22ba0af3..cb397ad60e8 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts @@ -38,8 +38,8 @@ export interface IWorkspacesManagementMainService { readonly _serviceBrand: undefined; - readonly onUntitledWorkspaceDeleted: Event; - readonly onWorkspaceEntered: Event; + readonly onDidDeleteUntitledWorkspace: Event; + readonly onDidEnterWorkspace: Event; enterWorkspace(intoWindow: ICodeWindow, openedWindows: ICodeWindow[], path: URI): Promise; @@ -65,16 +65,16 @@ export class WorkspacesManagementMainService extends Disposable implements IWork declare readonly _serviceBrand: undefined; - private readonly untitledWorkspacesHome = this.environmentService.untitledWorkspacesHome; // local URI that contains all untitled workspaces + private readonly untitledWorkspacesHome = this.environmentMainService.untitledWorkspacesHome; // local URI that contains all untitled workspaces - private readonly _onUntitledWorkspaceDeleted = this._register(new Emitter()); - readonly onUntitledWorkspaceDeleted: Event = this._onUntitledWorkspaceDeleted.event; + private readonly _onDidDeleteUntitledWorkspace = this._register(new Emitter()); + readonly onDidDeleteUntitledWorkspace: Event = this._onDidDeleteUntitledWorkspace.event; - private readonly _onWorkspaceEntered = this._register(new Emitter()); - readonly onWorkspaceEntered: Event = this._onWorkspaceEntered.event; + private readonly _onDidEnterWorkspace = this._register(new Emitter()); + readonly onDidEnterWorkspace: Event = this._onDidEnterWorkspace.event; constructor( - @IEnvironmentMainService private readonly environmentService: IEnvironmentMainService, + @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @ILogService private readonly logService: ILogService, @IBackupMainService private readonly backupMainService: IBackupMainService, @IDialogMainService private readonly dialogMainService: IDialogMainService @@ -101,7 +101,7 @@ export class WorkspacesManagementMainService extends Disposable implements IWork } private isWorkspacePath(uri: URI): boolean { - return isUntitledWorkspace(uri, this.environmentService) || hasWorkspaceFileExtension(uri); + return isUntitledWorkspace(uri, this.environmentMainService) || hasWorkspaceFileExtension(uri); } private doResolveWorkspace(path: URI, contents: string): IResolvedWorkspace | null { @@ -186,7 +186,7 @@ export class WorkspacesManagementMainService extends Disposable implements IWork } isUntitledWorkspace(workspace: IWorkspaceIdentifier): boolean { - return isUntitledWorkspace(workspace.configPath, this.environmentService); + return isUntitledWorkspace(workspace.configPath, this.environmentMainService); } deleteUntitledWorkspaceSync(workspace: IWorkspaceIdentifier): void { @@ -198,7 +198,7 @@ export class WorkspacesManagementMainService extends Disposable implements IWork this.doDeleteUntitledWorkspaceSync(workspace); // Event - this._onUntitledWorkspaceDeleted.fire(workspace); + this._onDidDeleteUntitledWorkspace.fire(workspace); } async deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise { @@ -213,7 +213,7 @@ export class WorkspacesManagementMainService extends Disposable implements IWork rimrafSync(dirname(configPath)); // Mark Workspace Storage to be deleted - const workspaceStoragePath = join(this.environmentService.workspaceStorageHome.fsPath, workspace.id); + const workspaceStoragePath = join(this.environmentMainService.workspaceStorageHome.fsPath, workspace.id); if (existsSync(workspaceStoragePath)) { writeFileSync(join(workspaceStoragePath, 'obsolete'), ''); } @@ -260,7 +260,7 @@ export class WorkspacesManagementMainService extends Disposable implements IWork } // Emit as event - this._onWorkspaceEntered.fire({ window, workspace: result.workspace }); + this._onDidEnterWorkspace.fire({ window, workspace: result.workspace }); return result; } diff --git a/src/vs/platform/workspaces/test/common/workspaces.test.ts b/src/vs/platform/workspaces/test/common/workspaces.test.ts index 270eaee4899..cb8c60e8668 100644 --- a/src/vs/platform/workspaces/test/common/workspaces.test.ts +++ b/src/vs/platform/workspaces/test/common/workspaces.test.ts @@ -5,10 +5,25 @@ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; -import { hasWorkspaceFileExtension, toWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { hasWorkspaceFileExtension, toWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, ISerializedWorkspaceIdentifier, reviveIdentifier, ISerializedSingleFolderWorkspaceIdentifier, IEmptyWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; suite('Workspaces', () => { + test('reviveIdentifier', () => { + let serializedWorkspaceIdentifier: ISerializedWorkspaceIdentifier = { id: 'id', configPath: URI.file('foo').toJSON() }; + assert.strictEqual(isWorkspaceIdentifier(reviveIdentifier(serializedWorkspaceIdentifier)), true); + + let serializedSingleFolderWorkspaceIdentifier: ISerializedSingleFolderWorkspaceIdentifier = { id: 'id', uri: URI.file('foo').toJSON() }; + assert.strictEqual(isSingleFolderWorkspaceIdentifier(reviveIdentifier(serializedSingleFolderWorkspaceIdentifier)), true); + + let serializedEmptyWorkspaceIdentifier: IEmptyWorkspaceIdentifier = { id: 'id' }; + assert.strictEqual(reviveIdentifier(serializedEmptyWorkspaceIdentifier).id, serializedEmptyWorkspaceIdentifier.id); + assert.strictEqual(isWorkspaceIdentifier(serializedEmptyWorkspaceIdentifier), false); + assert.strictEqual(isSingleFolderWorkspaceIdentifier(serializedEmptyWorkspaceIdentifier), false); + + assert.strictEqual(reviveIdentifier(undefined), undefined); + }); + test('hasWorkspaceFileExtension', () => { assert.strictEqual(hasWorkspaceFileExtension('something'), false); assert.strictEqual(hasWorkspaceFileExtension('something.code-workspace'), true); diff --git a/src/vs/platform/workspaces/test/electron-main/workspacesManagementMainService.test.ts b/src/vs/platform/workspaces/test/electron-main/workspacesManagementMainService.test.ts index fc4b803c38a..55ad732b8b3 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspacesManagementMainService.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspacesManagementMainService.test.ts @@ -81,14 +81,14 @@ suite('WorkspacesManagementMainService', () => { let testDir: string; let untitledWorkspacesHomePath: string; - let environmentService: EnvironmentMainService; + let environmentMainService: EnvironmentMainService; let service: WorkspacesManagementMainService; setup(async () => { testDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'workspacesmanagementmainservice'); untitledWorkspacesHomePath = path.join(testDir, 'Workspaces'); - environmentService = new class TestEnvironmentService extends EnvironmentMainService { + environmentMainService = new class TestEnvironmentService extends EnvironmentMainService { constructor() { super(parseArgs(process.argv, OPTIONS)); } @@ -97,7 +97,7 @@ suite('WorkspacesManagementMainService', () => { } }; - service = new WorkspacesManagementMainService(environmentService, new NullLogService(), new TestBackupMainService(), new TestDialogMainService()); + service = new WorkspacesManagementMainService(environmentMainService, new NullLogService(), new TestBackupMainService(), new TestDialogMainService()); return fs.promises.mkdir(untitledWorkspacesHomePath, { recursive: true }); }); diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index febf42232e6..ce9a5668450 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -64,29 +64,25 @@ declare module 'vscode' { readonly onDidChangeSessions: Event; /** - * Returns an array of current sessions. - * - * TODO @RMacfarlane finish deprecating this and remove it + * Get a list of sessions. + * @param scopes An optional list of scopes. If provided, the sessions returned should match + * these permissions, otherwise all sessions should be returned. + * @returns A promise that resolves to an array of authentication sessions. */ // eslint-disable-next-line vscode-dts-provider-naming - getAllSessions(): Thenable>; - - - /** - * Returns an array of current sessions. - */ - // eslint-disable-next-line vscode-dts-provider-naming - getSessions(scopes: string[]): Thenable>; + getSessions(scopes?: string[]): Thenable>; /** * Prompts a user to login. + * @param scopes A list of scopes, permissions, that the new session should be created with. + * @returns A promise that resolves to an authentication session. */ // eslint-disable-next-line vscode-dts-provider-naming createSession(scopes: string[]): Thenable; /** * Removes the session corresponding to session id. - * @param sessionId The session id to log out of + * @param sessionId The id of the session to remove. */ // eslint-disable-next-line vscode-dts-provider-naming removeSession(sessionId: string): Thenable; @@ -208,6 +204,12 @@ declare module 'vscode' { elevationRequired?: boolean; } + export enum CandidatePortSource { + None = 0, + Process = 1, + Output = 2 + } + export type ResolverResult = ResolvedAuthority & ResolvedOptions & TunnelInformation; export class RemoteAuthorityResolverError extends Error { @@ -242,6 +244,8 @@ declare module 'vscode' { elevation: boolean; public: boolean; }; + + candidatePortSource?: CandidatePortSource; } export namespace workspace { @@ -738,8 +742,149 @@ declare module 'vscode' { //#endregion + //#region inline value provider: https://github.com/microsoft/vscode/issues/105690 + + /** + * The inline values provider interface defines the contract between extensions and the VS Code debugger inline values feature. + * In this contract the provider returns inline value information for a given document range + * and VS Code shows this information in the editor at the end of lines. + */ + export interface InlineValuesProvider { + + /** + * An optional event to signal that inline values have changed. + * @see [EventEmitter](#EventEmitter) + */ + onDidChangeInlineValues?: Event | undefined; + + /** + * Provide "inline value" information for a given document and range. + * VS Code calls this method whenever debugging stops in the given document. + * The returned inline values information is rendered in the editor at the end of lines. + * + * @param document The document for which the inline values information is needed. + * @param viewPort The visible document range for which inline values should be computed. + * @param context A bag containing contextual information like the current location. + * @param token A cancellation token. + * @return An array of InlineValueDescriptors or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideInlineValues(document: TextDocument, viewPort: Range, context: InlineValueContext, token: CancellationToken): ProviderResult; + } + + /** + * An open ended information bag passed to the inline value provider. + * A minimal context containes just the document location where the debugger has stopped. + * Additional optional information might be scope information or variables and their values. + */ + export interface InlineValueContext { + /** + * The document range where execution has stopped. + * Typically the end position of the range denotes the line where the inline values are shown. + */ + stoppedLocation: Range; + + // ... more to come, e.g. Scope information or variable/value candidate information + } + + /** + * Inline value information can be provided by different means: + * - directly as a text value (class InlineValueText). + * - as a name to use for a variable lookup (class InlineValueVariableLookup) + * - as an evaluatable expression (class InlineValueEvaluatableExpression) + * The InlineValue types combines all inline value types into one type. + */ + export type InlineValue = InlineValueText | InlineValueVariableLookup | InlineValueEvaluatableExpression; + + /** + * Provide inline value as text. + */ + export class InlineValueText { + /** + * The text of the inline value. + */ + readonly text: string; + /** + * The range of the inline value. + */ + readonly range: Range; + /** + * Creates a new InlineValueText object. + * + * @param text The value to be shown for the line. + * @param range The document line where to show the inline value. + */ + constructor(text: string, range: Range); + } + + /** + * Provide inline value through a variable lookup. + */ + export class InlineValueVariableLookup { + /** + * The name of the variable to look up. + */ + readonly variableName: string; + /** + * How to perform the lookup. + */ + readonly caseSensitiveLookup: boolean; + /** + * The range of the inline value. + */ + readonly range: Range; + /** + * Creates a new InlineValueVariableLookup object. + * + * @param variableName The name of the variable to look up. + * @param range The document line where to show the inline value. + * @param caseSensitiveLookup How to perform the lookup. If missing lookup is case sensitive. + */ + constructor(variableName: string, range: Range, caseSensitiveLookup?: boolean); + } + + /** + * Provide inline value through an expression evaluation. + */ + export class InlineValueEvaluatableExpression { + /** + * The expression to evaluate. + */ + readonly expression: string; + /** + * The range of the inline value. + */ + readonly range: Range; + /** + * Creates a new InlineValueEvaluatableExpression object. + * + * @param expression The expression to evaluate. + * @param range The document line where to show the inline value. + */ + constructor(expression: string, range: Range); + } + + export namespace languages { + + /** + * Register a provider that returns inline values for text documents. + * If debugging has stopped VS Code shows inline values in the editor at the end of lines. + * + * Multiple providers can be registered for a language. In that case providers are asked in + * parallel and the results are merged. A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider An inline values provider. + * @return A [disposable](#Disposable) that unregisters this provider when being disposed. + */ + export function registerInlineValuesProvider(selector: DocumentSelector, provider: InlineValuesProvider): Disposable; + } + + //#endregion + // eslint-disable-next-line vscode-dts-region-comments - //#region debug + //#region @weinand: variables view action contributions /** * A DebugProtocolVariableContainer is an opaque stand-in type for the intersection of the Scope and Variable types defined in the Debug Adapter Protocol. @@ -1017,34 +1162,44 @@ declare module 'vscode' { Idle = 2 } - // TODO@API - // make this a class, allow modified using with-pattern - export interface NotebookCellMetadata { + export class NotebookCellMetadata { /** * Controls whether a cell's editor is editable/readonly. */ - editable?: boolean; - + readonly editable?: boolean; /** * Controls if the cell has a margin to support the breakpoint UI. * This metadata is ignored for markdown cell. */ - breakpointMargin?: boolean; - + readonly breakpointMargin?: boolean; /** * Whether a code cell's editor is collapsed */ - inputCollapsed?: boolean; - + readonly outputCollapsed?: boolean; /** * Whether a code cell's outputs are collapsed */ - outputCollapsed?: boolean; - + readonly inputCollapsed?: boolean; /** * Additional attributes of a cell metadata. */ - custom?: { [key: string]: any; }; + readonly custom?: Record; + + // todo@API duplicates status bar API + readonly statusMessage?: string; + + // run related API, will be removed + readonly runnable?: boolean; + readonly hasExecutionOrder?: boolean; + readonly executionOrder?: number; + readonly runState?: NotebookCellRunState; + readonly runStartTime?: number; + readonly lastRunDuration?: number; + + constructor(editable?: boolean, breakpointMargin?: boolean, runnable?: boolean, hasExecutionOrder?: boolean, executionOrder?: number, runState?: NotebookCellRunState, runStartTime?: number, statusMessage?: string, lastRunDuration?: number, inputCollapsed?: boolean, outputCollapsed?: boolean, custom?: Record) + + // todo@API write a proper signature + with(change: Partial>): NotebookCellMetadata; } // todo@API support ids https://github.com/jupyter/enhancement-proposals/blob/master/62-cell-id/cell-id.md @@ -1056,39 +1211,48 @@ declare module 'vscode' { readonly document: TextDocument; readonly language: string; readonly outputs: readonly NotebookCellOutput[]; - readonly metadata: NotebookCellMetadata; - /** @deprecated use WorkspaceEdit.replaceCellOutput */ - // outputs: CellOutput[]; - // readonly outputs2: NotebookCellOutput[]; - /** @deprecated use WorkspaceEdit.replaceCellMetadata */ - // metadata: NotebookCellMetadata; + readonly metadata: NotebookCellMetadata } + export class NotebookDocumentMetadata { - export interface NotebookDocumentMetadata { /** * Controls if users can add or delete cells * Defaults to true */ - editable?: boolean; - + readonly editable: boolean; /** * Default value for [cell editable metadata](#NotebookCellMetadata.editable). * Defaults to true. */ - cellEditable?: boolean; - displayOrder?: GlobPattern[]; - + readonly cellEditable: boolean; /** * Additional attributes of the document metadata. */ - custom?: { [key: string]: any; }; - + readonly custom: { [key: string]: any; }; /** * Whether the document is trusted, default to true * When false, insecure outputs like HTML, JavaScript, SVG will not be rendered. */ - trusted?: boolean; + readonly trusted: boolean; + + // todo@API how does glob apply to mime times? + readonly displayOrder: GlobPattern[]; + + // todo@API is this a kernel property? + readonly cellHasExecutionOrder: boolean; + + // run related, remove infer from kernel, exec + // todo@API infer from kernel + // todo@API remove + readonly runnable: boolean; + readonly cellRunnable: boolean; + readonly runState: NotebookRunState; + + constructor(editable?: boolean, runnable?: boolean, cellEditable?: boolean, cellRunnable?: boolean, cellHasExecutionOrder?: boolean, displayOrder?: GlobPattern[], custom?: { [key: string]: any; }, runState?: NotebookRunState, trusted?: boolean); + + // TODO@API make this a proper signature + with(change: Partial>): NotebookDocumentMetadata; } export interface NotebookDocumentContentOptions { @@ -1108,7 +1272,9 @@ declare module 'vscode' { export interface NotebookDocument { readonly uri: Uri; readonly version: number; + // todo@API don't have this... readonly fileName: string; + // todo@API should we really expose this? readonly viewType: string; readonly isDirty: boolean; readonly isUntitled: boolean; @@ -1118,13 +1284,14 @@ declare module 'vscode' { } // todo@API maybe have a NotebookCellPosition sibling - // todo@API should be a class - export interface NotebookCellRange { + export class NotebookCellRange { readonly start: number; /** * exclusive */ readonly end: number; + + constructor(start: number, end: number); } export enum NotebookEditorRevealType { @@ -1309,6 +1476,8 @@ declare module 'vscode' { export namespace notebook { + // todo@API should we really support to pass the viewType? We do NOT support + // to open the same file with different viewTypes at the same time export function openNotebookDocument(uri: Uri, viewType?: string): Thenable; export const onDidOpenNotebookDocument: Event; export const onDidCloseNotebookDocument: Event; @@ -1323,6 +1492,9 @@ declare module 'vscode' { export const onDidChangeNotebookDocumentMetadata: Event; export const onDidChangeNotebookCells: Event; export const onDidChangeCellOutputs: Event; + + // todo@API we send document close and open events when the language of a document changes and + // I believe we should stick that for cells as well export const onDidChangeCellLanguage: Event; export const onDidChangeCellMetadata: Event; } @@ -1334,6 +1506,7 @@ declare module 'vscode' { export const onDidChangeActiveNotebookEditor: Event; export const onDidChangeNotebookEditorSelection: Event; export const onDidChangeNotebookEditorVisibleRanges: Event; + // TODO@API add overload for just a URI export function showNotebookDocument(document: NotebookDocument, options?: NotebookDocumentShowOptions): Thenable; } @@ -1353,9 +1526,9 @@ declare module 'vscode' { readonly mime: string; readonly value: unknown; - readonly metadata?: Record; + readonly metadata?: Record; - constructor(mime: string, value: unknown, metadata?: Record); + constructor(mime: string, value: unknown, metadata?: Record); } // @jrieken @@ -1363,7 +1536,11 @@ declare module 'vscode' { export class NotebookCellOutput { readonly id: string; readonly outputs: NotebookCellOutputItem[]; - constructor(outputs: NotebookCellOutputItem[], id?: string); + readonly metadata?: Record; + + constructor(outputs: NotebookCellOutputItem[], metadata?: Record); + + constructor(outputs: NotebookCellOutputItem[], id: string, metadata?: Record); } //#endregion @@ -1482,34 +1659,6 @@ declare module 'vscode' { //#region https://github.com/microsoft/vscode/issues/106744, NotebookKernel - export interface NotebookDocumentMetadata { - - /** - * Controls whether the full notebook can be run at once. - * Defaults to true - */ - // todo@API infer from kernel - // todo@API remove - runnable?: boolean; - - /** - * Default value for [cell runnable metadata](#NotebookCellMetadata.runnable). - * Defaults to true. - */ - cellRunnable?: boolean; - - /** - * Default value for [cell hasExecutionOrder metadata](#NotebookCellMetadata.hasExecutionOrder). - * Defaults to true. - */ - cellHasExecutionOrder?: boolean; - - /** - * The document's current run state - */ - runState?: NotebookRunState; - } - // todo@API use the NotebookCellExecution-object as a container to model and enforce // the flow of a cell execution @@ -1531,49 +1680,6 @@ declare module 'vscode' { // export const onDidStartNotebookCellExecution: Event; // export const onDidStopNotebookCellExecution: Event; - export interface NotebookCellMetadata { - - /** - * Controls if the cell is executable. - * This metadata is ignored for markdown cell. - */ - // todo@API infer from kernel - runnable?: boolean; - - /** - * Whether the [execution order](#NotebookCellMetadata.executionOrder) indicator will be displayed. - * Defaults to true. - */ - hasExecutionOrder?: boolean; - - /** - * The order in which this cell was executed. - */ - executionOrder?: number; - - /** - * A status message to be shown in the cell's status bar - */ - // todo@API duplicates status bar API - statusMessage?: string; - - /** - * The cell's current run state - */ - runState?: NotebookCellRunState; - - /** - * If the cell is running, the time at which the cell started running - */ - runStartTime?: number; - - /** - * The total duration of the cell's last run - */ - // todo@API depends on having output - lastRunDuration?: number; - } - export interface NotebookKernel { readonly id?: string; label: string; diff --git a/src/vs/workbench/api/browser/mainThreadAuthentication.ts b/src/vs/workbench/api/browser/mainThreadAuthentication.ts index dfad0388642..64f7d14880f 100644 --- a/src/vs/workbench/api/browser/mainThreadAuthentication.ts +++ b/src/vs/workbench/api/browser/mainThreadAuthentication.ts @@ -89,14 +89,10 @@ export class MainThreadAuthenticationProvider extends Disposable { } } - async getSessions(scopes: string[]) { + async getSessions(scopes?: string[]) { return this._proxy.$getSessions(this.id, scopes); } - async getAllSessions(): Promise> { - return this._proxy.$getAllSessions(this.id); - } - createSession(scopes: string[]): Promise { return this._proxy.$createSession(this.id, scopes); } diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 04d6dfed752..fd83c6e81a0 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -250,6 +250,31 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha })); } + // --- inline values + + $registerInlineValuesProvider(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void { + const provider = { + provideInlineValues: (model: ITextModel, viewPort: EditorRange, context: modes.InlineValueContext, token: CancellationToken): Promise => { + return this._proxy.$provideInlineValues(handle, model.uri, viewPort, context, token); + } + }; + + if (typeof eventHandle === 'number') { + const emitter = new Emitter(); + this._registrations.set(eventHandle, emitter); + provider.onDidChangeInlineValues = emitter.event; + } + + this._registrations.set(handle, modes.InlineValuesProviderRegistry.register(selector, provider)); + } + + $emitInlineValuesEvent(eventHandle: number, event?: any): void { + const obj = this._registrations.get(eventHandle); + if (obj instanceof Emitter) { + obj.fire(event); + } + } + // --- occurrences $registerDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void { diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index 812b2bed3f4..ef5198afafe 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -10,7 +10,8 @@ import { Emitter } from 'vs/base/common/event'; import { IRelativePattern } from 'vs/base/common/glob'; import { combinedDisposable, Disposable, DisposableStore, dispose, IDisposable, IReference } from 'vs/base/common/lifecycle'; import { ResourceMap } from 'vs/base/common/map'; -import { IExtUri } from 'vs/base/common/resources'; +import { Schemas } from 'vs/base/common/network'; +import { IExtUri, isEqual } from 'vs/base/common/resources'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -30,6 +31,7 @@ import { IEditorGroup, IEditorGroupsService, preferredSideBySideGroupDirection } import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith'; import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookCellStatusBarEntryDto, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookModelAddedData, MainContext, MainThreadNotebookShape, NotebookEditorRevealType, NotebookExtensionDescription } from '../common/extHost.protocol'; class DocumentAndEditorState { @@ -121,6 +123,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo constructor( extHostContext: IExtHostContext, + @IWorkingCopyService private readonly _workingCopyService: IWorkingCopyService, @INotebookService private _notebookService: INotebookService, @IConfigurationService private readonly _configurationService: IConfigurationService, @IEditorService private readonly _editorService: IEditorService, @@ -202,6 +205,22 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo } registerListeners() { + + // forward changes to dirty state + // todo@bpasero this seem way too complicated... is there an easy way to + // the actual resource from a working copy? + this._register(this._workingCopyService.onDidChangeDirty(e => { + if (e.resource.scheme !== Schemas.vscodeNotebook) { + return; + } + for (const notebook of this._notebookService.getNotebookTextModels()) { + if (isEqual(notebook.uri.with({ scheme: Schemas.vscodeNotebook }), e.resource)) { + this._proxy.$acceptDirtyStateChanged(notebook.uri, e.isDirty()); + break; + } + } + })); + this._notebookService.listNotebookEditors().forEach((e) => { this._addNotebookEditor(e); }); @@ -461,7 +480,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo }, viewOptions: options.viewOptions, reloadNotebook: async (mainthreadTextModel: NotebookTextModel) => { - const data = await this._proxy.$resolveNotebookData(viewType, mainthreadTextModel.uri); + const data = await this._proxy.$openNotebook(viewType, mainthreadTextModel.uri); mainthreadTextModel.metadata = data.metadata; mainthreadTextModel.transientOptions = contentOptions; @@ -475,8 +494,8 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo }); }); }, - resolveNotebookDocument: async (viewType: string, uri: URI, backupId?: string) => { - const data = await this._proxy.$resolveNotebookData(viewType, uri, backupId); + openNotebook: async (viewType: string, uri: URI, backupId?: string) => { + const data = await this._proxy.$openNotebook(viewType, uri, backupId); return { data, transientOptions: contentOptions @@ -501,7 +520,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo const disposable = this._notebookService.registerNotebookController(viewType, extension, controller); this._notebookProviders.set(viewType, { controller, disposable }); - return; } async $updateNotebookProviderOptions(viewType: string, options?: { transientOutputs: boolean; transientMetadata: TransientMetadata; }): Promise { @@ -538,7 +556,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo const result: INotebookKernel[] = []; const kernelsDto = await that._proxy.$provideNotebookKernels(handle, uri, token); for (const dto of kernelsDto) { - result.push({ id: dto.id, friendlyId: dto.friendlyId, diff --git a/src/vs/workbench/api/browser/mainThreadTesting.ts b/src/vs/workbench/api/browser/mainThreadTesting.ts index 95fc3e9ae7f..110becb9583 100644 --- a/src/vs/workbench/api/browser/mainThreadTesting.ts +++ b/src/vs/workbench/api/browser/mainThreadTesting.ts @@ -6,6 +6,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; +import { Range } from 'vs/editor/common/core/range'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { getTestSubscriptionKey, ITestState, RunTestsRequest, TestDiffOpType, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection'; import { ITestResultService, LiveTestResult } from 'vs/workbench/contrib/testing/common/testResultService'; @@ -18,6 +19,7 @@ const reviveDiff = (diff: TestsDiff) => { const item = entry[1]; if (item.item.location) { item.item.location.uri = URI.revive(item.item.location.uri); + item.item.location.range = Range.lift(item.item.location.range); } } } @@ -71,6 +73,7 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh for (const message of state.messages) { if (message.location) { message.location.uri = URI.revive(message.location.uri); + message.location.range = Range.lift(message.location.range); } } diff --git a/src/vs/workbench/api/browser/mainThreadTreeViews.ts b/src/vs/workbench/api/browser/mainThreadTreeViews.ts index cfe9d842e8d..40b6794bfae 100644 --- a/src/vs/workbench/api/browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/browser/mainThreadTreeViews.ts @@ -31,7 +31,7 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTreeViews); } - $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean }): void { + async $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean }): Promise { this.logService.trace('MainThreadTreeViews#$registerTreeViewDataProvider', treeViewId, options); this.extensionService.whenInstalledExtensionsRegistered().then(() => { diff --git a/src/vs/workbench/api/browser/mainThreadTunnelService.ts b/src/vs/workbench/api/browser/mainThreadTunnelService.ts index bc7c653b981..052f5a24516 100644 --- a/src/vs/workbench/api/browser/mainThreadTunnelService.ts +++ b/src/vs/workbench/api/browser/mainThreadTunnelService.ts @@ -4,17 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { MainThreadTunnelServiceShape, IExtHostContext, MainContext, ExtHostContext, ExtHostTunnelServiceShape } from 'vs/workbench/api/common/extHost.protocol'; +import { MainThreadTunnelServiceShape, IExtHostContext, MainContext, ExtHostContext, ExtHostTunnelServiceShape, CandidatePortSource } from 'vs/workbench/api/common/extHost.protocol'; import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; -import { CandidatePort, IRemoteExplorerService, makeAddress } from 'vs/workbench/services/remote/common/remoteExplorerService'; +import { CandidatePort, IRemoteExplorerService, makeAddress, PORT_AUTO_FORWARD_SETTING, PORT_AUTO_SOURCE_SETTING, PORT_AUTO_SOURCE_SETTING_OUTPUT } from 'vs/workbench/services/remote/common/remoteExplorerService'; import { ITunnelProvider, ITunnelService, TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions, RemoteTunnel, isPortPrivileged } from 'vs/platform/remote/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'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { PORT_AUTO_FORWARD_SETTING } from 'vs/workbench/contrib/remote/browser/tunnelView'; +import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService } from 'vs/platform/log/common/log'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; @extHostNamedCustomer(MainContext.MainThreadTunnelService) export class MainThreadTunnelService extends Disposable implements MainThreadTunnelServiceShape { @@ -27,7 +27,8 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun @ITunnelService private readonly tunnelService: ITunnelService, @INotificationService private readonly notificationService: INotificationService, @IConfigurationService private readonly configurationService: IConfigurationService, - @ILogService private readonly logService: ILogService + @ILogService private readonly logService: ILogService, + @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService ) { super(); this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTunnelService); @@ -132,6 +133,32 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun }); } + async $setCandidatePortSource(source: CandidatePortSource): Promise { + // Must wait for the remote environment before trying to set settings there. + this.remoteAgentService.getEnvironment().then(() => { + switch (source) { + case CandidatePortSource.None: { + const autoDetectionEnablement = this.configurationService.inspect(PORT_AUTO_FORWARD_SETTING); + if (autoDetectionEnablement.userRemote === undefined) { + // Only update the remote setting if the user hasn't already set it. + this.configurationService.updateValue(PORT_AUTO_FORWARD_SETTING, false, ConfigurationTarget.USER_REMOTE); + } + break; + } + case CandidatePortSource.Output: { + const candidatePortSourceSetting = this.configurationService.inspect(PORT_AUTO_SOURCE_SETTING); + if (candidatePortSourceSetting.userRemote === undefined) { + // Only update the remote setting if the user hasn't already set it. + this.configurationService.updateValue(PORT_AUTO_SOURCE_SETTING, PORT_AUTO_SOURCE_SETTING_OUTPUT, ConfigurationTarget.USER_REMOTE); + } + break; + } + default: // Do nothing, the defaults for these settings should be used. + } + }).catch(() => { + // The remote failed to get setup. Errors from that area will already be surfaced to the user. + }); + } dispose(): void { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 09a44ef2b07..155c978701a 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -15,7 +15,7 @@ import { OverviewRulerLane } from 'vs/editor/common/model'; import * as languageConfiguration from 'vs/editor/common/modes/languageConfiguration'; import { score } from 'vs/editor/common/modes/languageSelector'; import * as files from 'vs/platform/files/common/files'; -import { ExtHostContext, MainContext, ExtHostLogServiceShape, UIKind } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostContext, MainContext, ExtHostLogServiceShape, UIKind, CandidatePortSource } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostApiCommands } from 'vs/workbench/api/common/extHostApiCommands'; import { ExtHostClipboard } from 'vs/workbench/api/common/extHostClipboard'; import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; @@ -198,10 +198,11 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I } else if (typeof selector === 'string') { informOnce(selector); } else { - if (typeof selector.scheme === 'undefined') { + const filter = selector as vscode.DocumentFilter; // TODO: microsoft/TypeScript#42768 + if (typeof filter.scheme === 'undefined') { informOnce(selector); } - if (!extension.enableProposedApi && typeof selector.exclusive === 'boolean') { + if (!extension.enableProposedApi && typeof filter.exclusive === 'boolean') { throwProposedApiError(extension); } } @@ -409,6 +410,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I registerEvaluatableExpressionProvider(selector: vscode.DocumentSelector, provider: vscode.EvaluatableExpressionProvider): vscode.Disposable { return extHostLanguageFeatures.registerEvaluatableExpressionProvider(extension, checkSelector(selector), provider, extension.identifier); }, + registerInlineValuesProvider(selector: vscode.DocumentSelector, provider: vscode.InlineValuesProvider): vscode.Disposable { + checkProposedApiEnabled(extension); + return extHostLanguageFeatures.registerInlineValuesProvider(extension, checkSelector(selector), provider, extension.identifier); + }, registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable { return extHostLanguageFeatures.registerDocumentHighlightProvider(extension, checkSelector(selector), provider); }, @@ -1129,6 +1134,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I CallHierarchyOutgoingCall: extHostTypes.CallHierarchyOutgoingCall, CancellationError: errors.CancellationError, CancellationTokenSource: CancellationTokenSource, + CandidatePortSource: CandidatePortSource, CodeAction: extHostTypes.CodeAction, CodeActionKind: extHostTypes.CodeActionKind, CodeActionTrigger: extHostTypes.CodeActionTrigger, @@ -1165,6 +1171,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I EndOfLine: extHostTypes.EndOfLine, EnvironmentVariableMutatorType: extHostTypes.EnvironmentVariableMutatorType, EvaluatableExpression: extHostTypes.EvaluatableExpression, + InlineValueText: extHostTypes.InlineValueText, + InlineValueVariableLookup: extHostTypes.InlineValueVariableLookup, + InlineValueEvaluatableExpression: extHostTypes.InlineValueEvaluatableExpression, EventEmitter: Emitter, ExtensionKind: extHostTypes.ExtensionKind, ExtensionMode: extHostTypes.ExtensionMode, @@ -1253,6 +1262,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I // checkProposedApiEnabled(extension); return extHostTypes.TimelineItem; }, + get NotebookCellRange() { + return extHostTypes.NotebookCellRange; + }, get NotebookCellKind() { // checkProposedApiEnabled(extension); return extHostTypes.NotebookCellKind; @@ -1261,6 +1273,12 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I // checkProposedApiEnabled(extension); return extHostTypes.NotebookCellRunState; }, + get NotebookDocumentMetadata() { + return extHostTypes.NotebookDocumentMetadata; + }, + get NotebookCellMetadata() { + return extHostTypes.NotebookCellMetadata; + }, get NotebookRunState() { // checkProposedApiEnabled(extension); return extHostTypes.NotebookRunState; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 9e121b2c822..2e330fd4ee5 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -285,7 +285,7 @@ export interface MainThreadTextEditorsShape extends IDisposable { } export interface MainThreadTreeViewsShape extends IDisposable { - $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean; }): void; + $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean; }): Promise; $refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem; }): Promise; $reveal(treeViewId: string, itemInfo: { item: ITreeItem, parentChain: ITreeItem[] } | undefined, options: IRevealOptions): Promise; $setMessage(treeViewId: string, message: string): void; @@ -378,6 +378,8 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { $registerTypeDefinitionSupport(handle: number, selector: IDocumentFilterDto[]): void; $registerHoverProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerEvaluatableExpressionProvider(handle: number, selector: IDocumentFilterDto[]): void; + $registerInlineValuesProvider(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void; + $emitInlineValuesEvent(eventHandle: number, event?: any): void; $registerDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerLinkedEditingRangeProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerReferenceSupport(handle: number, selector: IDocumentFilterDto[]): void; @@ -990,6 +992,12 @@ export interface MainThreadWindowShape extends IDisposable { $asExternalUri(uri: UriComponents, options: IOpenUriOptions): Promise; } +export enum CandidatePortSource { + None = 0, + Process = 1, + Output = 2 +} + export interface MainThreadTunnelServiceShape extends IDisposable { $openTunnel(tunnelOptions: TunnelOptions, source: string | undefined): Promise; $closeTunnel(remote: { host: string, port: number }): Promise; @@ -998,6 +1006,7 @@ export interface MainThreadTunnelServiceShape extends IDisposable { $setRemoteTunnelService(processId: number): Promise; $setCandidateFilter(): Promise; $onFoundNewCandidates(candidates: { host: string, port: number, detail: string }[]): Promise; + $setCandidatePortSource(source: CandidatePortSource): Promise; } export interface MainThreadTimelineShape extends IDisposable { @@ -1125,8 +1134,7 @@ export interface ExtHostLabelServiceShape { } export interface ExtHostAuthenticationShape { - $getAllSessions(id: string): Promise>; - $getSessions(id: string, scopes: string[]): Promise>; + $getSessions(id: string, scopes?: string[]): Promise>; $createSession(id: string, scopes: string[]): Promise; $removeSession(id: string, sessionId: string): Promise; $onDidChangeAuthenticationSessions(id: string, label: string, event: modes.AuthenticationSessionsChangeEvent): Promise; @@ -1467,6 +1475,10 @@ export interface ILinkedEditingRangesDto { wordPattern?: IRegExpDto; } +export interface IInlineValueContextDto { + stoppedLocation: IRange; +} + export interface ExtHostLanguageFeaturesShape { $provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise; $provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise; @@ -1478,6 +1490,7 @@ export interface ExtHostLanguageFeaturesShape { $provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; $provideHover(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; $provideEvaluatableExpression(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; + $provideInlineValues(handle: number, resource: UriComponents, range: IRange, context: modes.InlineValueContext, token: CancellationToken): Promise; $provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; $provideLinkedEditingRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; $provideReferences(handle: number, resource: UriComponents, position: IPosition, context: modes.ReferenceContext, token: CancellationToken): Promise; @@ -1732,13 +1745,8 @@ export interface INotebookSelectionChangeEvent { selections: number[]; } -export interface INotebookCellVisibleRange { - start: number; - end: number; -} - export interface INotebookVisibleRangesEvent { - ranges: INotebookCellVisibleRange[]; + ranges: ICellRange[]; } export interface INotebookEditorPropertiesChangeData { @@ -1791,7 +1799,7 @@ export interface INotebookKernelInfoDto2 { } export interface ExtHostNotebookShape { - $resolveNotebookData(viewType: string, uri: UriComponents, backupId?: string): Promise; + $openNotebook(viewType: string, uri: UriComponents, backupId?: string): Promise; $resolveNotebookEditor(viewType: string, uri: UriComponents, editorId: string): Promise; $provideNotebookKernels(handle: number, uri: UriComponents, token: CancellationToken): Promise; $resolveNotebookKernel(handle: number, editorId: string, uri: UriComponents, kernelId: string, token: CancellationToken): Promise; @@ -1804,6 +1812,7 @@ export interface ExtHostNotebookShape { $acceptNotebookActiveKernelChange(event: { uri: UriComponents, providerHandle: number | undefined, kernelFriendlyId: string | undefined }): void; $onDidReceiveMessage(editorId: string, rendererId: string | undefined, message: unknown): void; $acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEventDto, isDirty: boolean): void; + $acceptDirtyStateChanged(uriComponents: UriComponents, isDirty: boolean): void; $acceptModelSaved(uriComponents: UriComponents): void; $acceptEditorPropertiesChanged(id: string, data: INotebookEditorPropertiesChangeData): void; $acceptDocumentPropertiesChanged(uriComponents: UriComponents, data: INotebookDocumentPropertiesChangeData): void; diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index 93f77a9576d..6bdfa6fb2b4 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -359,6 +359,14 @@ const newCommands: ApiCommand[] = [ }; })) ), + // --- debug support + new ApiCommand( + 'vscode.executeInlineValueProvider', '_executeInlineValueProvider', 'Execute inline value provider', + [ApiCommandArgument.Uri, ApiCommandArgument.Range], + new ApiCommandResult('A promise that resolves to an array of InlineValue objects', result => { + return result.map(typeConverters.InlineValue.to); + }) + ), // --- open'ish commands new ApiCommand( 'vscode.open', '_workbench.open', 'Opens the provided resource in the editor. Can be a text or binary file, or a http(s) url. If you need more control over the options for opening a text file, use vscode.window.showTextDocument instead.', diff --git a/src/vs/workbench/api/common/extHostAuthentication.ts b/src/vs/workbench/api/common/extHostAuthentication.ts index 988f93a5c9f..b4445864a7c 100644 --- a/src/vs/workbench/api/common/extHostAuthentication.ts +++ b/src/vs/workbench/api/common/extHostAuthentication.ts @@ -147,16 +147,7 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape { throw new Error(`Unable to find authentication provider with handle: ${providerId}`); } - $getAllSessions(providerId: string): Promise> { - const providerData = this._authenticationProviders.get(providerId); - if (providerData) { - return Promise.resolve(providerData.provider.getAllSessions()); - } - - throw new Error(`Unable to find authentication provider with handle: ${providerId}`); - } - - $getSessions(providerId: string, scopes: string[]): Promise> { + $getSessions(providerId: string, scopes?: string[]): Promise> { const providerData = this._authenticationProviders.get(providerId); if (providerData) { return Promise.resolve(providerData.provider.getSessions(scopes)); diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 34ca106504d..5d96d713260 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -291,6 +291,24 @@ class EvaluatableExpressionAdapter { } } +class InlineValuesAdapter { + + constructor( + private readonly _documents: ExtHostDocuments, + private readonly _provider: vscode.InlineValuesProvider, + ) { } + + public provideInlineValues(resource: URI, viewPort: IRange, context: extHostProtocol.IInlineValueContextDto, token: CancellationToken): Promise { + const doc = this._documents.getDocument(resource); + return asPromise(() => this._provider.provideInlineValues(doc, typeConvert.Range.to(viewPort), typeConvert.InlineValueContext.to(context), token)).then(value => { + if (Array.isArray(value)) { + return value.map(iv => typeConvert.InlineValue.from(iv)); + } + return undefined; + }); + } +} + class DocumentHighlightAdapter { constructor( @@ -1334,7 +1352,8 @@ type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | Hov | RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter | SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter | ColorProviderAdapter | FoldingProviderAdapter | DeclarationAdapter - | SelectionRangeAdapter | CallHierarchyAdapter | DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter | EvaluatableExpressionAdapter + | SelectionRangeAdapter | CallHierarchyAdapter | DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter + | EvaluatableExpressionAdapter | InlineValuesAdapter | LinkedEditingRangeAdapter | InlineHintsAdapter; class AdapterData { @@ -1568,6 +1587,27 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF return this._withAdapter(handle, EvaluatableExpressionAdapter, adapter => adapter.provideEvaluatableExpression(URI.revive(resource), position, token), undefined); } + // --- debug inline values + + registerInlineValuesProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.InlineValuesProvider, extensionId?: ExtensionIdentifier): vscode.Disposable { + + const eventHandle = typeof provider.onDidChangeInlineValues === 'function' ? this._nextHandle() : undefined; + const handle = this._addNewAdapter(new InlineValuesAdapter(this._documents, provider), extension); + + this._proxy.$registerInlineValuesProvider(handle, this._transformDocumentSelector(selector), eventHandle); + let result = this._createDisposable(handle); + + if (eventHandle !== undefined) { + const subscription = provider.onDidChangeInlineValues!(_ => this._proxy.$emitInlineValuesEvent(eventHandle)); + result = Disposable.from(result, subscription); + } + return result; + } + + $provideInlineValues(handle: number, resource: UriComponents, range: IRange, context: extHostProtocol.IInlineValueContextDto, token: CancellationToken): Promise { + return this._withAdapter(handle, InlineValuesAdapter, adapter => adapter.provideInlineValues(URI.revive(resource), range, context, token), undefined); + } + // --- occurrences registerDocumentHighlightProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable { diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 40ff93d7dd5..e6ba271e43d 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -123,6 +123,7 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable { label: kernel.label, extension: this._extension.identifier, extensionLocation: this._extension.extensionLocation, + providerHandle: this._handle, description: kernel.description, detail: kernel.detail, isPreferred: kernel.isPreferred, @@ -403,7 +404,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN resolvedOptions = { position: typeConverters.ViewColumn.from(options.viewColumn), preserveFocus: options.preserveFocus, - selection: options.selection, + selection: options.selection && typeConverters.NotebookCellRange.from(options.selection), pinned: typeof options.preview === 'boolean' ? !options.preview : undefined }; } else { @@ -442,7 +443,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN }); } - async $resolveNotebookData(viewType: string, uri: UriComponents, backupId?: string): Promise { + async $openNotebook(viewType: string, uri: UriComponents, backupId?: string): Promise { const provider = this._notebookContentProviders.get(viewType); if (!provider) { throw new Error(`NO provider for '${viewType}'`); @@ -578,7 +579,14 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } } - public $acceptModelSaved(uriComponents: UriComponents): void { + $acceptDirtyStateChanged(resource: UriComponents, isDirty: boolean): void { + const document = this._documents.get(URI.revive(resource)); + if (document) { + document.acceptModelChanged({ rawEvents: [], versionId: document.notebookDocument.version }, isDirty); + } + } + + $acceptModelSaved(uriComponents: UriComponents): void { const document = this._documents.get(URI.revive(uriComponents)); if (document) { // this.$acceptDirtyStateChanged(uriComponents, false); @@ -601,7 +609,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } if (data.visibleRanges) { - editor.editor._acceptVisibleRanges(data.visibleRanges.ranges); + editor.editor._acceptVisibleRanges(data.visibleRanges.ranges.map(typeConverters.NotebookCellRange.to)); this._onDidChangeNotebookEditorVisibleRanges.fire({ notebookEditor: editor.editor, visibleRanges: editor.editor.visibleRanges @@ -636,7 +644,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } } - private _createExtHostEditor(document: ExtHostNotebookDocument, editorId: string, selections: number[], visibleRanges: vscode.NotebookCellRange[]) { + private _createExtHostEditor(document: ExtHostNotebookDocument, editorId: string, selections: number[], visibleRanges: extHostTypes.NotebookCellRange[]) { const revivedUri = document.uri; let webComm = this._webviewComm.get(editorId); @@ -706,23 +714,31 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } const that = this; - const document = new ExtHostNotebookDocument(this._documentsAndEditors, { - emitModelChange(event: vscode.NotebookCellsChangeEvent): void { - that._onDidChangeNotebookCells.fire(event); + const document = new ExtHostNotebookDocument( + this._documentsAndEditors, + { + emitModelChange(event: vscode.NotebookCellsChangeEvent): void { + that._onDidChangeNotebookCells.fire(event); + }, + emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void { + that._onDidChangeCellOutputs.fire(event); + }, + emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void { + that._onDidChangeCellLanguage.fire(event); + }, + emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void { + that._onDidChangeCellMetadata.fire(event); + }, + emitDocumentMetadataChange(event: vscode.NotebookDocumentMetadataChangeEvent): void { + that._onDidChangeNotebookDocumentMetadata.fire(event); + } }, - emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void { - that._onDidChangeCellOutputs.fire(event); - }, - emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void { - that._onDidChangeCellLanguage.fire(event); - }, - emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void { - that._onDidChangeCellMetadata.fire(event); - }, - emitDocumentMetadataChange(event: vscode.NotebookDocumentMetadataChangeEvent): void { - that._onDidChangeNotebookDocumentMetadata.fire(event); - } - }, viewType, modelData.contentOptions, { ...notebookDocumentMetadataDefaults, ...modelData.metadata }, uri, storageRoot); + viewType, + modelData.contentOptions, + modelData.metadata ? typeConverters.NotebookDocumentMetadata.to(modelData.metadata) : new extHostTypes.NotebookDocumentMetadata(), + uri, + storageRoot + ); document.acceptModelChanged({ versionId: modelData.versionId, @@ -746,7 +762,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN // create editor if populated if (modelData.attachedEditor) { - this._createExtHostEditor(document, modelData.attachedEditor.id, modelData.attachedEditor.selections, modelData.attachedEditor.visibleRanges); + this._createExtHostEditor(document, modelData.attachedEditor.id, modelData.attachedEditor.selections, modelData.attachedEditor.visibleRanges.map(typeConverters.NotebookCellRange.to)); editorChanged = true; } @@ -766,7 +782,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN const document = this._documents.get(revivedUri); if (document) { - this._createExtHostEditor(document, editorModelData.id, editorModelData.selections, editorModelData.visibleRanges); + this._createExtHostEditor(document, editorModelData.id, editorModelData.selections, editorModelData.visibleRanges.map(typeConverters.NotebookCellRange.to)); editorChanged = true; } } diff --git a/src/vs/workbench/api/common/extHostNotebookDocument.ts b/src/vs/workbench/api/common/extHostNotebookDocument.ts index 21d2e99a563..4bd68b65686 100644 --- a/src/vs/workbench/api/common/extHostNotebookDocument.ts +++ b/src/vs/workbench/api/common/extHostNotebookDocument.ts @@ -8,29 +8,29 @@ import { hash } from 'vs/base/common/hash'; import { Disposable, DisposableStore, dispose } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { joinPath } from 'vs/base/common/resources'; -import { ISplice } from 'vs/base/common/sequence'; import { URI } from 'vs/base/common/uri'; import { CellKind, INotebookDocumentPropertiesChangeData } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostDocumentsAndEditors, IExtHostModelAddedData } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import * as extHostTypeConverters from 'vs/workbench/api/common/extHostTypeConverters'; -import { IMainCellDto, IOutputDto, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; +import { IMainCellDto, IOutputDto, IOutputItemDto, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import * as vscode from 'vscode'; class RawContentChangeEvent { - constructor(readonly start: number, readonly deletedCount: number, readonly deletedItems: ExtHostCell[], readonly items: ExtHostCell[]) { } + constructor(readonly start: number, readonly deletedCount: number, readonly deletedItems: vscode.NotebookCell[], readonly items: ExtHostCell[]) { } static asApiEvent(event: RawContentChangeEvent): vscode.NotebookCellsChangeData { return Object.freeze({ start: event.start, deletedCount: event.deletedCount, - deletedItems: event.deletedItems.map(data => data.cell), + deletedItems: event.deletedItems, items: event.items.map(data => data.cell) }); } } -export class ExtHostCell extends Disposable { +export class ExtHostCell { static asModelAddData(notebook: vscode.NotebookDocument, cell: IMainCellDto): IExtHostModelAddedData { return { @@ -47,12 +47,8 @@ export class ExtHostCell extends Disposable { private _onDidDispose = new Emitter(); readonly onDidDispose: Event = this._onDidDispose.event; - private _onDidChangeOutputs = new Emitter[]>(); - readonly onDidChangeOutputs: Event[]> = this._onDidChangeOutputs.event; - - private _outputs: IOutputDto[]; - - private _metadata: vscode.NotebookCellMetadata; + private _outputs: extHostTypes.NotebookCellOutput[]; + private _metadata: extHostTypes.NotebookCellMetadata; readonly handle: number; readonly uri: URI; @@ -65,16 +61,16 @@ export class ExtHostCell extends Disposable { private readonly _extHostDocument: ExtHostDocumentsAndEditors, private readonly _cellData: IMainCellDto, ) { - super(); - this.handle = _cellData.handle; this.uri = URI.revive(_cellData.uri); this.cellKind = _cellData.cellKind; + this._outputs = _cellData.outputs.map(extHostTypeConverters.NotebookCellOutput.to); + this._metadata = extHostTypeConverters.NotebookCellMetadata.to(_cellData.metadata ?? {}); + } - this._outputs = _cellData.outputs; - - - this._metadata = _cellData.metadata ?? {}; + dispose() { + this._onDidDispose.fire(); + this._onDidDispose.dispose(); } get cell(): vscode.NotebookCell { @@ -91,7 +87,7 @@ export class ExtHostCell extends Disposable { cellKind: extHostTypeConverters.NotebookCellKind.to(this._cellData.cellKind), document: data.document, get language() { return data!.document.languageId; }, - get outputs() { return that._outputs.map(extHostTypeConverters.NotebookCellOutput.to); }, + get outputs() { return that._outputs.slice(0); }, set outputs(_value) { throw new Error('Use WorkspaceEdit to update cell outputs.'); }, get metadata() { return that._metadata; }, set metadata(_value) { throw new Error('Use WorkspaceEdit to update cell metadata.'); }, @@ -100,17 +96,23 @@ export class ExtHostCell extends Disposable { return this._cell; } - dispose() { - super.dispose(); - this._onDidDispose.fire(); - } - setOutputs(newOutputs: IOutputDto[]): void { - this._outputs = newOutputs; + this._outputs = newOutputs.map(extHostTypeConverters.NotebookCellOutput.to); } - setMetadata(newMetadata: vscode.NotebookCellMetadata): void { - this._metadata = newMetadata; + setOutputItems(outputId: string, append: boolean, newOutputItems: IOutputItemDto[]) { + const newItems = newOutputItems.map(extHostTypeConverters.NotebookCellOutputItem.to); + const output = this._outputs.find(op => op.id === outputId); + if (output) { + if (!append) { + output.outputs.length = 0; + } + output.outputs.push(...newItems); + } + } + + setMetadata(newMetadata: NotebookCellMetadata): void { + this._metadata = extHostTypeConverters.NotebookCellMetadata.to(newMetadata); } } @@ -137,10 +139,8 @@ export class ExtHostNotebookDocument extends Disposable { private _cellDisposableMapping = new Map(); private _notebook: vscode.NotebookDocument | undefined; - // private _metadata: Required; - // private _metadataChangeListener: IDisposable; private _versionId = 0; - private _isDirty: boolean = false; + private _isDirty = false; private _backupCounter = 1; private _backup?: vscode.NotebookDocumentBackup; private _disposed = false; @@ -150,7 +150,7 @@ export class ExtHostNotebookDocument extends Disposable { private readonly _emitter: INotebookEventEmitter, private readonly _viewType: string, private readonly _contentOptions: vscode.NotebookDocumentContentOptions, - private _metadata: Required, + private _metadata: extHostTypes.NotebookDocumentMetadata, public readonly uri: URI, private readonly _storagePath: URI | undefined ) { @@ -206,7 +206,7 @@ export class ExtHostNotebookDocument extends Disposable { ...notebookDocumentMetadataDefaults, ...data.metadata }; - this._metadata = newMetadata; + this._metadata = this._metadata.with(newMetadata); this._emitter.emitDocumentMetadataChange({ document: this.notebookDocument }); } @@ -222,6 +222,8 @@ export class ExtHostNotebookDocument extends Disposable { this._moveCell(e.index, e.newIdx); } else if (e.kind === NotebookCellsChangeType.Output) { this._setCellOutputs(e.index, e.outputs); + } else if (e.kind === NotebookCellsChangeType.OutputItem) { + this._setCellOutputItems(e.index, e.outputId, e.append, e.outputItems); } else if (e.kind === NotebookCellsChangeType.ChangeLanguage) { this._changeCellLanguage(e.index, e.language); } else if (e.kind === NotebookCellsChangeType.ChangeCellMetadata) { @@ -263,12 +265,14 @@ export class ExtHostNotebookDocument extends Disposable { this._cellDisposableMapping.delete(this._cells[j].handle); } + const changeEvent = new RawContentChangeEvent(splice[0], splice[1], [], newCells); const deletedItems = this._cells.splice(splice[0], splice[1], ...newCells); for (let cell of deletedItems) { removedCellDocuments.push(cell.uri); + changeEvent.deletedItems.push(cell.cell); } - contentChangeEvents.push(new RawContentChangeEvent(splice[0], splice[1], deletedItems, newCells)); + contentChangeEvents.push(changeEvent); }); this._documentsAndEditors.acceptDocumentsAndEditorsDelta({ @@ -310,6 +314,12 @@ export class ExtHostNotebookDocument extends Disposable { this._emitter.emitCellOutputsChange({ document: this.notebookDocument, cells: [cell.cell] }); } + private _setCellOutputItems(index: number, outputId: string, append: boolean, outputItems: IOutputItemDto[]): void { + const cell = this._cells[index]; + cell.setOutputItems(outputId, append, outputItems); + this._emitter.emitCellOutputsChange({ document: this.notebookDocument, cells: [cell.cell] }); + } + private _changeCellLanguage(index: number, language: string): void { const cell = this._cells[index]; const event: vscode.NotebookCellLanguageChangeEvent = { document: this.notebookDocument, cell: cell.cell, language }; diff --git a/src/vs/workbench/api/common/extHostNotebookEditor.ts b/src/vs/workbench/api/common/extHostNotebookEditor.ts index 9afae7d5be3..7b3105cfb18 100644 --- a/src/vs/workbench/api/common/extHostNotebookEditor.ts +++ b/src/vs/workbench/api/common/extHostNotebookEditor.ts @@ -90,7 +90,7 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook //TODO@rebornix noop setter? selection?: vscode.NotebookCell; - private _visibleRanges: vscode.NotebookCellRange[] = []; + private _visibleRanges: extHostTypes.NotebookCellRange[] = []; private _viewColumn?: vscode.ViewColumn; private _active: boolean = false; private _visible: boolean = false; @@ -153,11 +153,11 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook return this._visibleRanges; } - set visibleRanges(_range: vscode.NotebookCellRange[]) { + set visibleRanges(_range) { throw readonly('visibleRanges'); } - _acceptVisibleRanges(value: vscode.NotebookCellRange[]): void { + _acceptVisibleRanges(value: extHostTypes.NotebookCellRange[]): void { this._visibleRanges = value; } @@ -233,13 +233,13 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook return this._proxy.$trySetDecorations( this.id, - range, + extHostConverter.NotebookCellRange.from(range), decorationType.key ); } revealRange(range: vscode.NotebookCellRange, revealType?: extHostTypes.NotebookEditorRevealType) { - this._proxy.$tryRevealRange(this.id, range, revealType || extHostTypes.NotebookEditorRevealType.Default); + this._proxy.$tryRevealRange(this.id, extHostConverter.NotebookCellRange.from(range), revealType ?? extHostTypes.NotebookEditorRevealType.Default); } async postMessage(message: any): Promise { diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index ca1914d2e38..df51908e74a 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -84,7 +84,7 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { if (!options || !options.treeDataProvider) { throw new Error('Options with treeDataProvider is mandatory'); } - + const registerPromise = this._proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany }); const treeView = this.createExtHostTreeView(viewId, options, extension); return { get onDidCollapseElement() { return treeView.onDidCollapseElement; }, @@ -110,7 +110,9 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { reveal: (element: T, options?: IRevealOptions): Promise => { return treeView.reveal(element, options); }, - dispose: () => { + dispose: async () => { + // Wait for the registration promise to finish before doing the dispose. + await registerPromise; this.treeViews.delete(viewId); treeView.dispose(); } @@ -240,7 +242,6 @@ class ExtHostTreeView extends Disposable { } } this.dataProvider = options.treeDataProvider; - this.proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany }); if (this.dataProvider.onDidChangeTreeData) { this._register(this.dataProvider.onDidChangeTreeData(element => this._onDidChangeData.fire({ message: false, element }))); } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 9799bddf7bd..1c0301a8ac2 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -31,7 +31,7 @@ import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays'; import { RenderLineNumbersType } from 'vs/editor/common/config/editorOptions'; import { CommandsConverter } from 'vs/workbench/api/common/extHostCommands'; import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; -import { CellEditType, CellKind, ICellDto2, INotebookDecorationRenderOptions, IOutputDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import * as notebooks from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ITestItem, ITestState } from 'vs/workbench/contrib/testing/common/testCollection'; export interface PositionLike { @@ -509,7 +509,7 @@ export namespace TextEdit { } export namespace WorkspaceEdit { - export function from(value: vscode.WorkspaceEdit, documents?: ExtHostDocumentsAndEditors, notebooks?: ExtHostNotebookController): extHostProtocol.IWorkspaceEditDto { + export function from(value: vscode.WorkspaceEdit, documents?: ExtHostDocumentsAndEditors, extHostNotebooks?: ExtHostNotebookController): extHostProtocol.IWorkspaceEditDto { const result: extHostProtocol.IWorkspaceEditDto = { edits: [] }; @@ -544,7 +544,7 @@ export namespace WorkspaceEdit { resource: entry.uri, edit: entry.edit, notebookMetadata: entry.notebookMetadata, - notebookVersionId: notebooks?.lookupNotebookDocument(entry.uri)?.notebookDocument.version + notebookVersionId: extHostNotebooks?.lookupNotebookDocument(entry.uri)?.notebookDocument.version }); } else if (entry._type === types.FileEditType.CellOutput) { @@ -554,7 +554,7 @@ export namespace WorkspaceEdit { metadata: entry.metadata, resource: entry.uri, edit: { - editType: CellEditType.Output, + editType: notebooks.CellEditType.Output, index: entry.index, append: entry.append, outputs: entry.newOutputs.map(NotebookCellOutput.from) @@ -568,7 +568,7 @@ export namespace WorkspaceEdit { metadata: entry.metadata, resource: entry.uri, edit: { - editType: CellEditType.Metadata, + editType: notebooks.CellEditType.Metadata, index: entry.index, metadata: entry.newMetadata } @@ -579,9 +579,9 @@ export namespace WorkspaceEdit { _type: extHostProtocol.WorkspaceEditType.Cell, metadata: entry.metadata, resource: entry.uri, - notebookVersionId: notebooks?.lookupNotebookDocument(entry.uri)?.notebookDocument.version, + notebookVersionId: extHostNotebooks?.lookupNotebookDocument(entry.uri)?.notebookDocument.version, edit: { - editType: CellEditType.Replace, + editType: notebooks.CellEditType.Replace, index: entry.index, count: entry.count, cells: entry.cells.map(NotebookCellData.from) @@ -593,10 +593,10 @@ export namespace WorkspaceEdit { metadata: entry.metadata, resource: entry.uri, edit: { - editType: CellEditType.OutputItems, + editType: notebooks.CellEditType.OutputItems, index: entry.index, outputId: entry.outputId, - items: entry.newOutputItems?.map(item => ({ mime: item.mime, value: item.value, metadata: item.metadata })) || [], + items: entry.newOutputItems?.map(NotebookCellOutputItem.from) || [], append: entry.append } }); @@ -850,6 +850,66 @@ export namespace EvaluatableExpression { } } +export namespace InlineValue { + export function from(inlineValue: vscode.InlineValue): modes.InlineValue { + if (inlineValue instanceof types.InlineValueText) { + return { + type: 'text', + text: inlineValue.text, + range: Range.from(inlineValue.range) + }; + } else if (inlineValue instanceof types.InlineValueVariableLookup) { + return { + type: 'variable', + variableName: inlineValue.variableName, + caseSensitiveLookup: inlineValue.caseSensitiveLookup, + range: Range.from(inlineValue.range) + }; + } else if (inlineValue instanceof types.InlineValueEvaluatableExpression) { + return { + type: 'expression', + expression: inlineValue.expression, + range: Range.from(inlineValue.range) + }; + } else { + throw new Error(`Unknown 'InlineValue' type`); + } + } + + export function to(inlineValue: modes.InlineValue): vscode.InlineValue { + switch (inlineValue.type) { + case 'text': + return { + text: inlineValue.text, + range: Range.to(inlineValue.range) + }; + case 'variable': + return { + variableName: inlineValue.variableName, + caseSensitiveLookup: inlineValue.caseSensitiveLookup, + range: Range.to(inlineValue.range) + }; + case 'expression': + return { + expression: inlineValue.expression, + range: Range.to(inlineValue.range) + }; + } + } +} + +export namespace InlineValueContext { + export function from(inlineValueContext: vscode.InlineValueContext): extHostProtocol.IInlineValueContextDto { + return { + stoppedLocation: Range.from(inlineValueContext.stoppedLocation) + }; + } + + export function to(inlineValueContext: extHostProtocol.IInlineValueContextDto): types.InlineValueContext { + return new types.InlineValueContext(Range.to(inlineValueContext.stoppedLocation)); + } +} + export namespace DocumentHighlight { export function from(documentHighlight: vscode.DocumentHighlight): modes.DocumentHighlight { return { @@ -1334,32 +1394,63 @@ export namespace LanguageSelector { } else if (typeof selector === 'string') { return selector; } else { + const filter = selector as vscode.DocumentFilter; // TODO: microsoft/TypeScript#42768 return { - language: selector.language, - scheme: selector.scheme, - pattern: typeof selector.pattern === 'undefined' ? undefined : GlobPattern.from(selector.pattern), - exclusive: selector.exclusive + language: filter.language, + scheme: filter.scheme, + pattern: typeof filter.pattern === 'undefined' ? undefined : GlobPattern.from(filter.pattern), + exclusive: filter.exclusive }; } } } +export namespace NotebookCellRange { + + export function from(range: vscode.NotebookCellRange): notebooks.ICellRange { + return { start: range.start, end: range.end }; + } + + export function to(range: notebooks.ICellRange): types.NotebookCellRange { + return new types.NotebookCellRange(range.start, range.end); + } +} + +export namespace NotebookCellMetadata { + + export function to(data: notebooks.NotebookCellMetadata): types.NotebookCellMetadata { + return new types.NotebookCellMetadata(data.editable, data.breakpointMargin, data.runnable, data.hasExecutionOrder, data.executionOrder, data.runStartTime, data.runStartTime, data.statusMessage, data.lastRunDuration, data.inputCollapsed, data.outputCollapsed, data.custom); + } +} + +export namespace NotebookDocumentMetadata { + + export function from(data: types.NotebookDocumentMetadata): notebooks.NotebookDocumentMetadata { + return data; + } + + export function to(data: notebooks.NotebookDocumentMetadata): types.NotebookDocumentMetadata { + return new types.NotebookDocumentMetadata(data.editable, data.runnable, data.cellEditable, data.cellRunnable, data.cellHasExecutionOrder, data.displayOrder, data.custom, data.runState, data.trusted); + } + +} + export namespace NotebookCellKind { - export function from(data: vscode.NotebookCellKind): CellKind { + export function from(data: vscode.NotebookCellKind): notebooks.CellKind { switch (data) { case types.NotebookCellKind.Markdown: - return CellKind.Markdown; + return notebooks.CellKind.Markdown; case types.NotebookCellKind.Code: default: - return CellKind.Code; + return notebooks.CellKind.Code; } } - export function to(data: CellKind): vscode.NotebookCellKind { + export function to(data: notebooks.CellKind): vscode.NotebookCellKind { switch (data) { - case CellKind.Markdown: + case notebooks.CellKind.Markdown: return types.NotebookCellKind.Markdown; - case CellKind.Code: + case notebooks.CellKind.Code: default: return types.NotebookCellKind.Code; } @@ -1368,7 +1459,7 @@ export namespace NotebookCellKind { export namespace NotebookCellData { - export function from(data: vscode.NotebookCellData): ICellDto2 { + export function from(data: vscode.NotebookCellData): notebooks.ICellDto2 { return { cellKind: NotebookCellKind.from(data.cellKind), language: data.language, @@ -1385,31 +1476,32 @@ export namespace NotebookCellData { } } -export namespace NotebookCellOutput { - export function from(output: types.NotebookCellOutput): IOutputDto { - - const data = Object.create(null); - const custom = Object.create(null); - - for (let item of output.outputs) { - data[item.mime] = item.value; - custom[item.mime] = item.metadata; - } - +export namespace NotebookCellOutputItem { + export function from(item: types.NotebookCellOutputItem): notebooks.IOutputItemDto { return { - outputId: output.id, - outputs: (output.outputs || []).map(op => ({ - mime: op.mime, - value: op.value, - metadata: op.metadata - })) || [], - // metadata: isEmptyObject(custom) ? undefined : { custom } + mime: item.mime, + value: item.value, + metadata: item.metadata }; } - export function to(output: IOutputDto): vscode.NotebookCellOutput { - const items: types.NotebookCellOutputItem[] = output.outputs.map(op => new types.NotebookCellOutputItem(op.mime, op.value, op.metadata)); - return new types.NotebookCellOutput(items, output.outputId); + export function to(item: notebooks.IOutputItemDto): types.NotebookCellOutputItem { + return new types.NotebookCellOutputItem(item.mime, item.value, item.metadata); + } +} + +export namespace NotebookCellOutput { + export function from(output: types.NotebookCellOutput): notebooks.IOutputDto { + return { + outputId: output.id, + outputs: output.outputs.map(NotebookCellOutputItem.from), + metadata: output.metadata + }; + } + + export function to(output: notebooks.IOutputDto): vscode.NotebookCellOutput { + const items = output.outputs.map(NotebookCellOutputItem.to); + return new types.NotebookCellOutput(items, output.outputId, output.metadata); } } @@ -1488,7 +1580,7 @@ export namespace NotebookExclusiveDocumentPattern { } export namespace NotebookDecorationRenderOptions { - export function from(options: vscode.NotebookDecorationRenderOptions): INotebookDecorationRenderOptions { + export function from(options: vscode.NotebookDecorationRenderOptions): notebooks.INotebookDecorationRenderOptions { return { backgroundColor: options.backgroundColor, borderColor: options.borderColor, @@ -1507,7 +1599,7 @@ export namespace TestState { severity: message.severity, expectedOutput: message.expectedOutput, actualOutput: message.actualOutput, - location: message.location ? location.from(message.location) : undefined, + location: message.location ? location.from(message.location) as any : undefined, })) ?? [], }; } @@ -1536,7 +1628,7 @@ export namespace TestItem { return { extId: item.id ?? (parentExtId ? `${parentExtId}\0${item.label}` : item.label), label: item.label, - location: item.location ? location.from(item.location) : undefined, + location: item.location ? location.from(item.location) as any : undefined, debuggable: item.debuggable ?? false, description: item.description, runnable: item.runnable ?? true, diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 96a66824ee6..7a2b4b9a1b9 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -703,8 +703,7 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { } private _editNotebookCellOutput(uri: URI, index: number, append: boolean, outputs: vscode.NotebookCellOutput[], metadata: vscode.WorkspaceEditEntryMetadata | undefined): void { - let newOutputs: NotebookCellOutput[] = outputs; - this._edits.push({ _type: FileEditType.CellOutput, metadata, uri, index, append, newOutputs, newMetadata: undefined }); + this._edits.push({ _type: FileEditType.CellOutput, metadata, uri, index, append, newOutputs: outputs, newMetadata: undefined }); } replaceNotebookCellMetadata(uri: URI, index: number, cellMetadata: vscode.NotebookCellMetadata, metadata?: vscode.WorkspaceEditEntryMetadata): void { @@ -2439,6 +2438,51 @@ export class EvaluatableExpression implements vscode.EvaluatableExpression { } } +@es5ClassCompat +export class InlineValueText implements vscode.InlineValueText { + readonly range: Range; + readonly text: string; + + constructor(text: string, range: Range) { + this.text = text; + this.range = range; + } +} + +@es5ClassCompat +export class InlineValueVariableLookup implements vscode.InlineValueVariableLookup { + readonly variableName: string; + readonly caseSensitiveLookup: boolean; + readonly range: Range; + + constructor(variableName: string, range: Range, caseSensitiveLookup: boolean) { + this.variableName = variableName; + this.caseSensitiveLookup = caseSensitiveLookup; + this.range = range; + } +} + +@es5ClassCompat +export class InlineValueEvaluatableExpression implements vscode.InlineValueEvaluatableExpression { + readonly expression: string; + readonly range: Range; + + constructor(expression: string, range: Range) { + this.expression = expression; + this.range = range; + } +} + +@es5ClassCompat +export class InlineValueContext implements vscode.InlineValueContext { + + readonly stoppedLocation: vscode.Range; + + constructor(range: vscode.Range) { + this.stoppedLocation = range; + } +} + //#region file api export enum FileChangeType { @@ -2849,6 +2893,32 @@ export enum ColorThemeKind { //#region Notebook +export class NotebookCellRange { + + private _start: number; + private _end: number; + + get start() { + return this._start; + } + + get end() { + return this._end; + } + + constructor(start: number, end: number) { + // todo@rebornix + // if (start < 0) { + // throw illegalArgument('start must be positive'); + // } + // if (end < start) { + // throw illegalArgument('end cannot be smaller than start'); + // } + this._start = start; + this._end = end; + } +} + export class NotebookCellMetadata { constructor( @@ -2922,15 +2992,30 @@ export class NotebookCellOutputItem { constructor( readonly mime: string, readonly value: unknown, // JSON'able - readonly metadata?: any + readonly metadata?: Record ) { } } export class NotebookCellOutput { + + readonly outputs: NotebookCellOutputItem[]; + readonly id: string; + readonly metadata?: Record; + constructor( - readonly outputs: NotebookCellOutputItem[], - readonly id: string = generateUuid() - ) { } + outputs: NotebookCellOutputItem[], + idOrMetadata?: string | Record, + metadata?: Record + ) { + this.outputs = outputs; + if (typeof idOrMetadata === 'string') { + this.id = idOrMetadata; + this.metadata = metadata; + } else { + this.id = generateUuid(); + this.metadata = idOrMetadata ?? metadata; + } + } } export enum NotebookCellKind { diff --git a/src/vs/workbench/api/common/menusExtensionPoint.ts b/src/vs/workbench/api/common/menusExtensionPoint.ts index 6360dadf550..7122754cfc6 100644 --- a/src/vs/workbench/api/common/menusExtensionPoint.ts +++ b/src/vs/workbench/api/common/menusExtensionPoint.ts @@ -46,8 +46,7 @@ const apiMenus: IAPIMenu[] = [ { key: 'editor/title/run', id: MenuId.EditorTitleRun, - description: localize('menus.editorTitleRun', "Run submenu inside the editor title menu"), - proposed: true + description: localize('menus.editorTitleRun', "Run submenu inside the editor title menu") }, { key: 'editor/context', diff --git a/src/vs/workbench/api/node/extHostTunnelService.ts b/src/vs/workbench/api/node/extHostTunnelService.ts index c0cc5d43940..9b9dd79dc9c 100644 --- a/src/vs/workbench/api/node/extHostTunnelService.ts +++ b/src/vs/workbench/api/node/extHostTunnelService.ts @@ -196,6 +196,9 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe async setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise { if (provider) { + if (provider.candidatePortSource !== undefined) { + await this._proxy.$setCandidatePortSource(provider.candidatePortSource); + } if (provider.showCandidatePort) { this._showCandidatePort = provider.showCandidatePort; await this._proxy.$setCandidateFilter(); diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index 713e088b96f..8817e5fd820 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -23,7 +23,7 @@ import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IWorkspacesService, hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; -import { WORKSPACE_TRUST_ENABLED, WORKSPACE_TRUST_URI } from 'vs/workbench/services/workspaces/common/workspaceTrust'; +import { WORKSPACE_TRUST_ENABLED } from 'vs/workbench/services/workspaces/common/workspaceTrust'; export class OpenFileAction extends Action { @@ -258,13 +258,13 @@ class WorkspaceTrustManageAction extends Action2 { title: { value: nls.localize('manageTrustAction', "Manage Workspace Trust"), original: 'Manage Workspace Trust' }, precondition: ContextKeyExpr.equals(`config.${WORKSPACE_TRUST_ENABLED}`, true), category: nls.localize('workspacesCategory', "Workspaces"), - f1: true, + f1: true }); } - run(accessor: ServicesAccessor) { - const editorService = accessor.get(IEditorService); - editorService.openEditor({ resource: WORKSPACE_TRUST_URI, mode: 'jsonc', options: { pinned: true } }); + async run(accessor: ServicesAccessor) { + const commandService = accessor.get(ICommandService); + await commandService.executeCommand('workbench.trust.manage'); } } diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index c3f7817a47f..8b62c5a88de 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -210,7 +210,7 @@ export class AccountsActivityActionViewItem extends MenuActivityActionViewItem { const providers = this.authenticationService.getProviderIds(); const allSessions = providers.map(async providerId => { try { - const sessions = await this.authenticationService.getAllSessions(providerId); + const sessions = await this.authenticationService.getSessions(providerId); const groupedSessions: { [label: string]: AuthenticationSession[]; } = {}; sessions.forEach(session => { diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index ba37e0b668f..f9ee130df8c 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -534,8 +534,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { orientation: ActionsOrientation.VERTICAL, ariaLabel: localize('manage', "Manage"), animated: false, - preventLoopNavigation: true, - ignoreOrientationForPreviousAndNextKey: true + preventLoopNavigation: true })); this.globalActivityAction = this._register(new ActivityAction({ diff --git a/src/vs/workbench/browser/parts/compositeBar.ts b/src/vs/workbench/browser/parts/compositeBar.ts index 70d629fb2c0..94fccfcb646 100644 --- a/src/vs/workbench/browser/parts/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositeBar.ts @@ -227,7 +227,6 @@ export class CompositeBar extends Widget implements ICompositeBar { ariaLabel: nls.localize('activityBarAriaLabel', "Active View Switcher"), animated: false, preventLoopNavigation: this.options.preventLoopNavigation, - ignoreOrientationForPreviousAndNextKey: true, triggerKeys: { keyDown: true } })); diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 003527d67e0..78d51af076a 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -464,7 +464,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCo MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.SPLIT_EDITOR_RIGHT, title: nls.localize('splitRight', "Split Right") }, group: '5_split', order: 40 }); // Editor Title Menu -MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.TOGGLE_DIFF_SIDE_BY_SIDE, title: nls.localize('toggleInlineView', "Toggle Inline View") }, group: '1_diff', order: 10, when: ContextKeyExpr.has('isInDiffEditor') }); +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.TOGGLE_DIFF_SIDE_BY_SIDE, title: nls.localize('inlineView', "Inline View"), toggled: ContextKeyExpr.equals('config.diffEditor.renderSideBySide', false) }, group: '1_diff', order: 10, when: ContextKeyExpr.has('isInDiffEditor') }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.SHOW_EDITORS_IN_GROUP, title: nls.localize('showOpenedEditors', "Show Opened Editors") }, group: '3_open', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All") }, group: '5_close', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeAllSaved', "Close Saved") }, group: '5_close', order: 20 }); diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 7b657de07f4..850294ee6a0 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -509,8 +509,8 @@ export class TabsTitleControl extends TitleControl { setActive(isGroupActive: boolean): void { // Activity has an impact on each tab's active indication - this.forEachTab((editor, index, tabContainer, tabLabelWidget, tabLabel) => { - this.redrawTabActiveAndDirty(isGroupActive, editor, tabContainer, tabLabelWidget); + this.forEachTab((editor, index, tabContainer, tabLabelWidget, tabLabel, tabActionBar) => { + this.redrawTabActiveAndDirty(isGroupActive, editor, tabContainer, tabActionBar); }); // Activity has an impact on the toolbar, so we need to update and layout @@ -545,7 +545,7 @@ export class TabsTitleControl extends TitleControl { } updateEditorDirty(editor: IEditorInput): void { - this.withTab(editor, (editor, index, tabContainer, tabLabelWidget) => this.redrawTabActiveAndDirty(this.accessor.activeGroup === this.group, editor, tabContainer, tabLabelWidget)); + this.withTab(editor, (editor, index, tabContainer, tabLabelWidget, tabLabel, tabActionBar) => this.redrawTabActiveAndDirty(this.accessor.activeGroup === this.group, editor, tabContainer, tabActionBar)); } updateOptions(oldOptions: IEditorPartOptions, newOptions: IEditorPartOptions): void { @@ -1119,7 +1119,7 @@ export class TabsTitleControl extends TitleControl { this.redrawTabBorders(index, tabContainer); // Active / dirty state - this.redrawTabActiveAndDirty(this.accessor.activeGroup === this.group, editor, tabContainer, tabLabelWidget); + this.redrawTabActiveAndDirty(this.accessor.activeGroup === this.group, editor, tabContainer, tabActionBar); } private redrawTabLabel(editor: IEditorInput, index: number, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel): void { @@ -1177,26 +1177,23 @@ export class TabsTitleControl extends TitleControl { } } - private redrawTabActiveAndDirty(isGroupActive: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel): void { + private redrawTabActiveAndDirty(isGroupActive: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabActionBar: ActionBar): void { const isTabActive = this.group.isActive(editor); const hasModifiedBorderTop = this.doRedrawTabDirty(isGroupActive, isTabActive, editor, tabContainer); - this.doRedrawTabActive(isGroupActive, !hasModifiedBorderTop, editor, tabContainer, tabLabelWidget); + this.doRedrawTabActive(isGroupActive, !hasModifiedBorderTop, editor, tabContainer, tabActionBar); } - private doRedrawTabActive(isGroupActive: boolean, allowBorderTop: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel): void { - const index = this.group.getIndexOfEditor(editor); - const actionBar = this.tabActionBars[index]; + private doRedrawTabActive(isGroupActive: boolean, allowBorderTop: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabActionBar: ActionBar): void { + // Tab is active if (this.group.isActive(editor)) { // Container tabContainer.classList.add('active'); tabContainer.setAttribute('aria-selected', 'true'); - // Only active tab can be focused into - tabContainer.tabIndex = 0; - actionBar.setFocusable(true); + tabContainer.tabIndex = 0; // Only active tab can be focused into tabContainer.style.backgroundColor = this.getColor(isGroupActive ? TAB_ACTIVE_BACKGROUND : TAB_UNFOCUSED_ACTIVE_BACKGROUND) || ''; const activeTabBorderColorBottom = this.getColor(isGroupActive ? TAB_ACTIVE_BORDER : TAB_UNFOCUSED_ACTIVE_BORDER); @@ -1219,6 +1216,9 @@ export class TabsTitleControl extends TitleControl { // Label tabContainer.style.color = this.getColor(isGroupActive ? TAB_ACTIVE_FOREGROUND : TAB_UNFOCUSED_ACTIVE_FOREGROUND) || ''; + + // Actions + tabActionBar.setFocusable(true); } // Tab is inactive @@ -1227,13 +1227,15 @@ export class TabsTitleControl extends TitleControl { // Container tabContainer.classList.remove('active'); tabContainer.setAttribute('aria-selected', 'false'); - tabContainer.tabIndex = -1; - actionBar.setFocusable(false); + tabContainer.tabIndex = -1; // Only active tab can be focused into tabContainer.style.backgroundColor = this.getColor(isGroupActive ? TAB_INACTIVE_BACKGROUND : TAB_UNFOCUSED_INACTIVE_BACKGROUND) || ''; tabContainer.style.boxShadow = ''; // Label tabContainer.style.color = this.getColor(isGroupActive ? TAB_INACTIVE_FOREGROUND : TAB_UNFOCUSED_INACTIVE_FOREGROUND) || ''; + + // Actions + tabActionBar.setFocusable(false); } } diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index f3587a51a51..96f49a70965 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -127,13 +127,13 @@ export abstract class MenubarControl extends Disposable { this.updateService.onStateChange(() => this.onUpdateStateChange()); // Listen for changes in recently opened menu - this._register(this.workspacesService.onRecentlyOpenedChange(() => { this.onRecentlyOpenedChange(); })); + this._register(this.workspacesService.onDidChangeRecentlyOpened(() => { this.onDidChangeRecentlyOpened(); })); // Listen to keybindings change this._register(this.keybindingService.onDidUpdateKeybindings(() => this.updateMenubar())); // Update recent menu items on formatter registration - this._register(this.labelService.onDidChangeFormatters(() => { this.onRecentlyOpenedChange(); })); + this._register(this.labelService.onDidChangeFormatters(() => { this.onDidChangeRecentlyOpened(); })); } protected updateMenubar(): void { @@ -189,7 +189,7 @@ export abstract class MenubarControl extends Disposable { protected onDidChangeWindowFocus(hasFocus: boolean): void { // When we regain focus, update the recent menu items if (hasFocus) { - this.onRecentlyOpenedChange(); + this.onDidChangeRecentlyOpened(); } } @@ -205,7 +205,7 @@ export abstract class MenubarControl extends Disposable { // Since we try not update when hidden, we should // try to update the recently opened list on visibility changes if (event.affectsConfiguration('window.menuBarVisibility')) { - this.onRecentlyOpenedChange(); + this.onDidChangeRecentlyOpened(); } } @@ -213,7 +213,7 @@ export abstract class MenubarControl extends Disposable { return isMacintosh && isNative ? false : getMenuBarVisibility(this.configurationService) === 'hidden'; } - protected onRecentlyOpenedChange(): void { + protected onDidChangeRecentlyOpened(): void { // Do not update recently opened when the menubar is hidden #108712 if (!this.menubarHidden) { @@ -543,7 +543,7 @@ export class CustomMenubarControl extends MenubarControl { private onDidVisibilityChange(visible: boolean): void { this.visible = visible; - this.onRecentlyOpenedChange(); + this.onDidChangeRecentlyOpened(); this._onVisibilityChange.fire(visible); } @@ -765,12 +765,12 @@ export class CustomMenubarControl extends MenubarControl { super.onUpdateStateChange(); } - protected onRecentlyOpenedChange(): void { + protected onDidChangeRecentlyOpened(): void { if (!this.visible) { return; } - super.onRecentlyOpenedChange(); + super.onDidChangeRecentlyOpened(); } protected onUpdateKeybindings(): void { diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index bdecdbc0c90..9aea863b585 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -302,10 +302,10 @@ class BrowserMain extends Disposable { } private async createStorageService(payload: IWorkspaceInitializationPayload, environmentService: IWorkbenchEnvironmentService, fileService: IFileService, logService: ILogService): Promise { - const storageService = new BrowserStorageService(environmentService, fileService); + const storageService = new BrowserStorageService(payload, environmentService, fileService); try { - await storageService.initialize(payload); + await storageService.initialize(); return storageService; } catch (error) { diff --git a/src/vs/workbench/common/actions.ts b/src/vs/workbench/common/actions.ts index dfe61debb2e..4640272f1ae 100644 --- a/src/vs/workbench/common/actions.ts +++ b/src/vs/workbench/common/actions.ts @@ -59,7 +59,7 @@ Registry.add(Extensions.WorkbenchActions, new class implements IWorkbenchActionR // menu item // TODO@Rob slightly weird if-check required because of - // https://github.com/microsoft/vscode/blob/master/src/vs/workbench/contrib/search/electron-browser/search.contribution.ts#L266 + // https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/search/electron-browser/search.contribution.ts#L266 if (descriptor.label) { let idx = alias.indexOf(': '); diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 5471bd95d6f..9feceb6188b 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -1549,9 +1549,9 @@ export async function pathsToEditors(paths: IPathData[] | undefined, fileService pinned: true, override: path.overrideId } : { - pinned: true, - override: path.overrideId - }; + pinned: true, + override: path.overrideId + }; let input: IResourceEditorInput | IUntitledTextResourceEditorInput; if (!exists) { diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts index 85c2711cb46..bcdb3736d19 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts @@ -26,9 +26,9 @@ import { MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { Codicon } from 'vs/base/common/codicons'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; -const _ctxHasCallHierarchyProvider = new RawContextKey('editorHasCallHierarchyProvider', false); -const _ctxCallHierarchyVisible = new RawContextKey('callHierarchyVisible', false); -const _ctxCallHierarchyDirection = new RawContextKey('callHierarchyDirection', undefined); +const _ctxHasCallHierarchyProvider = new RawContextKey('editorHasCallHierarchyProvider', false, localize('editorHasCallHierarchyProvider', 'Whether a call hierarchy provider is available')); +const _ctxCallHierarchyVisible = new RawContextKey('callHierarchyVisible', false, localize('callHierarchyVisible', 'Whether call hierarchy peek is currently showing')); +const _ctxCallHierarchyDirection = new RawContextKey('callHierarchyDirection', undefined, { type: 'string', description: localize('callHierarchyDirection', 'Whether call hierarchy shows incoming or outgoing calls') }); function sanitizedDirection(candidate: string): CallHierarchyDirection { return candidate === CallHierarchyDirection.CallsFrom || candidate === CallHierarchyDirection.CallsTo diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index a257c4d65bb..07ed0eec5e9 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -688,9 +688,7 @@ class FunctionBreakpointInputRenderer implements IListRenderer { // Need to react with a timeout on the blur event due to possible concurent splices #56443 setTimeout(() => { - if (!template.breakpoint.name) { - wrapUp(true); - } + wrapUp(!!inputBox.value); }); })); @@ -858,11 +856,11 @@ export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolea startColumn: breakpoint.column || 1, endColumn: breakpoint.endColumn || Constants.MAX_SAFE_SMALL_INTEGER } : { - startLineNumber: breakpoint.lineNumber, - startColumn: breakpoint.column || 1, - endLineNumber: breakpoint.lineNumber, - endColumn: breakpoint.column || Constants.MAX_SAFE_SMALL_INTEGER - }; + startLineNumber: breakpoint.lineNumber, + startColumn: breakpoint.column || 1, + endLineNumber: breakpoint.lineNumber, + endColumn: breakpoint.column || Constants.MAX_SAFE_SMALL_INTEGER + }; return editorService.openEditor({ resource: breakpoint.uri, diff --git a/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts index b600fc9943f..c6021398d25 100644 --- a/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts +++ b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IAction, IActionRunner, IActionViewItem } from 'vs/base/common/actions'; +import { IAction } from 'vs/base/common/actions'; import { KeyCode } from 'vs/base/common/keyCodes'; import * as dom from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -19,20 +19,19 @@ import { IContextViewService } from 'vs/platform/contextview/browser/contextView import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ADD_CONFIGURATION_ID } from 'vs/workbench/contrib/debug/browser/debugCommands'; -import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { BaseActionViewItem, SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { debugStart } from 'vs/workbench/contrib/debug/browser/debugIcons'; const $ = dom.$; -export class StartDebugActionViewItem implements IActionViewItem { +export class StartDebugActionViewItem extends BaseActionViewItem { private static readonly SEPARATOR = '─────────'; - actionRunner!: IActionRunner; private container!: HTMLElement; private start!: HTMLElement; private selectBox: SelectBox; - private options: { label: string, handler: (() => Promise) }[] = []; + private debugOptions: { label: string, handler: (() => Promise) }[] = []; private toDispose: IDisposable[]; private selected = 0; private providers: { label: string, type: string, pick: () => Promise<{ launch: ILaunch, config: IConfig } | undefined> }[] = []; @@ -47,6 +46,7 @@ export class StartDebugActionViewItem implements IActionViewItem { @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IContextViewService contextViewService: IContextViewService, ) { + super(context, action); this.toDispose = []; this.selectBox = new SelectBox([], -1, contextViewService, undefined, { ariaLabel: nls.localize('debugLaunchConfigurations', 'Debug Launch Configurations') }); this.toDispose.push(this.selectBox); @@ -72,7 +72,6 @@ export class StartDebugActionViewItem implements IActionViewItem { this.start = dom.append(container, $(ThemeIcon.asCSSSelector(debugStart))); this.start.title = this.action.label; this.start.setAttribute('role', 'button'); - this.start.tabIndex = 0; this.toDispose.push(dom.addDisposableListener(this.start, dom.EventType.CLICK, () => { this.start.blur(); @@ -104,7 +103,7 @@ export class StartDebugActionViewItem implements IActionViewItem { } })); this.toDispose.push(this.selectBox.onDidSelect(async e => { - const target = this.options[e.index]; + const target = this.debugOptions[e.index]; const shouldBeSelected = target.handler ? await target.handler() : false; if (shouldBeSelected) { this.selected = e.index; @@ -151,11 +150,13 @@ export class StartDebugActionViewItem implements IActionViewItem { if (fromRight) { this.selectBox.focus(); } else { + this.start.tabIndex = 0; this.start.focus(); } } blur(): void { + this.start.tabIndex = -1; this.container.blur(); } @@ -165,7 +166,7 @@ export class StartDebugActionViewItem implements IActionViewItem { private updateOptions(): void { this.selected = 0; - this.options = []; + this.debugOptions = []; const manager = this.debugService.getConfigurationManager(); const inWorkspace = this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE; let lastGroup: string | undefined; @@ -173,17 +174,17 @@ export class StartDebugActionViewItem implements IActionViewItem { manager.getAllConfigurations().forEach(({ launch, name, presentation }) => { if (lastGroup !== presentation?.group) { lastGroup = presentation?.group; - if (this.options.length) { - this.options.push({ label: StartDebugActionViewItem.SEPARATOR, handler: () => Promise.resolve(false) }); - disabledIdxs.push(this.options.length - 1); + if (this.debugOptions.length) { + this.debugOptions.push({ label: StartDebugActionViewItem.SEPARATOR, handler: () => Promise.resolve(false) }); + disabledIdxs.push(this.debugOptions.length - 1); } } if (name === manager.selectedConfiguration.name && launch === manager.selectedConfiguration.launch) { - this.selected = this.options.length; + this.selected = this.debugOptions.length; } const label = inWorkspace ? `${name} (${launch.name})` : name; - this.options.push({ + this.debugOptions.push({ label, handler: async () => { await manager.selectConfiguration(launch, name); return true; @@ -194,9 +195,9 @@ export class StartDebugActionViewItem implements IActionViewItem { // Only take 3 elements from the recent dynamic configurations to not clutter the dropdown manager.getRecentDynamicConfigurations().slice(0, 3).forEach(({ name, type }) => { if (type === manager.selectedConfiguration.type && manager.selectedConfiguration.name === name) { - this.selected = this.options.length; + this.selected = this.debugOptions.length; } - this.options.push({ + this.debugOptions.push({ label: name, handler: async () => { await manager.selectConfiguration(undefined, name, undefined, { type }); @@ -205,16 +206,16 @@ export class StartDebugActionViewItem implements IActionViewItem { }); }); - if (this.options.length === 0) { - this.options.push({ label: nls.localize('noConfigurations', "No Configurations"), handler: async () => false }); + if (this.debugOptions.length === 0) { + this.debugOptions.push({ label: nls.localize('noConfigurations', "No Configurations"), handler: async () => false }); } - this.options.push({ label: StartDebugActionViewItem.SEPARATOR, handler: () => Promise.resolve(false) }); - disabledIdxs.push(this.options.length - 1); + this.debugOptions.push({ label: StartDebugActionViewItem.SEPARATOR, handler: () => Promise.resolve(false) }); + disabledIdxs.push(this.debugOptions.length - 1); this.providers.forEach(p => { - this.options.push({ + this.debugOptions.push({ label: `${p.label}...`, handler: async () => { const picked = await p.pick(); @@ -229,7 +230,7 @@ export class StartDebugActionViewItem implements IActionViewItem { manager.getLaunches().filter(l => !l.hidden).forEach(l => { const label = inWorkspace ? nls.localize("addConfigTo", "Add Config ({0})...", l.name) : nls.localize('addConfiguration', "Add Configuration..."); - this.options.push({ + this.debugOptions.push({ label, handler: async () => { await this.commandService.executeCommand(ADD_CONFIGURATION_ID, l.uri.toString()); return false; @@ -237,7 +238,7 @@ export class StartDebugActionViewItem implements IActionViewItem { }); }); - this.selectBox.setOptions(this.options.map((data, index) => { text: data.label, isDisabled: disabledIdxs.indexOf(index) !== -1 }), this.selected); + this.selectBox.setOptions(this.debugOptions.map((data, index) => { text: data.label, isDisabled: disabledIdxs.indexOf(index) !== -1 }), this.selected); } } diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts index f3fa36a2c2e..57ffcf9ef3e 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts @@ -11,7 +11,10 @@ import { setProperty } from 'vs/base/common/jsonEdit'; import { Constants } from 'vs/base/common/uint'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { StandardTokenType } from 'vs/editor/common/modes'; +import { InlineValuesProviderRegistry, StandardTokenType } from 'vs/editor/common/modes'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { flatten } from 'vs/base/common/arrays'; +import { onUnexpectedExternalError } from 'vs/base/common/errors'; import { DEFAULT_WORD_REGEXP } from 'vs/editor/common/model/wordHelper'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType, IPartialEditorMouseEvent } from 'vs/editor/browser/editorBrowser'; import { IDecorationOptions } from 'vs/editor/common/editorCommon'; @@ -47,7 +50,12 @@ const MAX_NUM_INLINE_VALUES = 100; // JS Global scope can have 700+ entries. We 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 -function createInlineValueDecoration(lineNumber: number, contentText: string): IDecorationOptions { +class InlineSegment { + constructor(public column: number, public text: string) { + } +} + +function createInlineValueDecoration(lineNumber: number, contentText: string, column = Constants.MAX_SAFE_SMALL_INTEGER): IDecorationOptions { // If decoratorText is too long, trim and add ellipses. This could happen for minified files with everything on a single line if (contentText.length > MAX_INLINE_DECORATOR_LENGTH) { contentText = contentText.substr(0, MAX_INLINE_DECORATOR_LENGTH) + '...'; @@ -57,8 +65,8 @@ function createInlineValueDecoration(lineNumber: number, contentText: string): I range: { startLineNumber: lineNumber, endLineNumber: lineNumber, - startColumn: Constants.MAX_SAFE_SMALL_INTEGER, - endColumn: Constants.MAX_SAFE_SMALL_INTEGER + startColumn: column, + endColumn: column }, renderOptions: { after: { @@ -572,20 +580,100 @@ export class DebugEditorContribution implements IDebugEditorContribution { this.removeInlineValuesScheduler.cancel(); - const scopes = await stackFrame.getMostSpecificScopes(stackFrame.range); - // Get all top level children in the scope chain - const decorationsPerScope = await Promise.all(scopes.map(async scope => { - const children = await scope.getChildren(); - let range = new Range(0, 0, stackFrame.range.startLineNumber, stackFrame.range.startColumn); - if (scope.range) { - range = range.setStartPosition(scope.range.startLineNumber, scope.range.startColumn); - } + let allDecorations: IDecorationOptions[]; - return createInlineValueDecorationsInsideRange(children, range, model, this.wordToLineNumbersMap); - })); + if (InlineValuesProviderRegistry.has(model)) { + const findVariable = async (_key: string, caseSensitiveLookup: boolean): Promise => { + const scopes = await stackFrame.getMostSpecificScopes(stackFrame.range); + const key = caseSensitiveLookup ? _key : _key.toLowerCase(); + for (let scope of scopes) { + const variables = await scope.getChildren(); + const found = variables.find(v => caseSensitiveLookup ? (v.name === key) : (v.name.toLowerCase() === key)); + if (found) { + return found.value; + } + } + return undefined; + }; + + const ranges = this.editor.getVisibleRangesPlusViewportAboveBelow(); + + const ctx = { stoppedLocation: new Range(1, 1, stackFrame.range.startLineNumber, stackFrame.range.startColumn) }; + const token = new CancellationTokenSource().token; + + const providers = InlineValuesProviderRegistry.ordered(model).reverse(); + + allDecorations = []; + + const lineDecorations = new Map(); + + const promises = flatten(providers.map(provider => ranges.map(range => Promise.resolve(provider.provideInlineValues(model, range, ctx, token)).then(async (result) => { + if (result) { + for (let iv of result) { + + let text: string | undefined = undefined; + switch (iv.type) { + case 'text': + text = iv.text; + break; + case 'variable': + const value = await findVariable(iv.variableName, iv.caseSensitiveLookup); + if (value) { + text = `${iv.variableName} = ${value}`; + } + break; + case 'expression': + text = `eval(${iv.expression})`; + break; + } + + if (text) { + const line = iv.range.startLineNumber; + let lineSegments = lineDecorations.get(line); + if (!lineSegments) { + lineSegments = []; + lineDecorations.set(line, lineSegments); + } + lineSegments.push(new InlineSegment(range.startColumn, text)); + } + } + } + }, err => { + onUnexpectedExternalError(err); + })))); + + await Promise.all(promises); + + // sort line segments and concatenate them into a decoration + + lineDecorations.forEach((segments, line) => { + if (segments.length > 0) { + segments = segments.sort((a, b) => a.column - b.column); + const text = segments.map(s => s.text).join(', '); + allDecorations.push(createInlineValueDecoration(line, text)); + } + }); + + } else { + // old "one-size-fits-all" strategy + + const scopes = await stackFrame.getMostSpecificScopes(stackFrame.range); + // Get all top level variables in the scope chain + const decorationsPerScope = await Promise.all(scopes.map(async scope => { + const variables = await scope.getChildren(); + + let range = new Range(0, 0, stackFrame.range.startLineNumber, stackFrame.range.startColumn); + if (scope.range) { + range = range.setStartPosition(scope.range.startLineNumber, scope.range.startColumn); + } + + return createInlineValueDecorationsInsideRange(variables, range, model, this.wordToLineNumbersMap); + })); + + allDecorations = decorationsPerScope.reduce((previous, current) => previous.concat(current), []); + } - const allDecorations = decorationsPerScope.reduce((previous, current) => previous.concat(current), []); this.editor.setDecorations(INLINE_VALUE_DECORATION_KEY, allDecorations); } diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index 59b05dd4866..451dcceccff 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -183,7 +183,8 @@ export class ExplorerView extends ViewPane { @IClipboardService private clipboardService: IClipboardService, @IFileService private readonly fileService: IFileService, @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, - @IOpenerService openerService: IOpenerService, + @ICommandService private readonly commandService: ICommandService, + @IOpenerService openerService: IOpenerService ) { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); @@ -459,6 +460,13 @@ export class ExplorerView extends ViewPane { } })); + this._register(this.tree.onMouseDblClick(e => { + if (e.element === null) { + // click in empty area -> create a new file #116676 + this.commandService.executeCommand(NEW_FILE_COMMAND_ID); + } + })); + // save view state this._register(this.storageService.onWillSaveState(() => { this.storageService.store(ExplorerView.TREE_VIEW_STATE_STORAGE_KEY, JSON.stringify(this.tree.getViewState()), StorageScope.WORKSPACE, StorageTarget.MACHINE); diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 6bf781d2e31..67fdc77b546 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -1229,7 +1229,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { res = await reader.read(); } - writeableStream.end(res.value instanceof Uint8Array ? VSBuffer.wrap(res.value) : undefined); + writeableStream.end(undefined); } catch (error) { writeableStream.end(error); } diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/issueService.ts b/src/vs/workbench/contrib/issue/electron-sandbox/issueService.ts index 1deb2d639b0..94beb1646ae 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/issueService.ts +++ b/src/vs/workbench/contrib/issue/electron-sandbox/issueService.ts @@ -54,7 +54,7 @@ export class WorkbenchIssueService implements IWorkbenchIssueService { }; }); const experiments = await this.experimentService.getCurrentExperiments(); - const githubSessions = await this.authenticationService.getAllSessions('github'); + const githubSessions = await this.authenticationService.getSessions('github'); const potentialSessions = githubSessions.filter(session => session.scopes.includes('repo')); const theme = this.themeService.getColorTheme(); const issueReporterData: IssueReporterData = Object.assign({ diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index 9705204ab05..56f85535ae3 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -21,7 +21,7 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; import { CATEGORIES } from 'vs/workbench/common/actions'; -import { BaseCellRenderTemplate, CellEditState, CellFocusMode, EXECUTE_CELL_COMMAND_ID, EXPAND_CELL_CONTENT_COMMAND_ID, IActiveNotebookEditor, ICellViewModel, INotebookEditor, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { BaseCellRenderTemplate, CellEditState, CellFocusMode, EXECUTE_CELL_COMMAND_ID, EXPAND_CELL_CONTENT_COMMAND_ID, IActiveNotebookEditor, ICellViewModel, INotebookEditor, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_KERNEL_COUNT, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { CellEditType, CellKind, ICellEditOperation, ICellRange, INotebookDocumentFilter, isDocumentExcludePattern, NotebookCellMetadata, NotebookCellRunState, NOTEBOOK_EDITOR_CURSOR_BEGIN_END, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; @@ -228,10 +228,11 @@ export function getWidgetFromUri(accessor: ServicesAccessor, uri: URI) { return undefined; } -registerAction2(class extends NotebookCellAction { +registerAction2(class ExecuteCell extends NotebookCellAction { constructor() { super({ id: EXECUTE_CELL_COMMAND_ID, + precondition: ContextKeyExpr.or(ContextKeyExpr.greater(NOTEBOOK_KERNEL_COUNT.key, 0), NOTEBOOK_CELL_TYPE.isEqualTo('markdown')), title: localize('notebookActions.execute', "Execute Cell"), keybinding: { when: NOTEBOOK_CELL_LIST_FOCUSED, @@ -312,7 +313,7 @@ registerAction2(class extends NotebookCellAction { } }); -registerAction2(class extends NotebookCellAction { +registerAction2(class StopExecuteCell extends NotebookCellAction { constructor() { super({ id: CANCEL_CELL_COMMAND_ID, @@ -442,10 +443,11 @@ export class DeleteCellAction extends MenuItemAction { } } -registerAction2(class extends NotebookCellAction { +registerAction2(class ExecuteCellSelectBelow extends NotebookCellAction { constructor() { super({ id: EXECUTE_CELL_SELECT_BELOW, + precondition: ContextKeyExpr.or(ContextKeyExpr.greater(NOTEBOOK_KERNEL_COUNT.key, 0), NOTEBOOK_CELL_TYPE.isEqualTo('markdown')), title: localize('notebookActions.executeAndSelectBelow', "Execute Notebook Cell and Select Below"), keybinding: { when: NOTEBOOK_CELL_LIST_FOCUSED, @@ -478,10 +480,11 @@ registerAction2(class extends NotebookCellAction { } }); -registerAction2(class extends NotebookCellAction { +registerAction2(class ExecuteCellInsertBelow extends NotebookCellAction { constructor() { super({ id: EXECUTE_CELL_INSERT_BELOW, + precondition: ContextKeyExpr.or(ContextKeyExpr.greater(NOTEBOOK_KERNEL_COUNT.key, 0), NOTEBOOK_CELL_TYPE.isEqualTo('markdown')), title: localize('notebookActions.executeAndInsertBelow', "Execute Notebook Cell and Insert Below"), keybinding: { when: NOTEBOOK_CELL_LIST_FOCUSED, @@ -1604,19 +1607,81 @@ interface ILanguagePickInput extends IQuickPickItem { description: string; } -export class ChangeCellLanguageAction extends NotebookCellAction { + +interface IChangeCellContext extends INotebookCellActionContext { + // TODO@rebornix : `cells` + // range: ICellRange; + language?: string; +} + +export class ChangeCellLanguageAction extends NotebookCellAction { constructor() { super({ id: CHANGE_CELL_LANGUAGE, title: localize('changeLanguage', 'Change Cell Language'), + description: { + description: localize('changeLanguage', 'Change Cell Language'), + args: [ + { + name: 'range', + description: 'The cell range', + schema: { + 'type': 'object', + 'required': ['start', 'end'], + 'properties': { + 'start': { + 'type': 'number' + }, + 'end': { + 'type': 'number' + } + } + } + }, + { + name: 'language', + description: 'The target cell language', + schema: { + 'type': 'string' + } + } + ] + } }); } - async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { - this.showLanguagePicker(accessor, context); + protected getCellContextFromArgs(accessor: ServicesAccessor, context?: ICellRange, ...additionalArgs: any[]): IChangeCellContext | undefined { + if (!context || typeof context.start !== 'number' || typeof context.end !== 'number' || context.start >= context.end) { + return; + } + + const language = additionalArgs.length && typeof additionalArgs[0] === 'string' ? additionalArgs[0] : undefined; + const activeEditorContext = this.getEditorContextFromArgsOrActive(accessor); + + if (!activeEditorContext || !activeEditorContext.notebookEditor.viewModel || context.start >= activeEditorContext.notebookEditor.viewModel.viewCells.length) { + return; + } + + const cells = activeEditorContext.notebookEditor.viewModel.viewCells; + + // TODO@rebornix, support multiple cells + return { + notebookEditor: activeEditorContext.notebookEditor, + cell: cells[context.start], + language + }; } - private async showLanguagePicker(accessor: ServicesAccessor, context: INotebookCellActionContext) { + + async runWithContext(accessor: ServicesAccessor, context: IChangeCellContext): Promise { + if (context.language) { + await this.setLanguage(context, context.language); + } else { + await this.showLanguagePicker(accessor, context); + } + } + + private async showLanguagePicker(accessor: ServicesAccessor, context: IChangeCellContext) { const topItems: ILanguagePickInput[] = []; const mainItems: ILanguagePickInput[] = []; @@ -1668,21 +1733,25 @@ export class ChangeCellLanguageAction extends NotebookCellAction { const selection = await quickInputService.pick(picks, { placeHolder: localize('pickLanguageToConfigure', "Select Language Mode") }) as ILanguagePickInput | undefined; if (selection && selection.languageId) { - if (selection.languageId === 'markdown' && context.cell?.language !== 'markdown') { - const newCell = await changeCellToKind(CellKind.Markdown, { cell: context.cell, notebookEditor: context.notebookEditor }, 'markdown'); - if (newCell) { - context.notebookEditor.focusNotebookCell(newCell, 'editor'); - } - } else if (selection.languageId !== 'markdown' && context.cell?.cellKind === CellKind.Markdown) { - await changeCellToKind(CellKind.Code, { cell: context.cell, notebookEditor: context.notebookEditor }, selection.languageId); - } else { - const index = context.notebookEditor.viewModel.notebookDocument.cells.indexOf(context.cell.model); - context.notebookEditor.viewModel.notebookDocument.applyEdits( - context.notebookEditor.viewModel.notebookDocument.versionId, - [{ editType: CellEditType.CellLanguage, index, language: selection.languageId }], - true, undefined, () => undefined, undefined - ); + await this.setLanguage(context, selection.languageId); + } + } + + private async setLanguage(context: IChangeCellContext, languageId: string) { + if (languageId === 'markdown' && context.cell?.language !== 'markdown') { + const newCell = await changeCellToKind(CellKind.Markdown, { cell: context.cell, notebookEditor: context.notebookEditor }, 'markdown'); + if (newCell) { + context.notebookEditor.focusNotebookCell(newCell, 'editor'); } + } else if (languageId !== 'markdown' && context.cell?.cellKind === CellKind.Markdown) { + await changeCellToKind(CellKind.Code, { cell: context.cell, notebookEditor: context.notebookEditor }, languageId); + } else { + const index = context.notebookEditor.viewModel.notebookDocument.cells.indexOf(context.cell.model); + context.notebookEditor.viewModel.notebookDocument.applyEdits( + context.notebookEditor.viewModel.notebookDocument.versionId, + [{ editType: CellEditType.CellLanguage, index, language: languageId }], + true, undefined, () => undefined, undefined + ); } } diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index 3d015269f38..543decf9627 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -287,6 +287,7 @@ position: absolute; width: 1px; height: 100%; + z-index: 1; } /* top border */ diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts index 80bb8e33897..cd68acce0ec 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts @@ -160,11 +160,12 @@ ${patterns} if (!this._textModel) { this._textModel = await this._notebookModelResolverService.resolve(this.resource, this.viewType!); - - this._register(this._textModel.object.onDidChangeDirty(() => { - this._onDidChangeDirty.fire(); - })); - + if (this.isDisposed()) { + this._textModel.dispose(); + this._textModel = null; + return null; + } + this._register(this._textModel.object.onDidChangeDirty(() => this._onDidChangeDirty.fire())); if (this._textModel.object.isDirty()) { this._onDidChangeDirty.fire(); } @@ -185,10 +186,8 @@ ${patterns} } dispose() { - if (this._textModel) { - this._textModel.dispose(); - this._textModel = null; - } + this._textModel?.dispose(); + this._textModel = null; super.dispose(); } } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 6f8e12d51b9..862443a95d5 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -2532,6 +2532,7 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.notebookOverlay .cell-list-top-cell-toolbar-container { top: -${SCROLLABLE_ELEMENT_PADDING_TOP}px }`); collector.addRule(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container { height: ${BOTTOM_CELL_TOOLBAR_HEIGHT}px }`); + collector.addRule(`.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.focused .cell-focus-indicator-left:before, .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.focused .cell-focus-indicator-right:before { top: -${CELL_TOP_MARGIN}px; height: calc(100% + ${CELL_TOP_MARGIN + CELL_BOTTOM_MARGIN}px)}`); }); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 3e6484a7baa..5049ff03ab4 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -680,6 +680,9 @@ export class NotebookService extends Disposable implements INotebookService, ICu } registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: IMainNotebookController): IDisposable { + if (this._notebookProviders.has(viewType)) { + throw new Error(`notebook controller for viewtype '${viewType}' already exists`); + } this._notebookProviders.set(viewType, { extensionData, controller }); if (controller.viewOptions && !this.notebookProviderInfoStore.get(viewType)) { @@ -765,7 +768,7 @@ export class NotebookService extends Disposable implements INotebookService, ICu return notebookModel; } else { - const dataDto = await provider.controller.resolveNotebookDocument(viewType, uri, backupId); + const dataDto = await provider.controller.openNotebook(viewType, uri, backupId); let cells = dataDto.data.cells.length ? dataDto.data.cells : (uri.scheme === Schemas.untitled ? [{ cellKind: CellKind.Code, language: 'plaintext', //TODO@jrieken unsure what this is 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 05567647704..c976351d704 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -266,8 +266,8 @@ abstract class AbstractCellRenderer { if (actions.primary.length || actions.secondary.length) { templateData.container.classList.add('cell-has-toolbar-actions'); if (isCodeCellRenderTemplate(templateData)) { - templateData.focusIndicatorLeft.style.top = `${EDITOR_TOOLBAR_HEIGHT}px`; - templateData.focusIndicatorRight.style.top = `${EDITOR_TOOLBAR_HEIGHT}px`; + templateData.focusIndicatorLeft.style.top = `${EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN}px`; + templateData.focusIndicatorRight.style.top = `${EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN}px`; } } else { templateData.container.classList.remove('cell-has-toolbar-actions'); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts index 03a42b55550..b45001f687b 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts @@ -308,9 +308,8 @@ export class StatefulMarkdownCell extends Disposable { this.templateData.editorContainer.innerText = ''; // create a special context key service that set the inCompositeEditor-contextkey - const editorContextKeyService = this.contextKeyService.createOverlay([ - [EditorContextKeys.inCompositeEditor.key, true] - ]); + const editorContextKeyService = this.contextKeyService.createScoped(this.templateData.editorPart); + EditorContextKeys.inCompositeEditor.bindTo(editorContextKeyService).set(true); const editorInstaService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, editorContextKeyService])); this.editor = editorInstaService.createInstance(CodeEditorWidget, this.templateData.editorContainer, { diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts index c59b6d52458..bda35e8bd33 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts @@ -131,7 +131,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod } const statusbarHeight = this.getEditorStatusbarHeight(); - const indicatorHeight = editorHeight + statusbarHeight + outputTotalHeight + outputShowMoreContainerHeight + CELL_TOP_MARGIN + CELL_BOTTOM_MARGIN; + const indicatorHeight = editorHeight + statusbarHeight + outputTotalHeight + outputShowMoreContainerHeight; const outputContainerOffset = EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN + editorHeight + statusbarHeight; const outputShowMoreContainerOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2 - outputShowMoreContainerHeight; const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2; @@ -152,7 +152,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod }; } else { outputTotalHeight = this.metadata?.inputCollapsed && this.metadata.outputCollapsed ? 0 : outputTotalHeight; - const indicatorHeight = COLLAPSED_INDICATOR_HEIGHT + outputTotalHeight + outputShowMoreContainerHeight + CELL_TOP_MARGIN + CELL_BOTTOM_MARGIN; + const indicatorHeight = COLLAPSED_INDICATOR_HEIGHT + outputTotalHeight + outputShowMoreContainerHeight; const outputContainerOffset = CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT; const totalHeight = CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT + CELL_BOTTOM_MARGIN + BOTTOM_CELL_TOOLBAR_GAP + outputTotalHeight + outputShowMoreContainerHeight; const outputShowMoreContainerOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2 - outputShowMoreContainerHeight; diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookCellOutputTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookCellOutputTextModel.ts index 0172d6a1f2d..aa05e729203 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookCellOutputTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookCellOutputTextModel.ts @@ -16,9 +16,11 @@ export class NotebookCellOutputTextModel extends Disposable implements ICellOutp get outputs() { return this._rawOutput.outputs || []; } - // get metadata(): NotebookCellOutputMetadata | undefined { - // return this._rawOutput.metadata; - // } + + get metadata(): Record | undefined { + return this._rawOutput.metadata; + } + get outputId(): string { return this._rawOutput.outputId; } @@ -47,10 +49,10 @@ export class NotebookCellOutputTextModel extends Disposable implements ICellOutp this._onDidChangeData.fire(); } - toJSON() { + toJSON(): IOutputDto { return { // data: this._data, - // metadata: this._rawOutput.metadata, + metadata: this._rawOutput.metadata, outputs: this._rawOutput.outputs, outputId: this._rawOutput.outputId }; diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index 3885b5e3d02..5d5364651e5 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -661,6 +661,14 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel const output = cell.outputs[outputIndex]; output.appendData(items); + this._eventEmitter.emit({ + kind: NotebookCellsChangeType.OutputItem, + index: this._cells.indexOf(cell), + outputId: output.outputId, + outputItems: items, + append: true, + transient: this.transientOptions.transientOutputs + }, true); } private _replaceNotebookCellOutputItems(cellHandle: number, outputId: string, items: IOutputItemDto[]) { @@ -677,6 +685,14 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel const output = cell.outputs[outputIndex]; output.replaceData(items); + this._eventEmitter.emit({ + kind: NotebookCellsChangeType.OutputItem, + index: this._cells.indexOf(cell), + outputId: output.outputId, + outputItems: items, + append: false, + transient: this.transientOptions.transientOutputs + }, true); } private _moveCellToIdx(index: number, length: number, newIdx: number, synchronous: boolean, pushedToUndoStack: boolean, beforeSelections: number[] | undefined, endSelections: number[] | undefined): boolean { diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index dee355a50bb..8196d8ac5f2 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -159,13 +159,8 @@ export interface IOutputItemDto { export interface IOutputDto { outputs: IOutputItemDto[]; - /** - * { mime_type: value } - */ - // data: { [key: string]: unknown; } - - // metadata?: NotebookCellOutputMetadata; outputId: string; + metadata?: Record; } export interface ICellOutput { @@ -237,9 +232,10 @@ export enum NotebookCellsChangeType { Initialize = 6, ChangeCellMetadata = 7, Output = 8, - ChangeCellContent = 9, - ChangeDocumentMetadata = 10, - Unknown = 11 + OutputItem = 9, + ChangeCellContent = 10, + ChangeDocumentMetadata = 11, + Unknown = 12 } export interface NotebookCellsInitializeEvent { @@ -270,6 +266,14 @@ export interface NotebookOutputChangedEvent { readonly outputs: IOutputDto[]; } +export interface NotebookOutputItemChangedEvent { + readonly kind: NotebookCellsChangeType.OutputItem; + readonly index: number; + readonly outputId: string; + readonly outputItems: IOutputItemDto[]; + readonly append: boolean; +} + export interface NotebookCellsChangeLanguageEvent { readonly kind: NotebookCellsChangeType.ChangeLanguage; readonly index: number; @@ -291,14 +295,14 @@ export interface NotebookDocumentUnknownChangeEvent { readonly kind: NotebookCellsChangeType.Unknown; } -export type NotebookRawContentEventDto = NotebookCellsInitializeEvent | NotebookDocumentChangeMetadataEvent | NotebookCellContentChangeEvent | NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookOutputChangedEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent | NotebookDocumentUnknownChangeEvent; +export type NotebookRawContentEventDto = NotebookCellsInitializeEvent | NotebookDocumentChangeMetadataEvent | NotebookCellContentChangeEvent | NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookOutputChangedEvent | NotebookOutputItemChangedEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent | NotebookDocumentUnknownChangeEvent; export type NotebookCellsChangedEventDto = { readonly rawEvents: NotebookRawContentEventDto[]; readonly versionId: number; }; -export type NotebookRawContentEvent = (NotebookCellsInitializeEvent | NotebookDocumentChangeMetadataEvent | NotebookCellContentChangeEvent | NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookOutputChangedEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent | NotebookDocumentUnknownChangeEvent) & { transient: boolean; }; +export type NotebookRawContentEvent = (NotebookCellsInitializeEvent | NotebookDocumentChangeMetadataEvent | NotebookCellContentChangeEvent | NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookOutputChangedEvent | NotebookOutputItemChangedEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent | NotebookDocumentUnknownChangeEvent) & { transient: boolean; }; export type NotebookTextModelChangedEvent = { readonly rawEvents: NotebookRawContentEvent[]; readonly versionId: number; diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts index 1ad9b60a8b6..583dad5a8cf 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts @@ -125,6 +125,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM async revert(options?: IRevertOptions | undefined): Promise { if (options?.soft) { await this._backupFileService.discardBackup(this.resource); + this.setDirty(false); return; } diff --git a/src/vs/workbench/contrib/notebook/common/notebookService.ts b/src/vs/workbench/contrib/notebook/common/notebookService.ts index b0faba9fcd8..c8cdc11f85a 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookService.ts @@ -23,7 +23,7 @@ export interface IMainNotebookController { supportBackup: boolean; viewOptions?: { displayName: string; filenamePattern: (string | IRelativePattern | INotebookExclusiveDocumentFilter)[]; exclusive: boolean; }; options: { transientOutputs: boolean; transientMetadata: TransientMetadata; }; - resolveNotebookDocument(viewType: string, uri: URI, backupId?: string): Promise<{ data: NotebookDataDto, transientOptions: TransientOptions; }>; + openNotebook(viewType: string, uri: URI, backupId?: string): Promise<{ data: NotebookDataDto, transientOptions: TransientOptions; }>; reloadNotebook(mainthreadTextModel: NotebookTextModel): Promise; resolveNotebookEditor(viewType: string, uri: URI, editorId: string): Promise; onDidReceiveMessage(editorId: string, rendererType: string | undefined, message: any): void; diff --git a/src/vs/workbench/contrib/outline/browser/outlinePane.ts b/src/vs/workbench/contrib/outline/browser/outlinePane.ts index 5a3c4048178..e988a1a3a9d 100644 --- a/src/vs/workbench/contrib/outline/browser/outlinePane.ts +++ b/src/vs/workbench/contrib/outline/browser/outlinePane.ts @@ -231,7 +231,7 @@ export class OutlinePane extends ViewPane { { ...newOutline.config.options, sorter, - openOnSingleClick: true, + expandOnDoubleClick: false, expandOnlyOnTwistieClick: true, multipleSelectionSupport: false, hideTwistiesOfChildlessElements: true, diff --git a/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts b/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts index 93974d117c3..4b810831bab 100644 --- a/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts +++ b/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts @@ -156,7 +156,7 @@ class PerfModelContentProvider implements ITextModelContentProvider { if (metrics.meminfo) { md.li(`Memory(Process): ${(metrics.meminfo.workingSetSize / ByteSize.KB).toFixed(2)} MB working set(${(metrics.meminfo.privateBytes / ByteSize.KB).toFixed(2)}MB private, ${(metrics.meminfo.sharedBytes / ByteSize.KB).toFixed(2)}MB shared)`); } - md.li(`VM(likelyhood): ${metrics.isVMLikelyhood}%`); + md.li(`VM(likelihood): ${metrics.isVMLikelyhood}%`); md.li(`Initial Startup: ${metrics.initialStartup}`); md.li(`Has ${metrics.windowCount - 1} other windows`); md.li(`Screen Reader Active: ${metrics.hasAccessibilitySupport}`); @@ -174,6 +174,7 @@ class PerfModelContentProvider implements ITextModelContentProvider { table.push(['window.loadUrl() => begin to require(workbench.desktop.main.js)', metrics.timers.ellapsedWindowLoadToRequire, '[main->renderer]', StartupKindToString(metrics.windowKind)]); table.push(['require(workbench.desktop.main.js)', metrics.timers.ellapsedRequire, '[renderer]', `cached data: ${(metrics.didUseCachedData ? 'YES' : 'NO')}${stats ? `, node_modules took ${stats.nodeRequireTotal}ms` : ''}`]); table.push(['wait for shell environment', metrics.timers.ellapsedWaitForShellEnv, '[renderer]', undefined]); + table.push(['init storage (global & workspace)', metrics.timers.ellapsedStorageInit, '[renderer]', undefined]); table.push(['require & init workspace storage', metrics.timers.ellapsedWorkspaceStorageInit, '[renderer]', undefined]); table.push(['init workspace service', metrics.timers.ellapsedWorkspaceServiceInit, '[renderer]', undefined]); if (isWeb) { diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 5f4734250dd..8d7405e04fe 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -61,7 +61,7 @@ export const enum SettingsFocusContext { SettingControl } -function createGroupIterator(group: SettingsTreeGroupElement): Iterable> { +export function createGroupIterator(group: SettingsTreeGroupElement): Iterable> { return Iterable.map(group.children, g => { return { element: g, diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 6cee6eb3ffe..298ac757dd9 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -1828,7 +1828,7 @@ class SettingsTreeDelegate extends CachedListVirtualDelegate extends ObjectTreeModel { +export class NonCollapsibleObjectTreeModel extends ObjectTreeModel { isCollapsible(element: T): boolean { return false; } diff --git a/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts b/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts index f3ba2a45148..e0c1a0c44cb 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts @@ -6,8 +6,8 @@ import * as nls from 'vs/nls'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { Extensions, IViewContainersRegistry, IViewDescriptorService, IViewsRegistry, IViewsService, ViewContainer, ViewContainerLocation } from 'vs/workbench/common/views'; -import { IRemoteExplorerService, makeAddress, mapHasAddressLocalhostOrAllInterfaces, OnPortForward, PortsAttributes, TUNNEL_VIEW_ID } from 'vs/workbench/services/remote/common/remoteExplorerService'; -import { PORT_AUTO_FORWARD_SETTING, forwardedPortsViewEnabled, ForwardPortAction, OpenPortInBrowserAction, TunnelPanel, TunnelPanelDescriptor, TunnelViewModel, OpenPortInPreviewAction } from 'vs/workbench/contrib/remote/browser/tunnelView'; +import { IRemoteExplorerService, makeAddress, mapHasAddressLocalhostOrAllInterfaces, OnPortForward, PortsAttributes, PORT_AUTO_FORWARD_SETTING, PORT_AUTO_SOURCE_SETTING, PORT_AUTO_SOURCE_SETTING_PROCESS, TUNNEL_VIEW_ID } from 'vs/workbench/services/remote/common/remoteExplorerService'; +import { forwardedPortsViewEnabled, ForwardPortAction, OpenPortInBrowserAction, TunnelPanel, TunnelPanelDescriptor, TunnelViewModel, OpenPortInPreviewAction } from 'vs/workbench/contrib/remote/browser/tunnelView'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -219,10 +219,13 @@ export class AutomaticPortForwarding extends Disposable implements IWorkbenchCon this._register(new OutputAutomaticPortForwarding(terminalService, notificationService, openerService, externalOpenerService, remoteExplorerService, configurationService, debugService, tunnelService, remoteAgentService, hostService, false)); } else if (environment?.os === OperatingSystem.Linux) { - this._register(new ProcAutomaticPortForwarding(configurationService, remoteExplorerService, notificationService, - openerService, externalOpenerService, tunnelService, hostService)); + const useProc = (this.configurationService.getValue(PORT_AUTO_SOURCE_SETTING) === PORT_AUTO_SOURCE_SETTING_PROCESS); + if (useProc) { + this._register(new ProcAutomaticPortForwarding(configurationService, remoteExplorerService, notificationService, + openerService, externalOpenerService, tunnelService, hostService)); + } this._register(new OutputAutomaticPortForwarding(terminalService, notificationService, openerService, externalOpenerService, - remoteExplorerService, configurationService, debugService, tunnelService, remoteAgentService, hostService, true)); + remoteExplorerService, configurationService, debugService, tunnelService, remoteAgentService, hostService, useProc)); } }); } @@ -435,7 +438,7 @@ class OutputAutomaticPortForwarding extends Disposable { } this.urlFinder = this._register(new UrlFinder(this.terminalService, this.debugService)); this._register(this.urlFinder.onDidMatchLocalUrl(async (localUrl) => { - if (mapHasAddressLocalhostOrAllInterfaces(this.remoteExplorerService.tunnelModel.forwarded, localUrl.host, localUrl.port)) { + if (mapHasAddressLocalhostOrAllInterfaces(this.remoteExplorerService.tunnelModel.detected, localUrl.host, localUrl.port)) { return; } if (this.portsAttributes.getAttributes(localUrl.port)?.onAutoForward === OnPortForward.Ignore) { @@ -544,6 +547,9 @@ class ProcAutomaticPortForwarding extends Disposable { if (this.initialCandidates.has(address)) { return undefined; } + if (this.notifiedOnly.has(address) || this.autoForwarded.has(address)) { + return undefined; + } const alreadyForwarded = mapHasAddressLocalhostOrAllInterfaces(this.remoteExplorerService.tunnelModel.forwarded, value.host, value.port); if (mapHasAddressLocalhostOrAllInterfaces(this.remoteExplorerService.tunnelModel.detected, value.host, value.port)) { return undefined; diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index daa3fba23f9..d0fb151868d 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -26,7 +26,7 @@ import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { ActionRunner, IAction } from 'vs/base/common/actions'; import { IMenuService, MenuId, IMenu, MenuRegistry, ILocalizedString, Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { createAndFillInContextMenuActions, createAndFillInActionBarActions, createActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IRemoteExplorerService, TunnelModel, makeAddress, TunnelType, ITunnelItem, Tunnel, mapHasAddressLocalhostOrAllInterfaces, TUNNEL_VIEW_ID, parseAddress, CandidatePort, TunnelPrivacy } from 'vs/workbench/services/remote/common/remoteExplorerService'; +import { IRemoteExplorerService, TunnelModel, makeAddress, TunnelType, ITunnelItem, Tunnel, mapHasAddressLocalhostOrAllInterfaces, TUNNEL_VIEW_ID, parseAddress, CandidatePort, TunnelPrivacy, PORT_AUTO_FORWARD_SETTING } from 'vs/workbench/services/remote/common/remoteExplorerService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { InputBox, MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; @@ -48,7 +48,6 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { isWeb } from 'vs/base/common/platform'; export const forwardedPortsViewEnabled = new RawContextKey('forwardedPortsViewEnabled', false); -export const PORT_AUTO_FORWARD_SETTING = 'remote.autoForwardPorts'; class TunnelTreeVirtualDelegate implements IListVirtualDelegate { getHeight(element: ITunnelItem): number { diff --git a/src/vs/workbench/contrib/remote/browser/urlFinder.ts b/src/vs/workbench/contrib/remote/browser/urlFinder.ts index cb3c96dc2f1..ba36c2b5414 100644 --- a/src/vs/workbench/contrib/remote/browser/urlFinder.ts +++ b/src/vs/workbench/contrib/remote/browser/urlFinder.ts @@ -104,7 +104,12 @@ export class UrlFinder extends Disposable { if (urlMatches && urlMatches.length > 0) { urlMatches.forEach((match) => { // check if valid url - const serverUrl = new URL(match); + let serverUrl; + try { + serverUrl = new URL(match); + } catch (e) { + // Not a valid URL + } if (serverUrl) { // check if the port is a valid integer value const portMatch = match.match(UrlFinder.extractPortRegex); diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts index 043cd4876ad..395e5c7cce5 100644 --- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts @@ -134,6 +134,16 @@ Registry.as(ConfigurationExtensions.Configuration) markdownDescription: localize('remote.autoForwardPorts', "When enabled, new running processes are detected and ports that they listen on are automatically forwarded."), default: true }, + 'remote.autoForwardPortsSource': { + type: 'string', + markdownDescription: localize('remote.autoForwardPortsSource', "Sets the source from which ports are automatically forwarded when `remote.autoForwardPorts` is true. Requires a reload to take effect."), + enum: ['process', 'output'], + enumDescriptions: [ + localize('remote.autoForwardPortsSource.process', "Ports will be automatically forwarded when discovered by watching for processes that are started and include a port."), + localize('remote.autoForwardPortsSource.output', "Ports will be automatically forwarded when discovered by reading terminal and debug output. Not all processes that use ports will print to the integrated terminal or debug console, so some ports will be missed. Ports forwarded based on output will not be \"un-forwarded\" until reload or until the port is closed by the user in the Ports view.") + ], + default: 'process' + }, // Consider making changes to extensions\configuration-editing\schemas\devContainer.schema.src.json // and extensions\configuration-editing\schemas\attachContainer.schema.json // to keep in sync with devcontainer.json schema. diff --git a/src/vs/workbench/contrib/search/browser/media/searchview.css b/src/vs/workbench/contrib/search/browser/media/searchview.css index 896a356acac..057347da4d8 100644 --- a/src/vs/workbench/contrib/search/browser/media/searchview.css +++ b/src/vs/workbench/contrib/search/browser/media/searchview.css @@ -3,6 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +.search-view { + display: flex; + flex-direction: column; + height: 100%; +} + +.search-view .results { + flex-grow: 1; + min-height: 0; /* Allow it to be smaller than its contents */ +} + .search-view .search-widgets-container { margin: 0px 12px 0 2px; padding-top: 6px; diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 40f36f73b52..027b9fcd189 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -1038,17 +1038,7 @@ export class SearchView extends ViewPane { this.inputPatternExcludes.setWidth(this.size.width - 28 /* container margin */); this.inputPatternIncludes.setWidth(this.size.width - 28 /* container margin */); - const messagesSize = this.messagesElement.style.display === 'none' ? - 0 : - dom.getTotalHeight(this.messagesElement); - - const searchResultContainerHeight = this.size.height - - messagesSize - - dom.getTotalHeight(this.searchWidgetsContainerElement); - - this.resultsElement.style.height = searchResultContainerHeight + 'px'; - - this.tree.layout(searchResultContainerHeight, this.size.width); + this.tree.layout(); // The tree will measure its container } protected layoutBody(height: number, width: number): void { @@ -1112,7 +1102,7 @@ export class SearchView extends ViewPane { this.tree.domFocus(); const selection = this.tree.getSelection(); if (selection.length === 0) { - this.tree.focusNext(); + this.tree.focusNext(undefined, undefined, getSelectionKeyboardEvent()); } } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 7674a991804..fa6cbfcab7d 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -35,6 +35,7 @@ import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/c import { selectBorder } from 'vs/platform/theme/common/colorRegistry'; import { ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox'; import { equals } from 'vs/base/common/arrays'; +import { BrowserFeatures } from 'vs/base/browser/canIUse'; const FIND_FOCUS_CLASS = 'find-focused'; @@ -244,7 +245,11 @@ export class TerminalViewPane extends ViewPane { await terminal.copySelection(); terminal.clearSelection(); } else { - terminal.paste(); + if (BrowserFeatures.clipboard.readText) { + terminal.paste(); + } else { + this._notificationService.info(`This browser doesn't support the clipboard.readText API needed to trigger a paste, try ${platform.isMacintosh ? '⌘' : 'Ctrl'}+V instead.`); + } } // Clear selection after all click event bubbling is finished on Mac to prevent // right-click selecting a word which is seemed cannot be disabled. There is a diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 1c9f51ea37c..e4ea96f5b74 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -15,40 +15,40 @@ import { IExtensionPointDescriptor } from 'vs/workbench/services/extensions/comm export const TERMINAL_VIEW_ID = 'terminal'; /** A context key that is set when there is at least one opened integrated terminal. */ -export const KEYBINDING_CONTEXT_TERMINAL_IS_OPEN = new RawContextKey('terminalIsOpen', false); +export const KEYBINDING_CONTEXT_TERMINAL_IS_OPEN = new RawContextKey('terminalIsOpen', false, true); /** A context key that is set when the integrated terminal has focus. */ -export const KEYBINDING_CONTEXT_TERMINAL_FOCUS = new RawContextKey('terminalFocus', false); +export const KEYBINDING_CONTEXT_TERMINAL_FOCUS = new RawContextKey('terminalFocus', false, 'Whether the terminal is focused'); export const KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY = 'terminalShellType'; /** A context key that is set to the detected shell for the most recently active terminal, this is set to the last known value when no terminals exist. */ -export const KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE = new RawContextKey(KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY, undefined); +export const KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE = new RawContextKey(KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY, undefined, { type: 'string', description: 'The shell type of the active terminal' }); -export const KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE = new RawContextKey('terminalAltBufferActive', false); +export const KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE = new RawContextKey('terminalAltBufferActive', false, true); /** A context key that is set when the integrated terminal does not have focus. */ export const KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED = KEYBINDING_CONTEXT_TERMINAL_FOCUS.toNegated(); /** A context key that is set when the user is navigating the accessibility tree */ -export const KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS = new RawContextKey('terminalA11yTreeFocus', false); +export const KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS = new RawContextKey('terminalA11yTreeFocus', false, true); /** A keybinding context key that is set when the integrated terminal has text selected. */ -export const KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED = new RawContextKey('terminalTextSelected', false); +export const KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED = new RawContextKey('terminalTextSelected', false, 'Whether text is selected in the active terminal'); /** A keybinding context key that is set when the integrated terminal does not have text selected. */ export const KEYBINDING_CONTEXT_TERMINAL_TEXT_NOT_SELECTED = KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED.toNegated(); /** A context key that is set when the find widget in integrated terminal is visible. */ -export const KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE = new RawContextKey('terminalFindVisible', false); +export const KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE = new RawContextKey('terminalFindVisible', false, true); /** A context key that is set when the find widget in integrated terminal is not visible. */ export const KEYBINDING_CONTEXT_TERMINAL_FIND_NOT_VISIBLE = KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE.toNegated(); /** A context key that is set when the find widget find input in integrated terminal is focused. */ -export const KEYBINDING_CONTEXT_TERMINAL_FIND_INPUT_FOCUSED = new RawContextKey('terminalFindInputFocused', false); +export const KEYBINDING_CONTEXT_TERMINAL_FIND_INPUT_FOCUSED = new RawContextKey('terminalFindInputFocused', false, true); /** A context key that is set when the find widget in integrated terminal is focused. */ -export const KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED = new RawContextKey('terminalFindFocused', false); +export const KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED = new RawContextKey('terminalFindFocused', false, true); /** A context key that is set when the find widget find input in integrated terminal is not focused. */ export const KEYBINDING_CONTEXT_TERMINAL_FIND_INPUT_NOT_FOCUSED = KEYBINDING_CONTEXT_TERMINAL_FIND_INPUT_FOCUSED.toNegated(); -export const KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED = new RawContextKey('terminalProcessSupported', false); +export const KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED = new RawContextKey('terminalProcessSupported', false, 'Whether terminal processes can be launched'); export const IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY = 'terminal.integrated.isWorkspaceShellAllowed'; export const NEVER_MEASURE_RENDER_TIME_STORAGE_KEY = 'terminal.integrated.neverMeasureRenderTime'; diff --git a/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts b/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts index d785bf0de24..6cab52d3a1b 100644 --- a/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts +++ b/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts @@ -7,6 +7,7 @@ import { ObjectTree } from 'vs/base/browser/ui/tree/objectTree'; import { Emitter } from 'vs/base/common/event'; import { FuzzyScore } from 'vs/base/common/filters'; import { Disposable } from 'vs/base/common/lifecycle'; +import { IndexedSet } from 'vs/base/common/map'; import { URI } from 'vs/base/common/uri'; import { Position } from 'vs/editor/common/core/position'; import { IWorkspaceFolder, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace'; @@ -39,11 +40,13 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes private readonly updateEmitter = new Emitter(); private readonly changes = new NodeChangeList(); private readonly locations = new TestLocationStore(); + private readonly itemSource = new IndexedSet(); + private readonly itemsByExtId = this.itemSource.index(v => v.test.item.extId); /** * Map of item IDs to test item objects. */ - protected readonly items = new Map(); + protected readonly items = this.itemSource.index(v => v.test.id); /** * Root folders @@ -81,16 +84,14 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes // when test states change, reflect in the tree // todo: optimize this to avoid needing to iterate - this._register(results.onTestChanged(([, { item, state, retired, computedState }]) => { - for (const i of this.items.values()) { - if (i.test.item.extId === item.extId) { - i.ownState = state.state; - i.retired = retired; - refreshComputedState(computedStateAccessor, i, this.addUpdated, computedState); - this.addUpdated(i); - this.updateEmitter.fire(); - return; - } + this._register(results.onTestChanged(({ item: result }) => { + const item = this.itemsByExtId.get(result.item.extId); + if (item) { + item.ownState = result.state.state; + item.retired = result.retired; + refreshComputedState(computedStateAccessor, item, this.addUpdated, result.computedState); + this.addUpdated(item); + this.updateEmitter.fire(); } })); @@ -219,14 +220,14 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes protected unstoreItem(item: HierarchicalElement) { item.parentItem.children.delete(item); - this.items.delete(item.test.id); + this.itemSource.delete(item); this.locations.remove(item); return item.children; } protected storeItem(item: HierarchicalElement) { item.parentItem.children.add(item); - this.items.set(item.test.id, item); + this.itemSource.add(item); this.locations.add(item); const prevState = this.results.getStateByExtId(item.test.item.extId)?.[1]; diff --git a/src/vs/workbench/contrib/testing/browser/explorerProjections/locationStore.ts b/src/vs/workbench/contrib/testing/browser/explorerProjections/locationStore.ts index 9e23c907a81..f604a164eec 100644 --- a/src/vs/workbench/contrib/testing/browser/explorerProjections/locationStore.ts +++ b/src/vs/workbench/contrib/testing/browser/explorerProjections/locationStore.ts @@ -6,25 +6,22 @@ import { findFirstInSorted } from 'vs/base/common/arrays'; import { URI } from 'vs/base/common/uri'; import { Position } from 'vs/editor/common/core/position'; -import { Location as ModeLocation } from 'vs/editor/common/modes'; +import { Range } from 'vs/editor/common/core/range'; +import { IRichLocation } from 'vs/workbench/contrib/testing/common/testCollection'; -export const locationsEqual = (a: ModeLocation | undefined, b: ModeLocation | undefined) => { +export const locationsEqual = (a: IRichLocation | undefined, b: IRichLocation | undefined) => { if (a === undefined || b === undefined) { return b === a; } - return a.uri.toString() === b.uri.toString() - && a.range.startLineNumber === b.range.startLineNumber - && a.range.startColumn === b.range.startColumn - && a.range.endLineNumber === b.range.endLineNumber - && a.range.endColumn === b.range.endColumn; + return a.uri.toString() === b.uri.toString() && a.range.equalsRange(b.range); }; /** * Stores and looks up test-item-like-objects by their uri/range. Used to * implement the 'reveal' action efficiently. */ -export class TestLocationStore { +export class TestLocationStore { private readonly itemsByUri = new Map(); public hasTestInDocument(uri: URI) { @@ -39,12 +36,7 @@ export class TestLocationStore { const range = test.location?.range; - return range - && new Position(range.startLineNumber, range.startColumn).isBeforeOrEqual(position) - && position.isBeforeOrEqual(new Position( - range.endLineNumber ?? range.startLineNumber, - range.endColumn ?? range.startColumn, - )); + return range && Range.lift(range).containsPosition(position); }); } diff --git a/src/vs/workbench/contrib/testing/browser/media/testing.css b/src/vs/workbench/contrib/testing/browser/media/testing.css index 19896bdbd8d..5e3adce155b 100644 --- a/src/vs/workbench/contrib/testing/browser/media/testing.css +++ b/src/vs/workbench/contrib/testing/browser/media/testing.css @@ -60,8 +60,20 @@ margin-right: 8px; } -.monaco-workbench .part > .title > .title-actions .action-label.codicon-testing-autorun::before { - padding: 2px; +.monaco-workbench .part > .title > .title-actions .action-label.codicon-testing-autorun::after { + content: ''; + display: none; + position: absolute; + width: 0.4em; + height: 0.4em; + top: 50%; + left: 50%; + margin: 0.1em 0 0 0.05em; + border-radius: 100%; +} + +.monaco-workbench .part > .title > .title-actions .action-label.codicon-testing-autorun.checked::after { + display: block; } .codicon-testing-loading-icon::before { diff --git a/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts b/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts index 60ffcfc38c0..62310f4dea3 100644 --- a/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts +++ b/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts @@ -25,9 +25,11 @@ import { TestExplorerViewMode, TestExplorerViewSorting, Testing } from 'vs/workb import { InternalTestItem, TestIdWithProvider } from 'vs/workbench/contrib/testing/common/testCollection'; import { ITestingAutoRun } from 'vs/workbench/contrib/testing/common/testingAutoRun'; import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys'; +import { isFailedState } from 'vs/workbench/contrib/testing/common/testingStates'; import { ITestResult, ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; -import { ITestService, waitForAllRoots } from 'vs/workbench/contrib/testing/common/testService'; +import { ITestService, waitForAllRoots, waitForAllTests } from 'vs/workbench/contrib/testing/common/testService'; import { IWorkspaceTestCollectionService } from 'vs/workbench/contrib/testing/common/workspaceTestCollectionService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; const category = localize('testing.category', 'Test'); @@ -94,7 +96,7 @@ export class RunAction extends Action { } } -abstract class RunOrDebugAction extends ViewAction { +abstract class RunOrDebugSelectedAction extends ViewAction { constructor(id: string, title: string, icon: ThemeIcon, private readonly debug: boolean) { super({ id, @@ -103,6 +105,7 @@ abstract class RunOrDebugAction extends ViewAction { viewId: Testing.ExplorerViewId, f1: true, category, + precondition: FocusedViewContext.isEqualTo(Testing.ExplorerViewId), }); } @@ -143,7 +146,7 @@ abstract class RunOrDebugAction extends ViewAction { protected abstract filter(item: InternalTestItem): boolean; } -export class RunSelectedAction extends RunOrDebugAction { +export class RunSelectedAction extends RunOrDebugSelectedAction { constructor( ) { super( @@ -162,7 +165,7 @@ export class RunSelectedAction extends RunOrDebugAction { } } -export class DebugSelectedAction extends RunOrDebugAction { +export class DebugSelectedAction extends RunOrDebugSelectedAction { constructor() { super( 'testing.debugSelected', @@ -194,7 +197,10 @@ abstract class RunOrDebugAllAllAction extends Action2 { group: 'navigation', when: ContextKeyAndExpr.create([ ContextKeyEqualsExpr.create('view', Testing.ExplorerViewId), - ContextKeyEqualsExpr.create(TestingContextKeys.isRunning.serialize(), false), + TestingContextKeys.isRunning.isEqualTo(false), + debug + ? TestingContextKeys.hasDebuggableTests.isEqualTo(true) + : TestingContextKeys.hasRunnableTests.isEqualTo(true), ]) } }); @@ -502,3 +508,323 @@ export class ToggleAutoRun extends Action2 { accessor.get(ITestingAutoRun).toggle(); } } + +abstract class RunOrDebugAtCursor extends Action2 { + /** + * @override + */ + public async run(accessor: ServicesAccessor) { + const control = accessor.get(IEditorService).activeTextEditorControl; + const position = control?.getPosition(); + const model = control?.getModel(); + if (!position || !model || !('uri' in model)) { + return; + } + + const testService = accessor.get(ITestService); + const collection = testService.subscribeToDiffs(ExtHostTestingResource.TextDocument, model.uri); + + let bestDepth = -1; + let bestNode: InternalTestItem | undefined; + + try { + await waitForAllTests(collection.object); + const queue: [depth: number, nodes: Iterable][] = [[0, collection.object.rootIds]]; + while (queue.length > 0) { + const [depth, candidates] = queue.pop()!; + for (const id of candidates) { + const candidate = collection.object.getNodeById(id); + if (candidate) { + if (depth > bestDepth && this.filter(candidate) && candidate.item.location?.range.containsPosition(position)) { + bestDepth = depth; + bestNode = candidate; + } + + queue.push([depth + 1, candidate.children]); + } + } + } + + if (bestNode) { + await this.runTest(testService, bestNode); + } + } finally { + collection.dispose(); + } + } + + protected abstract filter(node: InternalTestItem): boolean; + + protected abstract runTest(service: ITestService, node: InternalTestItem): Promise; +} + +export class RunAtCursor extends RunOrDebugAtCursor { + constructor() { + super({ + id: 'testing.runAtCursor', + title: localize('testing.runAtCursor', "Run Test at Cursor"), + f1: true, + category, + }); + } + + protected filter(node: InternalTestItem): boolean { + return node.item.runnable; + } + + protected runTest(service: ITestService, node: InternalTestItem): Promise { + return service.runTests({ debug: false, tests: [{ testId: node.id, providerId: node.providerId }] }); + } +} + +export class DebugAtCursor extends RunOrDebugAtCursor { + constructor() { + super({ + id: 'testing.debugAtCursor', + title: localize('testing.debugAtCursor', "Debug Test at Cursor"), + f1: true, + category, + }); + } + + protected filter(node: InternalTestItem): boolean { + return node.item.debuggable; + } + + protected runTest(service: ITestService, node: InternalTestItem): Promise { + return service.runTests({ debug: true, tests: [{ testId: node.id, providerId: node.providerId }] }); + } +} + +abstract class RunOrDebugCurrentFile extends Action2 { + /** + * @override + */ + public async run(accessor: ServicesAccessor) { + const control = accessor.get(IEditorService).activeTextEditorControl; + const position = control?.getPosition(); + const model = control?.getModel(); + if (!position || !model || !('uri' in model)) { + return; + } + + const testService = accessor.get(ITestService); + const collection = testService.subscribeToDiffs(ExtHostTestingResource.TextDocument, model.uri); + + try { + await waitForAllTests(collection.object); + + const roots = [...collection.object.rootIds] + .map(r => collection.object.getNodeById(r)) + .filter(isDefined) + .filter(n => this.filter(n)); + + if (roots.length) { + await this.runTest(testService, roots); + } + } finally { + collection.dispose(); + } + } + + protected abstract filter(node: InternalTestItem): boolean; + + protected abstract runTest(service: ITestService, node: InternalTestItem[]): Promise; +} + +export class RunCurrentFile extends RunOrDebugCurrentFile { + constructor() { + super({ + id: 'testing.runCurrentFile', + title: localize('testing.runCurrentFile', "Run Tests in Current File"), + f1: true, + category, + }); + } + + protected filter(node: InternalTestItem): boolean { + return node.item.runnable; + } + + protected runTest(service: ITestService, nodes: InternalTestItem[]): Promise { + return service.runTests({ debug: false, tests: nodes.map(node => ({ testId: node.id, providerId: node.providerId })) }); + } +} + +export class DebugCurrentFile extends RunOrDebugCurrentFile { + constructor() { + super({ + id: 'testing.debugCurrentFile', + title: localize('testing.debugCurrentFile', "Debug Tests in Current File"), + f1: true, + category, + }); + } + + protected filter(node: InternalTestItem): boolean { + return node.item.debuggable; + } + + protected runTest(service: ITestService, nodes: InternalTestItem[]): Promise { + return service.runTests({ debug: true, tests: nodes.map(node => ({ testId: node.id, providerId: node.providerId })) }); + } +} + +abstract class RunOrDebugTestResults extends Action2 { + /** + * @override + */ + public async run(accessor: ServicesAccessor) { + const testService = accessor.get(ITestService); + const extIds = this.getTestExtIdsToRun(accessor); + if (extIds.size === 0) { + return; + } + + const workspaceTests = accessor.get(IWorkspaceTestCollectionService).subscribeToWorkspaceTests(); + + try { + await Promise.all(workspaceTests.workspaceFolderCollections.map(([, c]) => waitForAllTests(c))); + + const toRun: InternalTestItem[] = []; + for (const [, collection] of workspaceTests.workspaceFolderCollections) { + for (const node of collection.all) { + if (extIds.has(node.item.extId) && this.filter(node)) { + toRun.push(node); + extIds.delete(node.item.extId); + } + } + } + + if (toRun.length) { + await this.runTest(testService, toRun); + } + } finally { + workspaceTests.dispose(); + } + } + + protected abstract getTestExtIdsToRun(accessor: ServicesAccessor): Set; + + protected abstract filter(node: InternalTestItem): boolean; + + protected abstract runTest(service: ITestService, node: InternalTestItem[]): Promise; +} + +abstract class RunOrDebugFailedTests extends RunOrDebugTestResults { + /** + * @inheritdoc + */ + protected getTestExtIdsToRun(accessor: ServicesAccessor): Set { + const { results } = accessor.get(ITestResultService); + const extIds = new Set(); + for (let i = results.length - 1; i >= 0; i--) { + for (const test of results[i].tests) { + if (isFailedState(test.state.state)) { + extIds.add(test.item.extId); + } else { + extIds.delete(test.item.extId); + } + } + } + + return extIds; + } +} + +abstract class RunOrDebugLastRun extends RunOrDebugTestResults { + /** + * @inheritdoc + */ + protected getTestExtIdsToRun(accessor: ServicesAccessor): Set { + const lastResult = accessor.get(ITestResultService).results[0]; + const extIds = new Set(); + if (!lastResult) { + return extIds; + } + + for (const test of lastResult.tests) { + if (test.direct) { + extIds.add(test.item.extId); + } + } + + return extIds; + } +} + +export class ReRunFailedTests extends RunOrDebugFailedTests { + constructor() { + super({ + id: 'testing.reRunFailTests', + title: localize('testing.reRunFailTests', "Re-run Failed Tests"), + f1: true, + category, + }); + } + + protected filter(node: InternalTestItem): boolean { + return node.item.runnable; + } + + protected runTest(service: ITestService, nodes: InternalTestItem[]): Promise { + return service.runTests({ debug: false, tests: nodes.map(node => ({ testId: node.id, providerId: node.providerId })) }); + } +} + +export class DebugFailedTests extends RunOrDebugFailedTests { + constructor() { + super({ + id: 'testing.debugFailTests', + title: localize('testing.debugFailTests', "Debug Failed Tests"), + f1: true, + category, + }); + } + + protected filter(node: InternalTestItem): boolean { + return node.item.debuggable; + } + + protected runTest(service: ITestService, nodes: InternalTestItem[]): Promise { + return service.runTests({ debug: true, tests: nodes.map(node => ({ testId: node.id, providerId: node.providerId })) }); + } +} + +export class ReRunLastRun extends RunOrDebugLastRun { + constructor() { + super({ + id: 'testing.reRunLastRun', + title: localize('testing.reRunLastRun', "Re-run Last Run"), + f1: true, + category, + }); + } + + protected filter(node: InternalTestItem): boolean { + return node.item.runnable; + } + + protected runTest(service: ITestService, nodes: InternalTestItem[]): Promise { + return service.runTests({ debug: false, tests: nodes.map(node => ({ testId: node.id, providerId: node.providerId })) }); + } +} + +export class DebugLastRun extends RunOrDebugLastRun { + constructor() { + super({ + id: 'testing.debugLastRun', + title: localize('testing.debugLastRun', "Debug Last Run"), + f1: true, + category, + }); + } + + protected filter(node: InternalTestItem): boolean { + return node.item.debuggable; + } + + protected runTest(service: ITestService, nodes: InternalTestItem[]): Promise { + return service.runTests({ debug: true, tests: nodes.map(node => ({ testId: node.id, providerId: node.providerId })) }); + } +} diff --git a/src/vs/workbench/contrib/testing/browser/testing.contribution.ts b/src/vs/workbench/contrib/testing/browser/testing.contribution.ts index 3cbf194564e..411a66f1fa6 100644 --- a/src/vs/workbench/contrib/testing/browser/testing.contribution.ts +++ b/src/vs/workbench/contrib/testing/browser/testing.contribution.ts @@ -19,7 +19,7 @@ import { testingViewIcon } from 'vs/workbench/contrib/testing/browser/icons'; import { TestingDecorations } from 'vs/workbench/contrib/testing/browser/testingDecorations'; import { ITestExplorerFilterState, TestExplorerFilterState } from 'vs/workbench/contrib/testing/browser/testingExplorerFilter'; import { TestingExplorerView } from 'vs/workbench/contrib/testing/browser/testingExplorerView'; -import { CloseTestPeek, TestingOutputPeekController } from 'vs/workbench/contrib/testing/browser/testingOutputPeek'; +import { CloseTestPeek, ITestingPeekOpener, TestingOutputPeekController, TestingPeekOpener } from 'vs/workbench/contrib/testing/browser/testingOutputPeek'; import { TestingViewPaneContainer } from 'vs/workbench/contrib/testing/browser/testingViewPaneContainer'; import { testingConfiguation } from 'vs/workbench/contrib/testing/common/configuration'; import { Testing } from 'vs/workbench/contrib/testing/common/constants'; @@ -38,6 +38,7 @@ registerSingleton(ITestService, TestService); registerSingleton(ITestResultService, TestResultService); registerSingleton(ITestExplorerFilterState, TestExplorerFilterState); registerSingleton(ITestingAutoRun, TestingAutoRun, true); +registerSingleton(ITestingPeekOpener, TestingPeekOpener); registerSingleton(IWorkspaceTestCollectionService, WorkspaceTestCollectionService); const viewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ @@ -96,6 +97,14 @@ registerAction2(Action.DebugAllAction); registerAction2(Action.EditFocusedTest); registerAction2(Action.ClearTestResultsAction); registerAction2(Action.ToggleAutoRun); +registerAction2(Action.DebugAtCursor); +registerAction2(Action.RunAtCursor); +registerAction2(Action.DebugCurrentFile); +registerAction2(Action.RunCurrentFile); +registerAction2(Action.ReRunFailedTests); +registerAction2(Action.DebugFailedTests); +registerAction2(Action.ReRunLastRun); +registerAction2(Action.DebugLastRun); registerAction2(CloseTestPeek); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TestingContentProvider, LifecyclePhase.Eventually); @@ -127,4 +136,14 @@ CommandsRegistry.registerCommand({ } }); +CommandsRegistry.registerCommand({ + id: 'vscode.peekTestError', + handler: async (accessor: ServicesAccessor, extId: string) => { + const lookup = accessor.get(ITestResultService).getStateByExtId(extId); + if (lookup) { + accessor.get(ITestingPeekOpener).tryPeekFirstError(lookup[0], lookup[1]); + } + } +}); + Registry.as(ConfigurationExtensions.Configuration).registerConfiguration(testingConfiguation); diff --git a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts index 01d809934ca..415b2ae90ad 100644 --- a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts +++ b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts @@ -14,7 +14,6 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { IRange } from 'vs/editor/common/core/range'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; -import { Location as ModeLocation } from 'vs/editor/common/modes'; import { overviewRulerError, overviewRulerInfo, overviewRulerWarning } from 'vs/editor/common/view/editorColorRegistry'; import { localize } from 'vs/nls'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -27,13 +26,14 @@ import { BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution } from import { testingRunAllIcon, testingRunIcon, testingStatesToIcons } from 'vs/workbench/contrib/testing/browser/icons'; import { TestingOutputPeekController } from 'vs/workbench/contrib/testing/browser/testingOutputPeek'; import { testMessageSeverityColors } from 'vs/workbench/contrib/testing/browser/theme'; -import { IncrementalTestCollectionItem, ITestMessage } from 'vs/workbench/contrib/testing/common/testCollection'; +import { IncrementalTestCollectionItem, IRichLocation, ITestMessage } from 'vs/workbench/contrib/testing/common/testCollection'; import { buildTestUri, TestUriType } from 'vs/workbench/contrib/testing/common/testingUri'; -import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; +import { ITestResultService, TestResultItem } from 'vs/workbench/contrib/testing/common/testResultService'; import { IMainThreadTestCollection, ITestService } from 'vs/workbench/contrib/testing/common/testService'; export class TestingDecorations extends Disposable implements IEditorContribution { private collection = this._register(new MutableDisposable>()); + private currentUri?: URI; private lastDecorations: ITestDecoration[] = []; constructor( @@ -53,9 +53,22 @@ export class TestingDecorations extends Disposable implements IEditorContributio } } })); + + this._register(this.results.onTestChanged(({ item: result }) => { + if (this.currentUri && result.item.location?.uri.toString() === this.currentUri.toString()) { + this.setDecorations(this.currentUri); + } + })); + this._register(this.results.onResultsChanged(() => { + if (this.currentUri) { + this.setDecorations(this.currentUri); + } + })); } private attachModel(uri?: URI) { + this.currentUri = uri; + if (!uri) { this.collection.value = undefined; this.clearDecorations(); @@ -63,15 +76,6 @@ export class TestingDecorations extends Disposable implements IEditorContributio } this.collection.value = this.testService.subscribeToDiffs(ExtHostTestingResource.TextDocument, uri, () => this.setDecorations(uri)); - this._register(this.results.onTestChanged(([, changed]) => { - if (changed.item.location?.uri.toString() === uri.toString()) { - this.setDecorations(uri); - } - })); - this._register(this.results.onResultsChanged(() => { - this.setDecorations(uri); - })); - this.setDecorations(uri); } @@ -87,7 +91,7 @@ export class TestingDecorations extends Disposable implements IEditorContributio const stateLookup = this.results.getStateByExtId(test.item.extId); if (hasValidLocation(uri, test.item)) { newDecorations.push(this.instantiationService.createInstance( - RunTestDecoration, test, ref.object, test.item.location, this.editor, stateLookup?.[1].computedState)); + RunTestDecoration, test, ref.object, test.item.location, this.editor, stateLookup?.[1])); } if (!stateLookup) { @@ -102,7 +106,7 @@ export class TestingDecorations extends Disposable implements IEditorContributio type: TestUriType.ResultActualOutput, messageIndex: i, resultId: result.id, - testId: stateItem.item.extId, + testExtId: stateItem.item.extId, }); newDecorations.push(this.instantiationService.createInstance(TestMessageDecoration, m, uri, m.location, this.editor)); @@ -144,7 +148,7 @@ interface ITestDecoration extends IDisposable { click(e: IEditorMouseEvent): boolean; } -const hasValidLocation = (editorUri: URI, t: T): t is T & { location: ModeLocation } => +const hasValidLocation = (editorUri: URI, t: T): t is T & { location: IRichLocation } => t.location?.uri.toString() === editorUri.toString(); const firstLineRange = (originalRange: IRange) => ({ @@ -170,9 +174,9 @@ class RunTestDecoration extends Disposable implements ITestDecoration { constructor( private readonly test: IncrementalTestCollectionItem, private readonly collection: IMainThreadTestCollection, - private readonly location: ModeLocation, + private readonly location: IRichLocation, private readonly editor: ICodeEditor, - computedState: TestRunState | undefined, + stateItem: TestResultItem | undefined, @ITestService private readonly testService: ITestService, @IContextMenuService private readonly contextMenuService: IContextMenuService, @ICommandService private readonly commandService: ICommandService, @@ -180,14 +184,21 @@ class RunTestDecoration extends Disposable implements ITestDecoration { super(); this.line = location.range.startLineNumber; - const icon = computedState !== undefined && computedState !== TestRunState.Unset - ? testingStatesToIcons.get(computedState)! + const icon = stateItem?.computedState !== undefined && stateItem.computedState !== TestRunState.Unset + ? testingStatesToIcons.get(stateItem.computedState)! : test.children.size > 0 ? testingRunAllIcon : testingRunIcon; + const hoverMessage = new MarkdownString('', true).appendText(localize('failedHoverMessage', '{0} has failed. ', test.item.label)); + if (stateItem?.state.messages.length) { + const args = encodeURIComponent(JSON.stringify([test.item.extId])); + hoverMessage.appendMarkdown(`[${localize('failedPeekAction', 'Peek Error')}](command:vscode.peekTestError?${args})`); + } + this.editorDecoration = { range: firstLineRange(this.location.range), options: { isWholeLine: true, + hoverMessage, glyphMarginClassName: ThemeIcon.asClassName(icon) + ' testing-run-glyph', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, glyphMarginHoverMessage: new MarkdownString().appendText(localize('testing.clickToRun', 'Click to run tests, right click for more options')), @@ -275,7 +286,7 @@ class TestMessageDecoration implements ITestDecoration { constructor( { message, severity }: ITestMessage, private readonly messageUri: URI, - location: ModeLocation, + location: IRichLocation, private readonly editor: ICodeEditor, @ICodeEditorService private readonly editorService: ICodeEditorService, @IThemeService themeService: IThemeService, diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts index eae8ec3d55d..3c102a91fd5 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts @@ -9,7 +9,7 @@ import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; import { HistoryInputBox } from 'vs/base/browser/ui/inputbox/inputBox'; -import { Action, IAction, IActionRunner } from 'vs/base/common/actions'; +import { Action, IAction, IActionRunner, Separator } from 'vs/base/common/actions'; import { Delayer } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -24,7 +24,7 @@ import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ViewContainerLocation } from 'vs/workbench/common/views'; import { testingFilterIcon } from 'vs/workbench/contrib/testing/browser/icons'; -import { Testing } from 'vs/workbench/contrib/testing/common/constants'; +import { TestExplorerStateFilter, Testing } from 'vs/workbench/contrib/testing/common/constants'; import { ObservableValue } from 'vs/workbench/contrib/testing/common/observableValue'; import { StoredValue } from 'vs/workbench/contrib/testing/common/storedValue'; import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys'; @@ -34,6 +34,7 @@ export interface ITestExplorerFilterState { readonly text: ObservableValue; /** Reveal request, the extId of the test to reveal */ readonly reveal: ObservableValue; + readonly stateFilter: ObservableValue; readonly currentDocumentOnly: ObservableValue; readonly onDidRequestInputFocus: Event; @@ -46,6 +47,11 @@ export class TestExplorerFilterState implements ITestExplorerFilterState { declare _serviceBrand: undefined; private readonly focusEmitter = new Emitter(); public readonly text = new ObservableValue(''); + public readonly stateFilter = ObservableValue.stored(new StoredValue({ + key: 'testStateFilter', + scope: StorageScope.WORKSPACE, + target: StorageTarget.USER + }, this.storage), TestExplorerStateFilter.All); public readonly currentDocumentOnly = ObservableValue.stored(new StoredValue({ key: 'testsByCurrentDocumentOnly', scope: StorageScope.WORKSPACE, @@ -83,6 +89,7 @@ export class TestingExplorerFilter extends BaseActionViewItem { super(null, action); this.updateFilterActiveState(); this._register(state.currentDocumentOnly.onDidChange(this.updateFilterActiveState, this)); + this._register(state.stateFilter.onDidChange(this.updateFilterActiveState, this)); } /** @@ -166,7 +173,8 @@ export class TestingExplorerFilter extends BaseActionViewItem { * Updates the 'checked' state of the filter submenu. */ private updateFilterActiveState() { - this.filtersAction.checked = this.state.currentDocumentOnly.value; + this.filtersAction.checked = this.state.currentDocumentOnly.value + || this.state.stateFilter.value !== TestExplorerStateFilter.All; } } @@ -198,6 +206,21 @@ class FiltersDropdownMenuActionViewItem extends DropdownMenuActionViewItem { private getActions(): IAction[] { return [ + ...[ + { v: TestExplorerStateFilter.OnlyFailed, label: localize('testing.filters.showOnlyFailed', "Show Only Failed Tests") }, + { v: TestExplorerStateFilter.OnlyExecuted, label: localize('testing.filters.showOnlyExecuted', "Show Only Executed Tests") }, + { v: TestExplorerStateFilter.All, label: localize('testing.filters.showAll', "Show All Tests") }, + ].map(({ v, label }) => ({ + checked: this.filters.stateFilter.value === v, + class: undefined, + enabled: true, + id: v, + label, + run: async () => this.filters.stateFilter.value = v, + tooltip: '', + dispose: () => null + })), + new Separator(), { checked: this.filters.currentDocumentOnly.value, class: undefined, diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts index b1a3c35be43..c65800ac901 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { IIdentityProvider, IKeyboardNavigationLabelProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; @@ -19,7 +20,9 @@ import { Event } from 'vs/base/common/event'; import { FuzzyScore } from 'vs/base/common/filters'; import { splitGlobAware } from 'vs/base/common/glob'; import { Iterable } from 'vs/base/common/iterator'; +import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { isDefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/testing'; import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -50,11 +53,10 @@ import { HierarchicalByLocationProjection } from 'vs/workbench/contrib/testing/b import { HierarchicalByNameProjection } from 'vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByName'; import { testingStatesToIcons } from 'vs/workbench/contrib/testing/browser/icons'; import { ITestExplorerFilterState, TestExplorerFilterState, TestingExplorerFilter } from 'vs/workbench/contrib/testing/browser/testingExplorerFilter'; -import { TestingOutputPeekController } from 'vs/workbench/contrib/testing/browser/testingOutputPeek'; -import { TestExplorerViewMode, TestExplorerViewSorting, Testing, testStateNames } from 'vs/workbench/contrib/testing/common/constants'; +import { ITestingPeekOpener, TestingOutputPeekController } from 'vs/workbench/contrib/testing/browser/testingOutputPeek'; +import { TestExplorerStateFilter, TestExplorerViewMode, TestExplorerViewSorting, Testing, testStateNames } from 'vs/workbench/contrib/testing/common/constants'; import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys'; import { cmpPriority, isFailedState } from 'vs/workbench/contrib/testing/common/testingStates'; -import { buildTestUri, TestUriType } from 'vs/workbench/contrib/testing/common/testingUri'; import { ITestResultService, sumCounts, TestStateCount } from 'vs/workbench/contrib/testing/common/testResultService'; import { ITestService } from 'vs/workbench/contrib/testing/common/testService'; import { IWorkspaceTestCollectionService, TestSubscriptionListener } from 'vs/workbench/contrib/testing/common/workspaceTestCollectionService'; @@ -224,12 +226,14 @@ export class TestingExplorerViewModel extends Disposable { listContainer: HTMLElement, onDidChangeVisibility: Event, private listener: TestSubscriptionListener | undefined, + @ITestService private readonly testService: ITestService, @ITestExplorerFilterState filterState: TestExplorerFilterState, @IInstantiationService private readonly instantiationService: IInstantiationService, @IEditorService private readonly editorService: IEditorService, @IStorageService private readonly storageService: IStorageService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @ITestResultService private readonly testResults: ITestResultService, + @ITestingPeekOpener private readonly peekOpener: ITestingPeekOpener, ) { super(); @@ -257,7 +261,11 @@ export class TestingExplorerViewModel extends Disposable { filter: this.filter, }) as WorkbenchObjectTree; - this._register(Event.any(filterState.currentDocumentOnly.onDidChange, filterState.text.onDidChange)(this.tree.refilter, this.tree)); + this._register(Event.any( + filterState.currentDocumentOnly.onDidChange, + filterState.text.onDidChange, + filterState.stateFilter.onDidChange, + )(this.tree.refilter, this.tree)); this._register(editorService.onDidActiveEditorChange(() => { if (filterState.currentDocumentOnly.value && editorService.activeEditor?.resource) { if (this.projection.hasTestInDocument(editorService.activeEditor.resource)) { @@ -270,12 +278,20 @@ export class TestingExplorerViewModel extends Disposable { this._register(this.tree); this._register(dom.addStandardDisposableListener(this.tree.getHTMLElement(), 'keydown', evt => { - if (DefaultKeyboardNavigationDelegate.mightProducePrintableCharacter(evt)) { + if (evt.equals(KeyCode.Enter)) { + this.handleExecuteKeypress(evt); + } else if (DefaultKeyboardNavigationDelegate.mightProducePrintableCharacter(evt)) { filterState.text.value = evt.browserEvent.key; filterState.focusInput(); } })); + this._register(onDidChangeVisibility(visible => { + if (visible) { + filterState.focusInput(); + } + })); + this.updatePreferredProjection(); this.onDidChangeSelection = this.tree.onDidChangeSelection; @@ -386,35 +402,30 @@ export class TestingExplorerViewModel extends Disposable { */ private async tryPeekError(item: ITestTreeElement) { const lookup = item.test && this.testResults.getStateByExtId(item.test.item.extId); - if (!lookup || !isFailedState(lookup[1].state.state)) { - return false; + return lookup && isFailedState(lookup[1].state.state) + ? this.peekOpener.tryPeekFirstError(lookup[0], lookup[1], { preserveFocus: true }) + : false; + } + + private handleExecuteKeypress(evt: IKeyboardEvent) { + const focused = this.tree.getFocus(); + const selected = this.tree.getSelection(); + let targeted: (ITestTreeElement | null)[]; + if (focused.length === 1 && selected.includes(focused[0])) { + evt.browserEvent?.preventDefault(); + targeted = selected; + } else { + targeted = focused; } - const [result, test] = lookup; - const index = test.state.messages.findIndex(m => !!m.location); - if (index === -1) { - return; + const toRun = targeted + .map(e => e?.test) + .filter(isDefined) + .filter(e => e.item.runnable); + + if (toRun.length) { + this.testService.runTests({ debug: false, tests: toRun.map(t => ({ providerId: t.providerId, testId: t.id })) }); } - - const message = test.state.messages[index]; - const pane = await this.editorService.openEditor({ - resource: message.location!.uri, - options: { selection: message.location!.range, preserveFocus: true } - }); - - const control = pane?.getControl(); - if (!isCodeEditor(control)) { - return false; - } - - TestingOutputPeekController.get(control).show(buildTestUri({ - type: TestUriType.ResultMessage, - messageIndex: index, - resultId: result.id, - testId: item.test!.item.extId, - })); - - return true; } private updatePreferredProjection() { @@ -549,7 +560,7 @@ class TestsFilter implements ITreeFilter { this.setFilter(this.state.text.value); } - switch (Math.min(this.testFilterText(element), this.testLocation(element))) { + switch (Math.min(this.testFilterText(element), this.testLocation(element), this.testState(element))) { case FilterResult.Exclude: return TreeVisibility.Hidden; case FilterResult.Include: @@ -559,6 +570,17 @@ class TestsFilter implements ITreeFilter { } } + private testState(element: ITestTreeElement): FilterResult { + switch (this.state.stateFilter.value) { + case TestExplorerStateFilter.All: + return FilterResult.Include; + case TestExplorerStateFilter.OnlyExecuted: + return element.ownState !== TestRunState.Unset ? FilterResult.Include : FilterResult.Inherit; + case TestExplorerStateFilter.OnlyFailed: + return isFailedState(element.ownState) ? FilterResult.Include : FilterResult.Inherit; + } + } + private testLocation(element: ITestTreeElement): FilterResult { if (!this._filterToUri || !this.state.currentDocumentOnly.value) { return FilterResult.Include; diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index 081c8893ad9..3e3bb38e45a 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; +import { alert } from 'vs/base/browser/ui/aria/aria'; import { Codicon } from 'vs/base/common/codicons'; import { Color } from 'vs/base/common/color'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -11,7 +12,7 @@ import { Disposable, IReference, MutableDisposable } from 'vs/base/common/lifecy import { clamp } from 'vs/base/common/numbers'; import { count } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction2 } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { EmbeddedCodeEditorWidget, EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; @@ -20,17 +21,22 @@ import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; import { getOuterEditor, IPeekViewService, peekViewTitleBackground, peekViewTitleForeground, peekViewTitleInfoForeground, PeekViewWidget } from 'vs/editor/contrib/peekView/peekView'; import { localize } from 'vs/nls'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; +import { createDecorator, IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService'; import { EditorModel } from 'vs/workbench/common/editor'; import { testingPeekBorder } from 'vs/workbench/contrib/testing/browser/theme'; +import { AutoOpenPeekViewWhen, getTestingConfiguration, TestingConfigKeys } from 'vs/workbench/contrib/testing/common/configuration'; import { Testing } from 'vs/workbench/contrib/testing/common/constants'; import { ITestItem, ITestMessage, ITestState } from 'vs/workbench/contrib/testing/common/testCollection'; import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys'; +import { isFailedState } from 'vs/workbench/contrib/testing/common/testingStates'; import { buildTestUri, parseTestUri, TestUriType } from 'vs/workbench/contrib/testing/common/testingUri'; -import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; +import { ITestResult, ITestResultService, TestResultItem, TestResultItemChange, TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResultService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; interface ITestDto { test: ITestItem, @@ -41,6 +47,93 @@ interface ITestDto { messageUri: URI; } +export interface ITestingPeekOpener { + _serviceBrand: undefined; + + /** + * Tries to peek the first test error, if the item is in a failed state. + * @returns a boolean indicating whether a peek was opened + */ + tryPeekFirstError(result: ITestResult, test: TestResultItem, options?: Partial): Promise; +} + +export const ITestingPeekOpener = createDecorator('testingPeekOpener'); + +export class TestingPeekOpener extends Disposable implements ITestingPeekOpener { + declare _serviceBrand: undefined; + + constructor( + @IConfigurationService private readonly configuration: IConfigurationService, + @IEditorService private readonly editorService: IEditorService, + @ICodeEditorService private readonly codeEditorService: ICodeEditorService, + @ITestResultService testResults: ITestResultService, + ) { + super(); + this._register(testResults.onTestChanged(this.openPeekOnFailure, this)); + } + + /** + * Tries to peek the first test error, if the item is in a failed state. + * @returns a boolean if a peek was opened + */ + public async tryPeekFirstError(result: ITestResult, test: TestResultItem, options?: Partial) { + const index = test.state.messages.findIndex(m => !!m.location); + if (index === -1) { + return false; + } + + const message = test.state.messages[index]; + const pane = await this.editorService.openEditor({ + resource: message.location!.uri, + options: { selection: message.location!.range, revealIfOpened: true, ...options } + }); + + const control = pane?.getControl(); + if (!isCodeEditor(control)) { + return false; + } + + TestingOutputPeekController.get(control).show(buildTestUri({ + type: TestUriType.ResultMessage, + messageIndex: index, + resultId: result.id, + testExtId: test.item.extId, + })); + + return true; + } + + /** + * Opens the peek view on a test failure, based on user preferences. + */ + private openPeekOnFailure(evt: TestResultItemChange) { + if (!isFailedState(evt.item.state.state) || !evt.item.state.messages.length) { + return; + } + + if (evt.result.isAutoRun && !getTestingConfiguration(this.configuration, TestingConfigKeys.AutoOpenPeekViewDuringAutoRun)) { + return; + } + + const editors = this.codeEditorService.listCodeEditors(); + const cfg = getTestingConfiguration(this.configuration, TestingConfigKeys.AutoOpenPeekView); + + // don't show the peek if the user asked to only auto-open peeks for visible tests, + // and this test is not in any of the editors' models. + const testUri = evt.item.item.location?.uri.toString(); + if (cfg === AutoOpenPeekViewWhen.FailureVisible && (!testUri || !editors.some(e => e.getModel()?.uri.toString() === testUri))) { + return; + } + + const controllers = editors.map(TestingOutputPeekController.get); + if (controllers.some(c => c?.isVisible)) { + return; + } + + this.tryPeekFirstError(evt.result, evt.item); + } +} + /** * Adds output/message peek functionality to code editors. */ @@ -62,6 +155,13 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo */ private readonly visible: IContextKey; + /** + * Gets whether a peek is currently shown in the associated editor. + */ + public get isVisible() { + return this.peek.value; + } + constructor( private readonly editor: ICodeEditor, @IInstantiationService private readonly instantiationService: IInstantiationService, @@ -71,6 +171,7 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo super(); this.visible = TestingContextKeys.isPeekVisible.bindTo(contextKeyService); this._register(editor.onDidChangeModel(() => this.peek.clear())); + this._register(testResults.onTestChanged((evt) => this.closePeekOnTestChange(evt))); } /** @@ -103,6 +204,7 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo this.peek.value!.create(); } + alert(message.message.toString()); this.peek.value!.setModel(dto); } @@ -113,13 +215,28 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo this.peek.clear(); } - private async retrieveTest(uri: URI): Promise { + /** + * If the test we're currently showing has its state change to something + * else, then clear the peek. + */ + private closePeekOnTestChange(evt: TestResultItemChange) { + if (evt.reason !== TestResultItemChangeReason.OwnStateChange || evt.previous.state === evt.item.state.state) { + return; + } + + const displayed = this.peek.value?.currentTest(); + if (displayed?.extId === evt.item.item.extId) { + this.peek.clear(); + } + } + + private retrieveTest(uri: URI): ITestDto | undefined { const parts = parseTestUri(uri); if (!parts) { return undefined; } - const test = this.testResults.getResult(parts.resultId)?.getStateByExtId(parts.testId); + const test = this.testResults.getResult(parts.resultId)?.getStateByExtId(parts.testExtId); return test && { test: test.item, state: test.state, @@ -168,6 +285,11 @@ abstract class TestingOutputPeek extends PeekViewWidget { */ public abstract setModel(dto: ITestDto): Promise; + /** + * Returns the test whose data is currently shown in the peek view. + */ + public abstract currentTest(): ITestItem | undefined; + /** * @override */ @@ -201,10 +323,13 @@ const diffEditorOptions: IDiffEditorOptions = { renderOverviewRuler: false, ignoreTrimWhitespace: false, renderSideBySide: true, + originalAriaLabel: localize('testingOutputExpected', 'Expected result'), + modifiedAriaLabel: localize('testingOutputActual', 'Actual result'), }; class TestingDiffOutputPeek extends TestingOutputPeek { private readonly diff = this._disposables.add(new MutableDisposable()); + private test: ITestItem | undefined; /** * @override @@ -227,6 +352,7 @@ class TestingDiffOutputPeek extends TestingOutputPeek { return; } + this.test = test; this.show(message.location.range, hintDiffPeekHeight(message)); this.setTitle(message.message.toString(), test.label); @@ -243,6 +369,13 @@ class TestingDiffOutputPeek extends TestingOutputPeek { } } + /** + * @override + */ + public currentTest() { + return this.test; + } + /** * @override */ @@ -254,6 +387,7 @@ class TestingDiffOutputPeek extends TestingOutputPeek { class TestingMessageOutputPeek extends TestingOutputPeek { private readonly preview = this._disposables.add(new MutableDisposable()); + private test: ITestItem | undefined; /** * @override @@ -276,6 +410,7 @@ class TestingMessageOutputPeek extends TestingOutputPeek { return; } + this.test = test; this.show(message.location.range, hintPeekStrHeight(message.message.toString())); this.setTitle(message.message.toString(), test.label); @@ -287,6 +422,13 @@ class TestingMessageOutputPeek extends TestingOutputPeek { } } + /** + * @override + */ + public currentTest() { + return this.test; + } + /** * @override */ diff --git a/src/vs/workbench/contrib/testing/browser/theme.ts b/src/vs/workbench/contrib/testing/browser/theme.ts index 3c80d02eae0..635baca1d34 100644 --- a/src/vs/workbench/contrib/testing/browser/theme.ts +++ b/src/vs/workbench/contrib/testing/browser/theme.ts @@ -8,6 +8,7 @@ import { localize } from 'vs/nls'; import { editorErrorForeground, editorForeground, editorHintForeground, editorInfoForeground, editorWarningForeground, inputActiveOptionBackground, inputActiveOptionBorder, inputActiveOptionForeground, registerColor } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { TestMessageSeverity, TestRunState } from 'vs/workbench/api/common/extHostTypes'; +import { ACTIVITY_BAR_BADGE_BACKGROUND } from 'vs/workbench/common/theme'; export const testingColorIconFailed = registerColor('testing.iconFailed', { dark: '#f14c4c', @@ -135,15 +136,17 @@ registerThemingParticipant((theme, collector) => { //#region active buttons const inputActiveOptionBorderColor = theme.getColor(inputActiveOptionBorder); if (inputActiveOptionBorderColor) { - collector.addRule(`.testing-filter-button.checked, .codicon-testing-autorun.checked::before { border-color: ${inputActiveOptionBorderColor}; }`); + collector.addRule(`.testing-filter-button.checked { border-color: ${inputActiveOptionBorderColor}; }`); } const inputActiveOptionForegroundColor = theme.getColor(inputActiveOptionForeground); if (inputActiveOptionForegroundColor) { - collector.addRule(`.testing-filter-button.checked, .codicon-testing-autorun.checked::before { color: ${inputActiveOptionForegroundColor}; }`); + collector.addRule(`.testing-filter-button.checked { color: ${inputActiveOptionForegroundColor}; }`); } const inputActiveOptionBackgroundColor = theme.getColor(inputActiveOptionBackground); if (inputActiveOptionBackgroundColor) { - collector.addRule(`.testing-filter-button.checked, .codicon-testing-autorun.checked::before { background-color: ${inputActiveOptionBackgroundColor}; }`); + collector.addRule(`.testing-filter-button.checked { background-color: ${inputActiveOptionBackgroundColor}; }`); } + const badgeColor = theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND); + collector.addRule(`.monaco-workbench .part > .title > .title-actions .action-label.codicon-testing-autorun::after { background-color: ${badgeColor}; }`); //#endregion }); diff --git a/src/vs/workbench/contrib/testing/common/configuration.ts b/src/vs/workbench/contrib/testing/common/configuration.ts index 0630b3f40ac..546ec5ff9da 100644 --- a/src/vs/workbench/contrib/testing/common/configuration.ts +++ b/src/vs/workbench/contrib/testing/common/configuration.ts @@ -8,7 +8,14 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; export const enum TestingConfigKeys { - AutoRunDelay = 'testing.autoRun.delay' + AutoRunDelay = 'testing.autoRun.delay', + AutoOpenPeekView = 'testing.automaticallyOpenPeekView', + AutoOpenPeekViewDuringAutoRun = 'testing.automaticallyOpenPeekViewDuringAutoRun', +} + +export const enum AutoOpenPeekViewWhen { + FailureVisible = 'failureInVisibleDocument', + FailureAnywhere = 'failureAnywhere', } export const testingConfiguation: IConfigurationNode = { @@ -23,11 +30,30 @@ export const testingConfiguation: IConfigurationNode = { description: localize('testing.autoRun.delay', "How long to wait, in milliseconds, after a test is marked as outdated and starting a new run."), default: 1000, }, + [TestingConfigKeys.AutoOpenPeekView]: { + description: localize('testing.automaticallyOpenPeekView', "Configures when the error peek view is automatically opened."), + enum: [ + AutoOpenPeekViewWhen.FailureAnywhere, + AutoOpenPeekViewWhen.FailureVisible, + ], + default: AutoOpenPeekViewWhen.FailureVisible, + enumDescriptions: [ + localize('testing.automaticallyOpenPeekView.failureAnywhere', "Open automatically no matter where the failure is."), + localize('testing.automaticallyOpenPeekView.failureInVisibleDocument', "Open automatically when a test fails in a visible document.") + ], + }, + [TestingConfigKeys.AutoOpenPeekViewDuringAutoRun]: { + description: localize('testing.automaticallyOpenPeekViewDuringAutoRun', "Controls whether to automatically open the peek view during auto-run mode."), + type: 'boolean', + default: false, + } } }; export interface ITestingConfiguration { [TestingConfigKeys.AutoRunDelay]: number; + [TestingConfigKeys.AutoOpenPeekView]: AutoOpenPeekViewWhen; + [TestingConfigKeys.AutoOpenPeekViewDuringAutoRun]: boolean; } export const getTestingConfiguration = (config: IConfigurationService, key: K) => config.getValue(key); diff --git a/src/vs/workbench/contrib/testing/common/constants.ts b/src/vs/workbench/contrib/testing/common/constants.ts index 817cef54b7e..0e6ebdae3ba 100644 --- a/src/vs/workbench/contrib/testing/common/constants.ts +++ b/src/vs/workbench/contrib/testing/common/constants.ts @@ -25,6 +25,12 @@ export const enum TestExplorerViewSorting { ByName = 'name', } +export const enum TestExplorerStateFilter { + OnlyFailed = 'failed', + OnlyExecuted = 'excuted', + All = 'all', +} + export const testStateNames: { [K in TestRunState]: string } = { [TestRunState.Errored]: localize('testState.errored', 'Errored'), [TestRunState.Failed]: localize('testState.failed', 'Failed'), diff --git a/src/vs/workbench/contrib/testing/common/testCollection.ts b/src/vs/workbench/contrib/testing/common/testCollection.ts index 8ceb490a2dc..e42f8a207a9 100644 --- a/src/vs/workbench/contrib/testing/common/testCollection.ts +++ b/src/vs/workbench/contrib/testing/common/testCollection.ts @@ -5,7 +5,7 @@ import { IMarkdownString } from 'vs/base/common/htmlContent'; import { URI } from 'vs/base/common/uri'; -import { Location as ModeLocation } from 'vs/editor/common/modes'; +import { Range } from 'vs/editor/common/core/range'; import { ExtHostTestingResource } from 'vs/workbench/api/common/extHost.protocol'; import { TestMessageSeverity, TestRunState } from 'vs/workbench/api/common/extHostTypes'; @@ -15,11 +15,12 @@ export interface TestIdWithProvider { } /** - * Request to them main thread to run a set of tests. + * Request to the main thread to run a set of tests. */ export interface RunTestsRequest { tests: TestIdWithProvider[]; debug: boolean; + isAutoRun?: boolean; } /** @@ -32,12 +33,20 @@ export interface RunTestForProviderRequest { debug: boolean; } +/** + * Location with a fully-instantiated Range and URI. + */ +export interface IRichLocation { + range: Range; + uri: URI; +} + export interface ITestMessage { message: string | IMarkdownString; severity: TestMessageSeverity | undefined; expectedOutput: string | undefined; actualOutput: string | undefined; - location: ModeLocation | undefined; + location: IRichLocation | undefined; } export interface ITestState { @@ -54,7 +63,7 @@ export interface ITestItem { extId: string; label: string; children?: never; - location: ModeLocation | undefined; + location: IRichLocation | undefined; description: string | undefined; runnable: boolean; debuggable: boolean; diff --git a/src/vs/workbench/contrib/testing/common/testResultService.ts b/src/vs/workbench/contrib/testing/common/testResultService.ts index bc2cbda0ca8..6cc3d553a54 100644 --- a/src/vs/workbench/contrib/testing/common/testResultService.ts +++ b/src/vs/workbench/contrib/testing/common/testResultService.ts @@ -6,13 +6,14 @@ import { Emitter, Event } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; +import { Range } from 'vs/editor/common/core/range'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { TestRunState } from 'vs/workbench/api/common/extHostTypes'; import { IComputedStateAccessor, refreshComputedState } from 'vs/workbench/contrib/testing/common/getComputedState'; import { StoredValue } from 'vs/workbench/contrib/testing/common/storedValue'; -import { IncrementalTestCollectionItem, ITestState, TestIdWithProvider } from 'vs/workbench/contrib/testing/common/testCollection'; +import { IncrementalTestCollectionItem, ITestState, RunTestsRequest } from 'vs/workbench/contrib/testing/common/testCollection'; import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys'; import { statesInOrder } from 'vs/workbench/contrib/testing/common/testingStates'; import { IMainThreadTestCollection } from 'vs/workbench/contrib/testing/common/testService'; @@ -22,6 +23,18 @@ import { IMainThreadTestCollection } from 'vs/workbench/contrib/testing/common/t */ export type TestStateCount = { [K in TestRunState]: number }; +export const enum TestResultItemChangeReason { + Retired, + ParentRetired, + ComputedStateChange, + OwnStateChange, +} + +export type TestResultItemChange = { item: TestResultItem; result: ITestResult } & ( + | { reason: TestResultItemChangeReason.Retired | TestResultItemChangeReason.ParentRetired | TestResultItemChangeReason.ComputedStateChange } + | { reason: TestResultItemChangeReason.OwnStateChange; previous: ITestState } +); + export interface ITestResult { /** * Count of the number of tests in each run state. @@ -38,6 +51,16 @@ export interface ITestResult { */ readonly isComplete: boolean; + /** + * Whether this test result is triggered from an auto run. + */ + readonly isAutoRun?: boolean; + + /** + * Gets all tests involved in the run. + */ + tests: IterableIterator; + /** * Gets the state of the test by its extension-assigned ID. */ @@ -50,7 +73,7 @@ export interface ITestResult { toJSON(): ISerializedResults; } -const makeEmptyCounts = () => { +export const makeEmptyCounts = () => { const o: Partial = {}; for (const state of statesInOrder) { o[state] = 0; @@ -129,6 +152,7 @@ const makeNodeAndChildren = ( test: IncrementalTestCollectionItem, byExtId: Map, byInternalId: Map, + isExecutedDirectly = true, ): TestResultItem => { const existing = byInternalId.get(test.id); if (existing) { @@ -136,10 +160,14 @@ const makeNodeAndChildren = ( } const mapped = itemToNode(test, byExtId, byInternalId); + if (isExecutedDirectly) { + mapped.direct = true; + } + for (const childId of test.children) { const child = collection.getNodeById(childId); if (child) { - makeNodeAndChildren(collection, child, byExtId, byInternalId); + makeNodeAndChildren(collection, child, byExtId, byInternalId, false); } } @@ -148,14 +176,14 @@ const makeNodeAndChildren = ( interface ISerializedResults { id: string; - counts: TestStateCount; - items: Iterable<[extId: string, item: TestResultItem]>; + items: (Omit & { children: string[], retired: undefined })[]; } -interface TestResultItem extends IncrementalTestCollectionItem { +export interface TestResultItem extends IncrementalTestCollectionItem { state: ITestState; computedState: TestRunState; retired: boolean; + direct?: true; } /** @@ -169,11 +197,11 @@ export class LiveTestResult implements ITestResult { */ public static from( collections: ReadonlyArray, - tests: ReadonlyArray, + req: RunTestsRequest, ) { const testByExtId = new Map(); const testByInternalId = new Map(); - for (const test of tests) { + for (const test of req.tests) { for (const collection of collections) { const node = collection.getNodeById(test.testId); if (!node) { @@ -185,15 +213,13 @@ export class LiveTestResult implements ITestResult { } } - return new LiveTestResult(collections, testByExtId, testByInternalId); + return new LiveTestResult(collections, testByExtId, testByInternalId, !!req.isAutoRun); } private readonly completeEmitter = new Emitter(); - private readonly retireEmitter = new Emitter(); - private readonly changeEmitter = new Emitter(); + private readonly changeEmitter = new Emitter(); private _complete = false; - public readonly onRetired = this.retireEmitter.event; public readonly onChange = this.changeEmitter.event; public readonly onComplete = this.completeEmitter.event; @@ -215,7 +241,7 @@ export class LiveTestResult implements ITestResult { public readonly counts: { [K in TestRunState]: number } = makeEmptyCounts(); /** - * Gets all tests involved in the run by ID. + * @inheritdoc */ public get tests() { return this.testByInternalId.values(); @@ -256,6 +282,7 @@ export class LiveTestResult implements ITestResult { private readonly collections: ReadonlyArray, private readonly testByExtId: Map, private readonly testByInternalId: Map, + public readonly isAutoRun: boolean, ) { this.counts[TestRunState.Unset] = testByInternalId.size; } @@ -273,10 +300,7 @@ export class LiveTestResult implements ITestResult { public setAllToState(state: ITestState, when: (_t: TestResultItem) => boolean) { for (const test of this.testByInternalId.values()) { if (when(test)) { - this.counts[state.state]--; - test.state = state; - this.counts[state.state]++; - refreshComputedState(this.computedStateAccessor, test, t => this.changeEmitter.fire(t)); + this.fireUpdateAndRefresh(test, state); } } } @@ -293,15 +317,22 @@ export class LiveTestResult implements ITestResult { return; } - if (state.state === entry.state.state) { - entry.state = state; - this.changeEmitter.fire(entry); - } else { - this.counts[entry.state.state]--; - entry.state = state; - this.counts[entry.state.state]++; - refreshComputedState(this.computedStateAccessor, entry, t => this.changeEmitter.fire(t)); + this.fireUpdateAndRefresh(entry, state); + } + + private fireUpdateAndRefresh(entry: TestResultItem, newState: ITestState) { + const previous = entry.state; + entry.state = newState; + + if (newState.state !== previous.state) { + this.counts[previous.state]--; + this.counts[newState.state]++; + refreshComputedState(this.computedStateAccessor, entry, t => ( + t !== entry && this.changeEmitter.fire({ item: t, result: this, reason: TestResultItemChangeReason.ComputedStateChange }) + )); } + + this.changeEmitter.fire({ item: entry, result: this, reason: TestResultItemChangeReason.OwnStateChange, previous }); } /** @@ -313,7 +344,6 @@ export class LiveTestResult implements ITestResult { return; } - this.retireEmitter.fire(root); const queue: Iterable[] = [[root.id]]; while (queue.length) { for (const id of queue.pop()!) { @@ -321,7 +351,13 @@ export class LiveTestResult implements ITestResult { if (entry && !entry.retired) { entry.retired = true; queue.push(entry.children); - this.changeEmitter.fire(entry); + this.changeEmitter.fire({ + result: this, + item: entry, + reason: entry === root + ? TestResultItemChangeReason.Retired + : TestResultItemChangeReason.ParentRetired + }); } } } @@ -336,7 +372,11 @@ export class LiveTestResult implements ITestResult { for (const collection of this.collections) { let test = collection.getNodeById(testId); if (test) { - return makeNodeAndChildren(collection, test, this.testByExtId, this.testByInternalId); + const originalSize = this.testByExtId.size; + makeParents(collection, test, this.testByExtId, this.testByInternalId); + const node = makeNodeAndChildren(collection, test, this.testByExtId, this.testByInternalId); + this.counts[TestRunState.Unset] += this.testByExtId.size - originalSize; + return node; } } @@ -361,7 +401,14 @@ export class LiveTestResult implements ITestResult { * @inheritdoc */ public toJSON(): ISerializedResults { - return { id: this.id, counts: this.counts, items: [...this.testByExtId.entries()] }; + return { + id: this.id, + items: [...this.testByExtId.values()].map(entry => ({ + ...entry, + retired: undefined, + children: [...entry.children], + })), + }; } } @@ -372,30 +419,46 @@ class HydratedTestResult implements ITestResult { /** * @inheritdoc */ - public readonly counts = this.serialized.counts; + public readonly counts = makeEmptyCounts(); /** * @inheritdoc */ - public readonly id = this.serialized.id; + public readonly id: string; /** * @inheritdoc */ public readonly isComplete = true; - private readonly map = new Map(); + /** + * @inheritdoc + */ + public get tests() { + return this.byExtId.values(); + } + + private readonly byExtId = new Map(); constructor(private readonly serialized: ISerializedResults) { - for (const [key, value] of serialized.items) { - this.map.set(key, value); + this.id = serialized.id; - value.retired = true; - for (const message of value.state.messages) { + for (const item of serialized.items) { + const cast: TestResultItem = { ...item, retired: true, children: new Set(item.children) }; + if (cast.item.location) { + cast.item.location.uri = URI.revive(cast.item.location.uri); + cast.item.location.range = Range.lift(cast.item.location.range); + } + + for (const message of cast.state.messages) { if (message.location) { message.location.uri = URI.revive(message.location.uri); + message.location.range = Range.lift(message.location.range); } } + + this.counts[item.state.state]++; + this.byExtId.set(item.item.extId, cast); } } @@ -403,7 +466,7 @@ class HydratedTestResult implements ITestResult { * @inheritdoc */ public getStateByExtId(extTestId: string) { - return this.map.get(extTestId); + return this.byExtId.get(extTestId); } /** @@ -429,12 +492,7 @@ export interface ITestResultService { /** * Fired when a test changed it state, or its computed state is updated. */ - readonly onTestChanged: Event<[results: ITestResult, item: TestResultItem]>; - - /** - * Fired when a test is retired, in addition to `onTestChanged`. - */ - readonly onTestRetired: Event; + readonly onTestChanged: Event; /** * List of known test results. @@ -469,8 +527,7 @@ const RETAIN_LAST_RESULTS = 64; export class TestResultService implements ITestResultService { declare _serviceBrand: undefined; private changeResultEmitter = new Emitter(); - private testRetiredEmitter = new Emitter(); - private testChangeEmitter = new Emitter<[results: ITestResult, item: TestResultItem]>(); + private testChangeEmitter = new Emitter(); /** * @inheritdoc @@ -487,11 +544,6 @@ export class TestResultService implements ITestResultService { */ public readonly onTestChanged = this.testChangeEmitter.event; - /** - * @inheritdoc - */ - public readonly onTestRetired = this.testRetiredEmitter.event; - private readonly isRunning: IContextKey; private readonly serializedResults: StoredValue; @@ -503,8 +555,12 @@ export class TestResultService implements ITestResultService { target: StorageTarget.MACHINE }, storage); - for (const value of this.serializedResults.get([])) { - this.results.push(new HydratedTestResult(value)); + try { + for (const value of this.serializedResults.get([])) { + this.results.push(new HydratedTestResult(value)); + } + } catch (e) { + // outdated structure } } @@ -532,8 +588,7 @@ export class TestResultService implements ITestResultService { } result.onComplete(() => this.onComplete(result)); - result.onChange(t => this.testChangeEmitter.fire([result, t]), this.testChangeEmitter); - result.onRetired(this.testRetiredEmitter.fire, this.testRetiredEmitter); + result.onChange(this.testChangeEmitter.fire, this.testChangeEmitter); this.isRunning.set(true); this.changeResultEmitter.fire({ started: result }); result.setAllToState(queuedState, () => true); @@ -568,7 +623,7 @@ export class TestResultService implements ITestResultService { private onComplete(result: LiveTestResult) { // move the complete test run down behind any still-running ones - for (let i = 0; i < this.results.length - 2; i++) { + for (let i = 0; i < this.results.length - 1; i++) { if (this.results[i].isComplete && !this.results[i + 1].isComplete) { [this.results[i], this.results[i + 1]] = [this.results[i + 1], this.results[i]]; } diff --git a/src/vs/workbench/contrib/testing/common/testService.ts b/src/vs/workbench/contrib/testing/common/testService.ts index 61639ba1874..8d1e5221dde 100644 --- a/src/vs/workbench/contrib/testing/common/testService.ts +++ b/src/vs/workbench/contrib/testing/common/testService.ts @@ -74,6 +74,23 @@ export const waitForAllRoots = (collection: IMainThreadTestCollection, timeout = }).finally(() => listener.dispose()); }; +export const waitForAllTests = (collection: IMainThreadTestCollection, timeout = 3000) => { + if (collection.busyProviders === 0) { + return Promise.resolve(); + } + + let listener: IDisposable; + return new Promise(resolve => { + listener = collection.onBusyProvidersChange(count => { + if (count === 0) { + resolve(); + } + }); + + setTimeout(resolve, timeout); + }).finally(() => listener.dispose()); +}; + export interface ITestService { readonly _serviceBrand: undefined; readonly onShouldSubscribe: Event<{ resource: ExtHostTestingResource, uri: URI; }>; diff --git a/src/vs/workbench/contrib/testing/common/testServiceImpl.ts b/src/vs/workbench/contrib/testing/common/testServiceImpl.ts index 8a18a20daa5..8b65c2c83ff 100644 --- a/src/vs/workbench/contrib/testing/common/testServiceImpl.ts +++ b/src/vs/workbench/contrib/testing/common/testServiceImpl.ts @@ -39,12 +39,16 @@ export class TestService extends Disposable implements ITestService { private readonly busyStateChangeEmitter = new Emitter(); private readonly changeProvidersEmitter = new Emitter<{ delta: number }>(); private readonly providerCount: IContextKey; + private readonly hasRunnable: IContextKey; + private readonly hasDebuggable: IContextKey; private readonly runningTests = new Map(); private rootProviderCount = 0; constructor(@IContextKeyService contextKeyService: IContextKeyService, @INotificationService private readonly notificationService: INotificationService, @ITestResultService private readonly testResults: ITestResultService) { super(); this.providerCount = TestingContextKeys.providerCount.bindTo(contextKeyService); + this.hasDebuggable = TestingContextKeys.hasDebuggableTests.bindTo(contextKeyService); + this.hasRunnable = TestingContextKeys.hasRunnableTests.bindTo(contextKeyService); } /** @@ -124,7 +128,7 @@ export class TestService extends Disposable implements ITestService { const subscriptions = [...this.testSubscriptions.values()] .filter(v => req.tests.some(t => v.collection.getNodeById(t.testId))) .map(s => this.subscribeToDiffs(s.ident.resource, s.ident.uri)); - const result = this.testResults.push(LiveTestResult.from(subscriptions.map(s => s.object), req.tests)); + const result = this.testResults.push(LiveTestResult.from(subscriptions.map(s => s.object), req)); try { const tests = groupBy(req.tests, (a, b) => a.providerId === b.providerId ? 0 : 1); @@ -216,11 +220,14 @@ export class TestService extends Disposable implements ITestService { */ public publishDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff) { const sub = this.testSubscriptions.get(getTestSubscriptionKey(resource, URI.revive(uri))); - if (sub) { - sub.collection.apply(diff); - // console.log('accept', sub.collection, diff); - sub.onDiff.fire(diff); + if (!sub) { + return; } + + sub.collection.apply(diff); + sub.onDiff.fire(diff); + this.hasDebuggable.set(!!this.findTest(t => t.item.debuggable)); + this.hasRunnable.set(!!this.findTest(t => t.item.runnable)); } /** @@ -240,9 +247,21 @@ export class TestService extends Disposable implements ITestService { this.providerCount.set(this.testControllers.size); this.changeProvidersEmitter.fire({ delta: -1 }); } + + private findTest(predicate: (t: InternalTestItem) => boolean): InternalTestItem | undefined { + for (const { collection } of this.testSubscriptions.values()) { + for (const test of collection.all) { + if (predicate(test)) { + return test; + } + } + } + + return undefined; + } } -class MainThreadTestCollection extends AbstractIncrementalTestCollection implements IMainThreadTestCollection { +export class MainThreadTestCollection extends AbstractIncrementalTestCollection implements IMainThreadTestCollection { private pendingRootChangeEmitter = new Emitter(); private busyProvidersChangeEmitter = new Emitter(); private _busyProviders = 0; diff --git a/src/vs/workbench/contrib/testing/common/testingAutoRun.ts b/src/vs/workbench/contrib/testing/common/testingAutoRun.ts index 360bbf09c31..a1eae07dd53 100644 --- a/src/vs/workbench/contrib/testing/common/testingAutoRun.ts +++ b/src/vs/workbench/contrib/testing/common/testingAutoRun.ts @@ -13,7 +13,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { getTestingConfiguration, TestingConfigKeys } from 'vs/workbench/contrib/testing/common/configuration'; import { TestIdWithProvider } from 'vs/workbench/contrib/testing/common/testCollection'; import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys'; -import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; +import { ITestResultService, TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResultService'; import { ITestService } from 'vs/workbench/contrib/testing/common/testService'; import { IWorkspaceTestCollectionService } from 'vs/workbench/contrib/testing/common/workspaceTestCollectionService'; @@ -82,7 +82,7 @@ export class TestingAutoRun extends Disposable implements ITestingAutoRun { isRunning = true; rerunIds.clear(); - await this.testService.runTests({ debug: false, tests }); + await this.testService.runTests({ debug: false, tests, isAutoRun: true }); isRunning = false; if (rerunIds.size > 0) { @@ -90,10 +90,15 @@ export class TestingAutoRun extends Disposable implements ITestingAutoRun { } }, delay)); - store.add(this.results.onTestRetired(test => { + store.add(this.results.onTestChanged(evt => { + if (evt.reason !== TestResultItemChangeReason.Retired) { + return; + } + + const { extId } = evt.item.item; const workspaceTest = mapFind(workspaceTests.workspaceFolderCollections, - ([, c]) => c.getNodeById(test.id) ?? Iterable.find(c.all, t => t.item.extId === test.item.extId)); - const subject = workspaceTest ?? test; + ([, c]) => c.getNodeById(evt.item.id) ?? Iterable.find(c.all, t => t.item.extId === extId)); + const subject = workspaceTest ?? evt.item; rerunIds.set(subject.id, ({ testId: subject.id, providerId: subject.providerId })); diff --git a/src/vs/workbench/contrib/testing/common/testingContentProvider.ts b/src/vs/workbench/contrib/testing/common/testingContentProvider.ts index c63a507d15f..b3e91103ce4 100644 --- a/src/vs/workbench/contrib/testing/common/testingContentProvider.ts +++ b/src/vs/workbench/contrib/testing/common/testingContentProvider.ts @@ -38,7 +38,7 @@ export class TestingContentProvider implements IWorkbenchContribution, ITextMode return null; } - const test = this.resultService.getResult(parsed.resultId)?.getStateByExtId(parsed.testId); + const test = this.resultService.getResult(parsed.resultId)?.getStateByExtId(parsed.testExtId); if (!test) { return null; diff --git a/src/vs/workbench/contrib/testing/common/testingContextKeys.ts b/src/vs/workbench/contrib/testing/common/testingContextKeys.ts index 3a1f40ad723..6f951ef63ac 100644 --- a/src/vs/workbench/contrib/testing/common/testingContextKeys.ts +++ b/src/vs/workbench/contrib/testing/common/testingContextKeys.ts @@ -9,6 +9,8 @@ import { TestExplorerViewMode, TestExplorerViewSorting } from 'vs/workbench/cont export namespace TestingContextKeys { export const providerCount = new RawContextKey('testing.providerCount', 0); + export const hasDebuggableTests = new RawContextKey('testing.hasDebuggableTests', false); + export const hasRunnableTests = new RawContextKey('testing.hasRunnableTests', false); export const viewMode = new RawContextKey('testing.explorerViewMode', TestExplorerViewMode.List); export const viewSorting = new RawContextKey('testing.explorerViewSorting', TestExplorerViewSorting.ByLocation); export const isRunning = new RawContextKey('testing.isRunning', false); diff --git a/src/vs/workbench/contrib/testing/common/testingUri.ts b/src/vs/workbench/contrib/testing/common/testingUri.ts index 5f99df72ba0..9503c99e152 100644 --- a/src/vs/workbench/contrib/testing/common/testingUri.ts +++ b/src/vs/workbench/contrib/testing/common/testingUri.ts @@ -15,7 +15,7 @@ export const enum TestUriType { interface IResultTestUri { resultId: string; - testId: string; + testExtId: string; } interface IResultTestMessageReference extends IResultTestUri { @@ -43,19 +43,20 @@ const enum TestUriParts { export const parseTestUri = (uri: URI): ParsedTestUri | undefined => { const type = uri.authority; - const [locationId, testId, ...request] = uri.path.slice(1).split('/'); + const [locationId, ...request] = uri.path.slice(1).split('/'); if (request[0] === TestUriParts.Messages) { const index = Number(request[1]); const part = request[2]; + const testExtId = uri.query; if (type === TestUriParts.Results) { switch (part) { case TestUriParts.Text: - return { resultId: locationId, testId, messageIndex: index, type: TestUriType.ResultMessage }; + return { resultId: locationId, testExtId, messageIndex: index, type: TestUriType.ResultMessage }; case TestUriParts.ActualOutput: - return { resultId: locationId, testId, messageIndex: index, type: TestUriType.ResultActualOutput }; + return { resultId: locationId, testExtId, messageIndex: index, type: TestUriType.ResultActualOutput }; case TestUriParts.ExpectedOutput: - return { resultId: locationId, testId, messageIndex: index, type: TestUriType.ResultExpectedOutput }; + return { resultId: locationId, testExtId, messageIndex: index, type: TestUriType.ResultExpectedOutput }; } } } @@ -71,7 +72,8 @@ export const buildTestUri = (parsed: ParsedTestUri): URI => { const msgRef = (locationId: string, index: number, ...remaining: string[]) => URI.from({ ...uriParts, - path: ['', locationId, parsed.testId, TestUriParts.Messages, index, ...remaining].join('/'), + query: parsed.testExtId, + path: ['', locationId, TestUriParts.Messages, index, ...remaining].join('/'), }); switch (parsed.type) { diff --git a/src/vs/workbench/contrib/testing/test/common/ownedTestCollection.ts b/src/vs/workbench/contrib/testing/test/common/ownedTestCollection.ts index a0f841e4b3e..8df8bb684ff 100644 --- a/src/vs/workbench/contrib/testing/test/common/ownedTestCollection.ts +++ b/src/vs/workbench/contrib/testing/test/common/ownedTestCollection.ts @@ -5,6 +5,8 @@ import { OwnedTestCollection, SingleUseTestCollection } from 'vs/workbench/contrib/testing/common/ownedTestCollection'; import { TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection'; +import { MainThreadTestCollection } from 'vs/workbench/contrib/testing/common/testServiceImpl'; +import { testStubs } from 'vs/workbench/contrib/testing/common/testStubs'; export class TestSingleUseCollection extends SingleUseTestCollection { private idCounter = 0; @@ -35,3 +37,15 @@ export class TestOwnedTestCollection extends OwnedTestCollection { return new TestSingleUseCollection(this.testIdToInternal, publishDiff); } } + +/** + * Gets a main thread test collection initialized with the given set of + * roots/stubs. + */ +export const getInitializedMainTestCollection = (root = testStubs.nested()) => { + const c = new MainThreadTestCollection(0); + const singleUse = new TestSingleUseCollection(new Map(), () => undefined); + singleUse.addRoot(root, 'provider'); + c.apply(singleUse.collectDiff()); + return c; +}; diff --git a/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts b/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts new file mode 100644 index 00000000000..86851a62f51 --- /dev/null +++ b/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts @@ -0,0 +1,194 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; +import { InternalTestItem } from 'vs/workbench/contrib/testing/common/testCollection'; +import { LiveTestResult, makeEmptyCounts, TestResultItemChange, TestResultItemChangeReason, TestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; +import { ReExportedTestRunState as TestRunState } from 'vs/workbench/contrib/testing/common/testStubs'; +import { getInitializedMainTestCollection } from 'vs/workbench/contrib/testing/test/common/ownedTestCollection'; +import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; + +suite('Workbench - Test Results Service', () => { + const getLabelsIn = (it: Iterable) => [...it].map(t => t.item.label).sort(); + const getChangeSummary = () => [...changed] + .map(c => ({ reason: c.reason, label: c.item.item.label })) + .sort((a, b) => a.label.localeCompare(b.label)); + + let r: LiveTestResult; + let changed = new Set(); + + setup(() => { + changed = new Set(); + r = LiveTestResult.from( + [getInitializedMainTestCollection()], + { tests: [{ providerId: 'provider', testId: '1' }], debug: false } + ); + + r.onChange(e => changed.add(e)); + }); + + suite('LiveTestResult', () => { + test('is empty if no tests are requesteed', () => { + const r = LiveTestResult.from([getInitializedMainTestCollection()], { tests: [], debug: false }); + assert.deepStrictEqual(getLabelsIn(r.tests), []); + }); + + test('does not change or retire initially', () => { + assert.deepStrictEqual(0, changed.size); + }); + + test('initializes with the subtree of requested tests', () => { + assert.deepStrictEqual(getLabelsIn(r.tests), ['a', 'aa', 'ab', 'root']); + }); + + test('initializes with valid counts', () => { + assert.deepStrictEqual(r.counts, { + ...makeEmptyCounts(), + [TestRunState.Unset]: 4 + }); + }); + + test('setAllToState', () => { + r.setAllToState({ state: TestRunState.Queued, duration: 0, messages: [] }, t => t.item.label !== 'root'); + assert.deepStrictEqual(r.counts, { + ...makeEmptyCounts(), + [TestRunState.Unset]: 1, + [TestRunState.Queued]: 3, + }); + + assert.deepStrictEqual(r.getStateByExtId('root\0a')?.state.state, TestRunState.Queued); + assert.deepStrictEqual(getChangeSummary(), [ + { label: 'a', reason: TestResultItemChangeReason.OwnStateChange }, + { label: 'aa', reason: TestResultItemChangeReason.OwnStateChange }, + { label: 'ab', reason: TestResultItemChangeReason.OwnStateChange }, + { label: 'root', reason: TestResultItemChangeReason.ComputedStateChange }, + ]); + }); + + test('updateState', () => { + r.updateState('1', { state: TestRunState.Running, duration: 0, messages: [] }); + assert.deepStrictEqual(r.counts, { + ...makeEmptyCounts(), + [TestRunState.Running]: 1, + [TestRunState.Unset]: 3, + }); + assert.deepStrictEqual(r.getStateByExtId('root\0a')?.state.state, TestRunState.Running); + // update computed state: + assert.deepStrictEqual(r.getStateByExtId('root')?.computedState, TestRunState.Running); + assert.deepStrictEqual(getChangeSummary(), [ + { label: 'a', reason: TestResultItemChangeReason.OwnStateChange }, + { label: 'root', reason: TestResultItemChangeReason.ComputedStateChange }, + ]); + }); + + test('retire', () => { + r.retire('root\0a'); + assert.deepStrictEqual(getChangeSummary(), [ + { label: 'a', reason: TestResultItemChangeReason.Retired }, + { label: 'aa', reason: TestResultItemChangeReason.ParentRetired }, + { label: 'ab', reason: TestResultItemChangeReason.ParentRetired }, + ]); + + changed.clear(); + r.retire('root\0a'); + assert.strictEqual(changed.size, 0); + }); + + test('addTestToRun', () => { + r.updateState('4', { state: TestRunState.Running, duration: 0, messages: [] }); + assert.deepStrictEqual(r.counts, { + ...makeEmptyCounts(), + [TestRunState.Running]: 1, + [TestRunState.Unset]: 4, + }); + assert.deepStrictEqual(r.getStateByExtId('root\0b')?.state.state, TestRunState.Running); + // update computed state: + assert.deepStrictEqual(r.getStateByExtId('root')?.computedState, TestRunState.Running); + }); + + test('markComplete', () => { + r.setAllToState({ state: TestRunState.Queued, duration: 0, messages: [] }, t => true); + r.updateState('2', { state: TestRunState.Passed, duration: 0, messages: [] }); + changed.clear(); + + r.markComplete(); + + assert.deepStrictEqual(r.counts, { + ...makeEmptyCounts(), + [TestRunState.Passed]: 1, + [TestRunState.Unset]: 3, + }); + + assert.deepStrictEqual(r.getStateByExtId('root')?.state.state, TestRunState.Unset); + assert.deepStrictEqual(r.getStateByExtId('root\0a\0aa')?.state.state, TestRunState.Passed); + }); + }); + + suite('service', () => { + let storage: TestStorageService; + let results: TestResultService; + + setup(() => { + storage = new TestStorageService(); + results = new TestResultService( + new MockContextKeyService(), + storage, + ); + }); + + test('pushes new result', () => { + results.push(r); + assert.deepStrictEqual(results.results, [r]); + }); + + test('serializes and re-hydrates', () => { + results.push(r); + r.updateState('2', { state: TestRunState.Passed, duration: 0, messages: [] }); + r.markComplete(); + + results = new TestResultService( + new MockContextKeyService(), + storage, + ); + + const [rehydrated, actual] = results.getStateByExtId('root')!; + const expected = r.getStateByExtId('root')!; + delete expected.state.duration; // delete undefined props that don't survive serialization + delete expected.item.location; + + assert.deepStrictEqual(actual, { ...expected, retired: true }); + assert.deepStrictEqual(rehydrated.counts, r.counts); + assert.strictEqual(rehydrated.isComplete, true); + }); + + test('clears results but keeps ongoing tests', () => { + results.push(r); + r.markComplete(); + + const r2 = results.push(LiveTestResult.from( + [getInitializedMainTestCollection()], + { tests: [{ providerId: 'provider', testId: '1' }], debug: false } + )); + results.clear(); + + assert.deepStrictEqual(results.results, [r2]); + }); + + test('keeps ongoing tests on top', () => { + results.push(r); + const r2 = results.push(LiveTestResult.from( + [getInitializedMainTestCollection()], + { tests: [{ providerId: 'provider', testId: '1' }], debug: false } + )); + + assert.deepStrictEqual(results.results, [r2, r]); + r2.markComplete(); + assert.deepStrictEqual(results.results, [r, r2]); + r.markComplete(); + assert.deepStrictEqual(results.results, [r, r2]); + }); + }); +}); diff --git a/src/vs/workbench/contrib/testing/test/common/testingUri.test.ts b/src/vs/workbench/contrib/testing/test/common/testingUri.test.ts index 41d534e5d69..e9c1d60412e 100644 --- a/src/vs/workbench/contrib/testing/test/common/testingUri.test.ts +++ b/src/vs/workbench/contrib/testing/test/common/testingUri.test.ts @@ -9,9 +9,9 @@ import { buildTestUri, ParsedTestUri, parseTestUri, TestUriType } from 'vs/workb suite('Workbench - Testing URIs', () => { test('round trip', () => { const uris: ParsedTestUri[] = [ - { type: TestUriType.ResultActualOutput, messageIndex: 42, resultId: 'r', testId: 't' }, - { type: TestUriType.ResultExpectedOutput, messageIndex: 42, resultId: 'r', testId: 't' }, - { type: TestUriType.ResultMessage, messageIndex: 42, resultId: 'r', testId: 't' }, + { type: TestUriType.ResultActualOutput, messageIndex: 42, resultId: 'r', testExtId: 't' }, + { type: TestUriType.ResultExpectedOutput, messageIndex: 42, resultId: 'r', testExtId: 't' }, + { type: TestUriType.ResultMessage, messageIndex: 42, resultId: 'r', testExtId: 't' }, ]; for (const uri of uris) { diff --git a/src/vs/workbench/contrib/url/browser/trustedDomains.ts b/src/vs/workbench/contrib/url/browser/trustedDomains.ts index b5188bfb036..145afdf032c 100644 --- a/src/vs/workbench/contrib/url/browser/trustedDomains.ts +++ b/src/vs/workbench/contrib/url/browser/trustedDomains.ts @@ -206,7 +206,7 @@ export async function readWorkspaceTrustedDomains(accessor: ServicesAccessor): P export async function readAuthenticationTrustedDomains(accessor: ServicesAccessor): Promise { const authenticationService = accessor.get(IAuthenticationService); - return authenticationService.isAuthenticationProviderRegistered('github') && ((await authenticationService.getAllSessions('github')) ?? []).length > 0 + return authenticationService.isAuthenticationProviderRegistered('github') && ((await authenticationService.getSessions('github')) ?? []).length > 0 ? [`https://github.com`] : []; } diff --git a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts index d67cf68e348..102f7132e9c 100644 --- a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts +++ b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts @@ -60,10 +60,13 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv return !!this._webview.value?.isFocused; } + private _isDisposed = false; + private readonly _onDidDispose = this._register(new Emitter()); public onDidDispose = this._onDidDispose.event; dispose() { + this._isDisposed = true; this.container.remove(); this._onDidDispose.fire(); super.dispose(); @@ -138,6 +141,10 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv } private show() { + if (this._isDisposed) { + throw new Error('Webview overlay is disposed'); + } + if (!this._webview.value) { const webview = this._webviewService.createWebviewElement(this.id, this._options, this._contentOptions, this.extension); this._webview.value = webview; diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index b2f093ee195..2d4ac83084a 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -446,6 +446,8 @@ host.onMessage('focus', () => { const activeFrame = getActiveFrame(); if (!activeFrame || !activeFrame.contentWindow) { + // Focus the top level webview instead + window.focus(); return; } @@ -589,6 +591,10 @@ contentWindow.addEventListener('scroll', handleInnerScroll); contentWindow.addEventListener('wheel', handleWheel); + if (document.hasFocus()) { + contentWindow.focus(); + } + pendingMessages.forEach((data) => { contentWindow.postMessage(data, '*'); }); diff --git a/src/vs/workbench/contrib/webview/browser/pre/service-worker.js b/src/vs/workbench/contrib/webview/browser/pre/service-worker.js index 1030c047421..f6c93682fea 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/service-worker.js +++ b/src/vs/workbench/contrib/webview/browser/pre/service-worker.js @@ -2,13 +2,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 + +/// /// +const sw = /** @type {ServiceWorkerGlobalScope} */ (/** @type {any} */ (self)); + const VERSION = 1; const resourceCacheName = `vscode-resource-cache-${VERSION}`; -const rootPath = self.location.pathname.replace(/\/service-worker.js$/, ''); +const rootPath = sw.location.pathname.replace(/\/service-worker.js$/, ''); /** * Root path for resources @@ -101,11 +106,12 @@ const localhostRequestStore = new RequestStore(); const notFound = () => new Response('Not Found', { status: 404, }); -self.addEventListener('message', async (event) => { +sw.addEventListener('message', async (event) => { switch (event.data.channel) { case 'version': { - self.clients.get(event.source.id).then(client => { + const source = /** @type {Client} */ (event.source); + sw.clients.get(source.id).then(client => { if (client) { client.postMessage({ channel: 'version', @@ -153,26 +159,26 @@ self.addEventListener('message', async (event) => { console.log('Unknown message'); }); -self.addEventListener('fetch', (event) => { +sw.addEventListener('fetch', (event) => { const requestUrl = new URL(event.request.url); // See if it's a resource request - if (requestUrl.origin === self.origin && requestUrl.pathname.startsWith(resourceRoot + '/')) { + if (requestUrl.origin === sw.origin && requestUrl.pathname.startsWith(resourceRoot + '/')) { return event.respondWith(processResourceRequest(event, requestUrl)); } // See if it's a localhost request - if (requestUrl.origin !== self.origin && requestUrl.host.match(/^localhost:(\d+)$/)) { + if (requestUrl.origin !== sw.origin && requestUrl.host.match(/^localhost:(\d+)$/)) { return event.respondWith(processLocalhostRequest(event, requestUrl)); } }); -self.addEventListener('install', (event) => { - event.waitUntil(self.skipWaiting()); // Activate worker immediately +sw.addEventListener('install', (event) => { + event.waitUntil(sw.skipWaiting()); // Activate worker immediately }); -self.addEventListener('activate', (event) => { - event.waitUntil(self.clients.claim()); // Become available to all pages +sw.addEventListener('activate', (event) => { + event.waitUntil(sw.clients.claim()); // Become available to all pages }); /** @@ -180,7 +186,7 @@ self.addEventListener('activate', (event) => { * @param {URL} requestUrl */ async function processResourceRequest(event, requestUrl) { - const client = await self.clients.get(event.clientId); + const client = await sw.clients.get(event.clientId); if (!client) { console.log('Could not find inner client for request'); return notFound(); @@ -252,7 +258,7 @@ async function processResourceRequest(event, requestUrl) { * @param {URL} requestUrl */ async function processLocalhostRequest(event, requestUrl) { - const client = await self.clients.get(event.clientId); + const client = await sw.clients.get(event.clientId); if (!client) { // This is expected when requesting resources on other localhost ports // that are not spawned by vs code @@ -299,7 +305,7 @@ function getWebviewIdForClient(client) { } async function getOuterIframeClient(webviewId) { - const allClients = await self.clients.matchAll({ includeUncontrolled: true }); + const allClients = await sw.clients.matchAll({ includeUncontrolled: true }); return allClients.find(client => { const clientUrl = new URL(client.url); return (clientUrl.pathname === `${rootPath}/` || clientUrl.pathname === `${rootPath}/index.html`) && clientUrl.search.match(new RegExp('\\bid=' + webviewId)); diff --git a/src/vs/workbench/contrib/webview/electron-sandbox/iframeWebviewElement.ts b/src/vs/workbench/contrib/webview/electron-sandbox/iframeWebviewElement.ts index 672018e2ce4..5321866d575 100644 --- a/src/vs/workbench/contrib/webview/electron-sandbox/iframeWebviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-sandbox/iframeWebviewElement.ts @@ -128,8 +128,16 @@ export class ElectronIframeWebview extends IFrameWebview { return; } + // Clear the existing focus first if not already on the webview. + // This is required because the next part where we set the focus is async. + if (document.activeElement && document.activeElement instanceof HTMLElement && document.activeElement !== this.element) { + // Don't blur if on the webview because this will also happen async and may unset the focus + // after the focus trigger fires below. + document.activeElement.blur(); + } + // Workaround for https://github.com/microsoft/vscode/issues/75209 - // .focus is async for imframes so for a sequence of actions such as: + // Electron's webview.focus is async so for a sequence of actions such as: // // 1. Open webview // 1. Show quick pick from command palette @@ -143,8 +151,7 @@ export class ElectronIframeWebview extends IFrameWebview { if (!this.isFocused || !this.element) { return; } - - if (document.activeElement?.tagName === 'INPUT') { + if (document.activeElement && document.activeElement?.tagName !== 'BODY') { return; } try { diff --git a/src/vs/workbench/contrib/webviewPanel/browser/webviewEditor.ts b/src/vs/workbench/contrib/webviewPanel/browser/webviewEditor.ts index 1eebb76e499..5a6f5253013 100644 --- a/src/vs/workbench/contrib/webviewPanel/browser/webviewEditor.ts +++ b/src/vs/workbench/contrib/webviewPanel/browser/webviewEditor.ts @@ -31,6 +31,7 @@ export class WebviewEditor extends EditorPane { private _element?: HTMLElement; private _dimension?: DOM.Dimension; private _visible = false; + private _isDisposed = false; private readonly _webviewVisibleDisposables = this._register(new DisposableStore()); private readonly _onFocusWindowHandler = this._register(new MutableDisposable()); @@ -71,6 +72,8 @@ export class WebviewEditor extends EditorPane { } public dispose(): void { + this._isDisposed = true; + if (this._element) { this._element.remove(); this._element = undefined; @@ -133,7 +136,7 @@ export class WebviewEditor extends EditorPane { await super.setInput(input, options, context, token); await input.resolve(); - if (token.isCancellationRequested) { + if (token.isCancellationRequested || this._isDisposed) { return; } diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts index cb7bcf8f3c9..f890c8f273e 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts +++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts @@ -242,6 +242,11 @@ export class GettingStartedPage extends EditorPane { mediaElement.setAttribute('src', ''); mediaElement.setAttribute('alt', ''); } + setTimeout(() => { + // rescan after animation finishes + this.detailsScrollbar?.scanDomNode(); + this.detailImageScrollbar?.scanDomNode(); + }, 100); this.detailsScrollbar?.scanDomNode(); this.detailImageScrollbar?.scanDomNode(); } @@ -302,7 +307,9 @@ export class GettingStartedPage extends EditorPane { $('.category-description.description', { 'aria-label': category.description + ' ' + localize('pressEnterToSelect', "Press Enter to Select") }, category.description), $('.category-progress', { 'x-data-category-id': category.id, }, $('.message'), - $('.progress-bar-outer', {}, + $('.progress-bar-outer', { + 'role': 'progressbar' + }, $('.progress-bar-inner')))) : $('.category-description-container', {}, @@ -310,12 +317,15 @@ export class GettingStartedPage extends EditorPane { $('.category-description.description', { 'aria-label': category.description + ' ' + localize('pressEnterToSelect', "Press Enter to Select") }, category.description)); return $('button.getting-started-category', - { 'x-dispatch': 'selectCategory:' + category.id, }, + { + 'x-dispatch': 'selectCategory:' + category.id, + 'role': 'listitem', + }, $(ThemeIcon.asCSSSelector(category.icon), {}), categoryDescriptionElement); }); const categoryScrollContainer = $('.getting-started-categories-scrolling-container'); - const categoriesContainer = $('.getting-started-categories-container'); + const categoriesContainer = $('.getting-started-categories-container', { 'role': 'list' }); categoryElements.forEach(element => { categoriesContainer.appendChild(element); }); @@ -324,7 +334,7 @@ export class GettingStartedPage extends EditorPane { const showOnStartupCheckbox = $('input.checkbox', { id: 'showOnStartup', type: 'checkbox' }) as HTMLInputElement; categoryScrollContainer.appendChild( $('.footer', {}, - $('button.skip.button-link', { 'x-dispatch': 'skip' }, localize('gettingStarted.skip', "Skip")), + // $('button.skip.button-link', { 'x-dispatch': 'skip' }, localize('gettingStarted.skip', "Skip")), $('p.showOnStartup', {}, showOnStartupCheckbox, $('label.caption', { for: 'showOnStartup' }, localize('welcomePage.showOnStartup', "Show Getting Started page on startup"))) @@ -384,6 +394,10 @@ export class GettingStartedPage extends EditorPane { const message = assertIsDefined(element.firstChild); const bar = assertIsDefined(element.querySelector('.progress-bar-inner')) as HTMLDivElement; + bar.setAttribute('aria-valuemin', '0'); + bar.setAttribute('aria-valuenow', '' + numDone); + bar.setAttribute('aria-valuemax', '' + numTotal); + bar.style.width = `${(numDone / numTotal) * 100}%`; if (numTotal === numDone) { @@ -429,6 +443,7 @@ export class GettingStartedPage extends EditorPane { 'x-dispatch': 'selectTask:' + task.id, 'data-task-id': task.id, 'aria-expanded': 'false', + 'role': 'listitem', }, $('.codicon' + (task.done ? '.complete.codicon-pass-filled' : '.codicon-circle-large-outline'), { 'data-done-task-id': task.id }), $('.task-description-container', {}, @@ -451,7 +466,7 @@ export class GettingStartedPage extends EditorPane { )) ))); - const detailContainer = $('.getting-started-detail-container'); + const detailContainer = $('.getting-started-detail-container', { 'role': 'list' }); if (this.detailsScrollbar) { this.detailsScrollbar.getDomNode().remove(); this.detailsScrollbar.dispose(); } this.detailsScrollbar = this._register(new DomScrollableElement(detailContainer, { className: 'full-height-scrollable' })); categoryElements.forEach(element => detailContainer.appendChild(element)); diff --git a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts index 8d6cb051ee9..04237e078bd 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts +++ b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts @@ -3,11 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import 'vs/css!./workspaceTrustEditor'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; import { Action2, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { Severity } from 'vs/platform/notification/common/notification'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkspaceTrustService, WorkspaceTrustState, WorkspaceTrustStateChangeEvent, workspaceTrustStateToString } from 'vs/platform/workspace/common/workspaceTrust'; @@ -18,13 +20,16 @@ import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { Codicon } from 'vs/base/common/codicons'; import { ThemeColor } from 'vs/workbench/api/common/extHostTypes'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { WorkspaceTrustFileSystemProvider } from 'vs/workbench/contrib/workspace/common/workspaceTrustFileSystemProvider'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/common/statusbar'; -import { WorkspaceTrustContext, WORKSPACE_TRUST_ENABLED, WORKSPACE_TRUST_URI } from 'vs/workbench/services/workspaces/common/workspaceTrust'; +import { IEditorRegistry, Extensions as EditorExtensions, EditorDescriptor } from 'vs/workbench/browser/editor'; +import { WorkspaceTrustEditor } from 'vs/workbench/contrib/workspace/browser/workspaceTrustEditor'; +import { WorkspaceTrustEditorInput } from 'vs/workbench/services/workspaces/browser/workspaceTrustEditorInput'; +import { WorkspaceTrustContext, WORKSPACE_TRUST_ENABLED } from 'vs/workbench/services/workspaces/common/workspaceTrust'; +import { EditorInput, Extensions as EditorInputExtensions, IEditorInputFactory, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; const workspaceTrustIcon = registerIcon('workspace-trust-icon', Codicon.shield, localize('workspaceTrustIcon', "Icon for workspace trust badge.")); @@ -173,12 +178,36 @@ Registry.as(WorkbenchExtensions.Workbench).regi LifecyclePhase.Starting ); -/* - * Trusted Workspace JSON Editor +/** + * Trusted Workspace GUI Editor */ -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( - WorkspaceTrustFileSystemProvider, - LifecyclePhase.Ready +class WorkspaceTrustEditorInputInputFactory implements IEditorInputFactory { + + canSerialize(editorInput: EditorInput): boolean { + return true; + } + + serialize(input: WorkspaceTrustEditorInput): string { + return '{}'; + } + + deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): WorkspaceTrustEditorInput { + return instantiationService.createInstance(WorkspaceTrustEditorInput); + } +} + +Registry.as(EditorInputExtensions.EditorInputFactories) + .registerEditorInputFactory(WorkspaceTrustEditorInput.ID, WorkspaceTrustEditorInputInputFactory); + +Registry.as(EditorExtensions.Editors).registerEditor( + EditorDescriptor.create( + WorkspaceTrustEditor, + WorkspaceTrustEditor.ID, + localize('workspaceTrustEditor', "Workspace Trust Editor") + ), + [ + new SyncDescriptor(WorkspaceTrustEditorInput) + ] ); /* @@ -285,7 +314,11 @@ registerAction2(class extends Action2 { run(accessor: ServicesAccessor) { const editorService = accessor.get(IEditorService); - editorService.openEditor({ resource: WORKSPACE_TRUST_URI, mode: 'jsonc', options: { pinned: true } }); + const instantiationService = accessor.get(IInstantiationService); + + const input = instantiationService.createInstance(WorkspaceTrustEditorInput); + + editorService.openEditor(input, { pinned: true, revealIfOpened: true }); return; } }); diff --git a/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.css b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.css new file mode 100644 index 00000000000..45ec02a5da4 --- /dev/null +++ b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.css @@ -0,0 +1,87 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-icon-label.file-icon.workspacetrusteditor-name-file-icon.ext-file-icon.tab-label::before { + font-family: 'codicon'; + content: '\eb53'; +} + +.workspace-trust-editor.settings-editor { + max-width: 1000px; + padding-top: 11px; + padding-left: 15px; + padding-right: 15px; + margin: auto; +} + +.workspace-trust-editor.settings-editor > .workspace-trust-header { + padding: 14px; + border: solid 1px; +} + +.workspace-trust-editor.settings-editor .workspace-trust-header .workspace-trust-title { + font-size: 24px; + font-weight: 600; + padding: 10px 0px; +} + +/** Buttons Container */ +.workspace-trust-editor.settings-editor .workspace-trust-header .workspace-trust-buttons-row { + display: flex; + align-items: center; + justify-content: flex-end; + padding: 20px 0 10px 0; + overflow: hidden; /* buttons row should never overflow */ + white-space: nowrap; +} + +/** Buttons */ +.workspace-trust-editor.settings-editor .workspace-trust-header .workspace-trust-buttons-row .workspace-trust-buttons { + display: flex; + overflow: hidden; +} + +.workspace-trust-editor.settings-editor .workspace-trust-header .workspace-trust-buttons-row .workspace-trust-buttons .monaco-button { + width: fit-content; + width: -moz-fit-content; + padding: 5px 10px; + overflow: hidden; + text-overflow: ellipsis; + margin: 4px 5px; /* allows button focus outline to be visible */ + outline-offset: 2px !important; +} + +.monaco-workbench.vs .workspace-trust-editor.settings-editor > .workspace-trust-header.workspace-trust-unknown { + border-color: #cccccc; +} + +.monaco-workbench.vs-dark .workspace-trust-editor.settings-editor > .workspace-trust-header.workspace-trust-unknown { + border-color: #3c3c3c; +} + +.monaco-workbench.vs .workspace-trust-editor.settings-editor > .workspace-trust-header.workspace-trust-untrusted, +.monaco-workbench.vs-dark .workspace-trust-editor.settings-editor > .workspace-trust-header.workspace-trust-untrusted { + border-color: #FF0000; +} + +.monaco-workbench.vs .workspace-trust-editor.settings-editor > .workspace-trust-header.workspace-trust-trusted, +.monaco-workbench.vs-dark .workspace-trust-editor.settings-editor > .workspace-trust-header.workspace-trust-trusted { + border-color: #327E36; +} + +/** Settings */ +.workspace-trust-editor.settings-editor > .settings-body .settings-tree-container .monaco-list-row .monaco-tl-contents { + padding-left: 0; + padding-right: 0; +} + +.workspace-trust-editor.settings-editor > .settings-body .settings-tree-container .monaco-list-row .monaco-tl-contents .setting-list-edit-row { + margin-top: 4px; +} + +.workspace-trust-editor.settings-editor > .settings-body .settings-tree-container .monaco-list-row .monaco-tl-contents .setting-list-edit-row > .setting-list-valueInput { + width: 100%; + max-width: 100%; +} diff --git a/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts new file mode 100644 index 00000000000..47e60a806c1 --- /dev/null +++ b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts @@ -0,0 +1,189 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { $, append, clearNode, Dimension, EventHelper } from 'vs/base/browser/dom'; +import { ButtonBar } from 'vs/base/browser/ui/button/button'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { Iterable } from 'vs/base/common/iterator'; +import { isArray } from 'vs/base/common/types'; +import { URI } from 'vs/base/common/uri'; +import { localize } from 'vs/nls'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +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 { WorkspaceTrustState } from 'vs/platform/workspace/common/workspaceTrust'; +import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; +import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; +import { IWorkspaceTrustSettingChangeEvent, WorkspaceTrustSettingArrayRenderer, WorkspaceTrustTree, WorkspaceTrustTreeModel } from 'vs/workbench/contrib/workspace/browser/workspaceTrustTree'; +import { WorkspaceTrustEditorInput } from 'vs/workbench/services/workspaces/browser/workspaceTrustEditorInput'; +import { WorkspaceTrustEditorModel } from 'vs/workbench/services/workspaces/common/workspaceTrust'; + +export class WorkspaceTrustEditor extends EditorPane { + static readonly ID: string = 'workbench.editor.workspaceTrust'; + private rootElement!: HTMLElement; + private headerContainer!: HTMLElement; + private headerTitle!: HTMLElement; + private headerDescription!: HTMLElement; + private headerButtons!: HTMLElement; + private configurationContainer!: HTMLElement; + private trustSettingsTree!: WorkspaceTrustTree; + private workspaceTrustSettingsTreeModel!: WorkspaceTrustTreeModel; + private workspaceTrustEditorModel!: WorkspaceTrustEditorModel; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IThemeService themeService: IThemeService, + @IStorageService storageService: IStorageService, + @ICommandService private readonly commandService: ICommandService, + @IInstantiationService private readonly instantiationService: IInstantiationService + ) { super(WorkspaceTrustEditor.ID, telemetryService, themeService, storageService); } + + protected createEditor(parent: HTMLElement): void { + this.rootElement = append(parent, $('.workspace-trust-editor.settings-editor', { tabindex: '-1' })); + + this.createHeaderElement(this.rootElement); + this.createConfigurationElement(this.rootElement); + } + + async setInput(input: WorkspaceTrustEditorInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { + + await super.setInput(input, options, context, token); + if (token.isCancellationRequested) { return; } + + const model = await input.resolve(); + if (token.isCancellationRequested || !(model instanceof WorkspaceTrustEditorModel)) { + return; + } + + this._register(model.dataModel.onDidChangeTrustState(() => { + this.render(model); + })); + + this.render(model); + + this.workspaceTrustEditorModel = model; + } + + private getHeaderContainerClass(trustState: WorkspaceTrustState): string { + switch (trustState) { + case WorkspaceTrustState.Trusted: + return 'workspace-trust-header workspace-trust-trusted'; + case WorkspaceTrustState.Untrusted: + return 'workspace-trust-header workspace-trust-untrusted'; + case WorkspaceTrustState.Unknown: + return 'workspace-trust-header workspace-trust-unknown'; + } + } + + private getHeaderTitleText(trustState: WorkspaceTrustState): string { + switch (trustState) { + case WorkspaceTrustState.Trusted: + return localize('trustedHeader', "This Workspace is Trusted"); + case WorkspaceTrustState.Untrusted: + return localize('untrustedHeader', "This Workspace is Not Trusted"); + case WorkspaceTrustState.Unknown: + return localize('unknownHeader', "This Workspace has Not Been Trusted"); + } + } + + private getHeaderDescriptionText(trustState: WorkspaceTrustState): string { + switch (trustState) { + case WorkspaceTrustState.Trusted: + return localize('trustedHeaderDescription', "All features requiring trust in this workspace are enabled. Below is the current list of features that will be disabled you grant trust to the workspace. Note that after trust is given, new features requiring trust will automatically inheret the current workspace trust status."); + case WorkspaceTrustState.Untrusted: + return localize('untrustedHeaderDescription', "This workspace has limited functionality as some features will not work until trust is given to the current workspace. Below is the current list of features that will be disabled you grant trust to the workspace. Note that after trust is given, new features requiring trust will automatically inherit the current workspace trust status."); + case WorkspaceTrustState.Unknown: + return localize('unknownHeaderDescription', "This workspace has limited functionality as some features will not work until trust is given to the current workspace. Below is the current list of features that will be disabled you grant trust to the workspace. Note that after trust is given, new features requiring trust will automatically inherit the current workspace trust status."); + } + } + + private render(model: WorkspaceTrustEditorModel): void { + this.headerTitle.innerText = this.getHeaderTitleText(model.currentWorkspaceTrustState); + this.headerDescription.innerText = this.getHeaderDescriptionText(model.currentWorkspaceTrustState); + this.headerContainer.className = this.getHeaderContainerClass(model.currentWorkspaceTrustState); + + clearNode(this.headerButtons); + const buttonBar = this._register(new ButtonBar(this.headerButtons)); + + const createButton = (label: string, command: string) => { + const button = buttonBar.addButton({ title: true }); + button.label = label; + this._register(button.onDidClick(e => { + if (e) { + EventHelper.stop(e); + } + + this.commandService.executeCommand(command); + })); + }; + + if (model.currentWorkspaceTrustState !== WorkspaceTrustState.Trusted) { + createButton(localize('trustButton', "Trust"), 'workbench.trust.grant'); + } + + if (model.currentWorkspaceTrustState !== WorkspaceTrustState.Untrusted) { + createButton(localize('doNotTrustButton', "Don't Trust"), 'workbench.trust.deny'); + } + + createButton(localize('learnMore', "Learn More"), 'workbench.trust.learnMore'); + + this.workspaceTrustSettingsTreeModel.update(model.dataModel.getTrustStateInfo()); + + this.trustSettingsTree.setChildren(null, Iterable.map(this.workspaceTrustSettingsTreeModel.settings, s => { return { element: s }; })); + } + + private createHeaderElement(parent: HTMLElement): void { + this.headerContainer = append(parent, $('.workspace-trust-header')); + this.headerTitle = append(this.headerContainer, $('.workspace-trust-title')); + this.headerDescription = append(this.headerContainer, $('.workspace-trust-description')); + + const buttonsRow = append(this.headerContainer, $('.workspace-trust-buttons-row')); + this.headerButtons = append(buttonsRow, $('.workspace-trust-buttons')); + } + + private createConfigurationElement(parent: HTMLElement): void { + this.configurationContainer = append(parent, $('.workspace-trust-settings.settings-body')); + + const workspaceTrustTreeContainer = append(this.configurationContainer, $('.workspace-trust-settings-tree-container.settings-tree-container')); + const renderer = this.instantiationService.createInstance(WorkspaceTrustSettingArrayRenderer,); + + this.trustSettingsTree = this._register(this.instantiationService.createInstance(WorkspaceTrustTree, + workspaceTrustTreeContainer, + [renderer])); + + this.workspaceTrustSettingsTreeModel = this.instantiationService.createInstance(WorkspaceTrustTreeModel); + + this._register(renderer.onDidChangeSetting(e => this.onDidChangeSetting(e))); + } + + private onDidChangeSetting(change: IWorkspaceTrustSettingChangeEvent) { + if (this.workspaceTrustEditorModel) { + if (isArray(change.value)) { + console.log(change.key); + console.log(change.value); + if (change.key === 'trustedFolders') { + this.workspaceTrustEditorModel.dataModel.setTrustedFolders(change.value.map(item => URI.file(item))); + } + + if (change.key === 'untrustedFolders') { + this.workspaceTrustEditorModel.dataModel.setUntrustedFolders(change.value.map(item => URI.file(item))); + } + } + } + } + + layout(dimension: Dimension): void { + if (!this.isVisible()) { + return; + } + + const listHeight = dimension.height - this.configurationContainer.offsetTop; + this.configurationContainer.style.height = `${listHeight}`; + + this.trustSettingsTree.layout(listHeight, dimension.width); + } +} diff --git a/src/vs/workbench/contrib/workspace/browser/workspaceTrustTree.ts b/src/vs/workbench/contrib/workspace/browser/workspaceTrustTree.ts new file mode 100644 index 00000000000..1abfc3cb2d7 --- /dev/null +++ b/src/vs/workbench/contrib/workspace/browser/workspaceTrustTree.ts @@ -0,0 +1,471 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { addDisposableListener, append, EventType, $, createStyleSheet, trackFocus } from 'vs/base/browser/dom'; +import { DefaultStyleController, IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; +import { IList } from 'vs/base/browser/ui/tree/indexTreeModel'; +import { IObjectTreeOptions } from 'vs/base/browser/ui/tree/objectTree'; +import { ITreeModel, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; +import { Color, RGBA } from 'vs/base/common/color'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable, DisposableStore, dispose } from 'vs/base/common/lifecycle'; +import { isArray } from 'vs/base/common/types'; +import { localize } from 'vs/nls'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +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 { IListService, WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { editorBackground, errorForeground, focusBorder, foreground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground } from 'vs/platform/theme/common/colorRegistry'; +import { IColorTheme, ICssStyleCollector, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { NonCollapsibleObjectTreeModel } from 'vs/workbench/contrib/preferences/browser/settingsTree'; +import { focusedRowBackground, focusedRowBorder, IListDataItem, ISettingListChangeEvent, ListSettingWidget, rowHoverBackground, settingsHeaderForeground } from 'vs/workbench/contrib/preferences/browser/settingsWidgets'; +import { attachStyler } from 'vs/platform/theme/common/styler'; +import { CachedListVirtualDelegate } from 'vs/base/browser/ui/list/list'; +import { IWorkspaceTrustStateInfo, WorkspaceTrustState } from 'vs/platform/workspace/common/workspaceTrust'; + + +export class WorkspaceTrustSettingsTreeEntry { + id: string; + displayLabel: string; + setting: { + key: string; + description: string; + }; + value: string[]; + + constructor(key: string, displayLabel: string, description: string, value: string[]) { + this.setting = { key, description }; + this.displayLabel = displayLabel; + this.value = value; + this.id = key; + } +} + +export interface IWorkspaceTrustSettingItemTemplate { + onChange?: (value: T) => void; + + toDispose: DisposableStore; + context?: WorkspaceTrustSettingsTreeEntry; + containerElement: HTMLElement; + labelElement: HTMLElement; + descriptionElement: HTMLElement; + controlElement: HTMLElement; + elementDisposables: DisposableStore; +} + +interface IWorkspaceTrustSettingListItemTemplate extends IWorkspaceTrustSettingItemTemplate { + listWidget: ListSettingWidget; + validationErrorMessageElement: HTMLElement; +} + +export interface IWorkspaceTrustSettingChangeEvent { + key: string; + value: any; // undefined => reset/unconfigure +} + + +export class WorkspaceTrustSettingArrayRenderer extends Disposable implements ITreeRenderer { + templateId = 'template.setting.array'; + + static readonly CONTROL_CLASS = 'setting-control-focus-target'; + static readonly CONTROL_SELECTOR = '.' + WorkspaceTrustSettingArrayRenderer.CONTROL_CLASS; + static readonly CONTENTS_CLASS = 'setting-item-contents'; + static readonly CONTENTS_SELECTOR = '.' + WorkspaceTrustSettingArrayRenderer.CONTENTS_CLASS; + static readonly ALL_ROWS_SELECTOR = '.monaco-list-row'; + + static readonly SETTING_KEY_ATTR = 'data-key'; + static readonly SETTING_ID_ATTR = 'data-id'; + static readonly ELEMENT_FOCUSABLE_ATTR = 'data-focusable'; + + protected readonly _onDidChangeSetting = this._register(new Emitter()); + readonly onDidChangeSetting: Event = this._onDidChangeSetting.event; + + private readonly _onDidFocusSetting = this._register(new Emitter()); + readonly onDidFocusSetting: Event = this._onDidFocusSetting.event; + + private readonly _onDidChangeIgnoredSettings = this._register(new Emitter()); + readonly onDidChangeIgnoredSettings: Event = this._onDidChangeIgnoredSettings.event; + + constructor( + @IThemeService protected readonly _themeService: IThemeService, + @IContextViewService protected readonly _contextViewService: IContextViewService, + @IOpenerService protected readonly _openerService: IOpenerService, + @IInstantiationService protected readonly _instantiationService: IInstantiationService, + @ICommandService protected readonly _commandService: ICommandService, + @IContextMenuService protected readonly _contextMenuService: IContextMenuService, + @IKeybindingService protected readonly _keybindingService: IKeybindingService, + @IConfigurationService protected readonly _configService: IConfigurationService, + ) { + super(); + } + + renderCommonTemplate(tree: any, _container: HTMLElement, typeClass: string): IWorkspaceTrustSettingItemTemplate { + _container.classList.add('setting-item'); + _container.classList.add('setting-item-' + typeClass); + + const container = append(_container, $(WorkspaceTrustSettingArrayRenderer.CONTENTS_SELECTOR)); + container.classList.add('settings-row-inner-container'); + const titleElement = append(container, $('.setting-item-title')); + const labelCategoryContainer = append(titleElement, $('.setting-item-cat-label-container')); + const labelElement = append(labelCategoryContainer, $('span.setting-item-label')); + const descriptionElement = append(container, $('.setting-item-description')); + const modifiedIndicatorElement = append(container, $('.setting-item-modified-indicator')); + modifiedIndicatorElement.title = localize('modified', "Modified"); + + const valueElement = append(container, $('.setting-item-value')); + const controlElement = append(valueElement, $('div.setting-item-control')); + const toDispose = new DisposableStore(); + + const template: IWorkspaceTrustSettingItemTemplate = { + toDispose, + elementDisposables: new DisposableStore(), + containerElement: container, + labelElement, + descriptionElement, + controlElement + }; + + // Prevent clicks from being handled by list + toDispose.add(addDisposableListener(controlElement, EventType.MOUSE_DOWN, e => e.stopPropagation())); + + toDispose.add(addDisposableListener(titleElement, EventType.MOUSE_ENTER, e => container.classList.add('mouseover'))); + toDispose.add(addDisposableListener(titleElement, EventType.MOUSE_LEAVE, e => container.classList.remove('mouseover'))); + + return template; + } + + addSettingElementFocusHandler(template: IWorkspaceTrustSettingItemTemplate): void { + const focusTracker = trackFocus(template.containerElement); + template.toDispose.add(focusTracker); + focusTracker.onDidBlur(() => { + if (template.containerElement.classList.contains('focused')) { + template.containerElement.classList.remove('focused'); + } + }); + + focusTracker.onDidFocus(() => { + template.containerElement.classList.add('focused'); + + if (template.context) { + this._onDidFocusSetting.fire(template.context); + } + }); + } + + renderTemplate(container: HTMLElement): IWorkspaceTrustSettingListItemTemplate { + const common = this.renderCommonTemplate(null, container, 'list'); + const descriptionElement = common.containerElement.querySelector('.setting-item-description')!; + const validationErrorMessageElement = $('.setting-item-validation-message'); + descriptionElement.after(validationErrorMessageElement); + + const listWidget = this._instantiationService.createInstance(ListSettingWidget, common.controlElement); + listWidget.domNode.classList.add(WorkspaceTrustSettingArrayRenderer.CONTROL_CLASS); + common.toDispose.add(listWidget); + + const template: IWorkspaceTrustSettingListItemTemplate = { + ...common, + listWidget, + validationErrorMessageElement + }; + + this.addSettingElementFocusHandler(template); + + common.toDispose.add( + listWidget.onDidChangeList(e => { + const newList = this.computeNewList(template, e); + if (newList !== null && template.onChange) { + template.onChange(newList); + } + }) + ); + + return template; + } + + private computeNewList(template: IWorkspaceTrustSettingListItemTemplate, e: ISettingListChangeEvent): string[] | undefined | null { + if (template.context) { + let newValue: string[] = []; + if (isArray(template.context.value)) { + newValue = [...template.context.value]; + } + + if (e.targetIndex !== undefined) { + // Delete value + if (!e.item?.value && e.originalItem.value && e.targetIndex > -1) { + newValue.splice(e.targetIndex, 1); + } + // Update value + else if (e.item?.value && e.originalItem.value) { + if (e.targetIndex > -1) { + newValue[e.targetIndex] = e.item.value; + } + // For some reason, we are updating and cannot find original value + // Just append the value in this case + else { + newValue.push(e.item.value); + } + } + // Add value + else if (e.item?.value && !e.originalItem.value && e.targetIndex >= newValue.length) { + newValue.push(e.item.value); + } + } + + return newValue; + } + + return undefined; + } + + renderElement(node: ITreeNode, index: number, template: IWorkspaceTrustSettingListItemTemplate): void { + const element = node.element; + template.context = element; + + template.containerElement.setAttribute(WorkspaceTrustSettingArrayRenderer.SETTING_KEY_ATTR, element.setting.key); + template.containerElement.setAttribute(WorkspaceTrustSettingArrayRenderer.SETTING_ID_ATTR, element.id); + + template.labelElement.textContent = element.displayLabel; + + template.descriptionElement.innerText = element.setting.description; + + const onChange = (value: any) => this._onDidChangeSetting.fire({ key: element.setting.key, value }); + this.renderValue(element, template, onChange); + } + + protected renderValue(dataElement: WorkspaceTrustSettingsTreeEntry, template: IWorkspaceTrustSettingListItemTemplate, onChange: (value: string[] | undefined) => void): void { + const value = getListDisplayValue(dataElement); + template.listWidget.setValue(value); + template.context = dataElement; + + template.onChange = (v) => { + onChange(v); + renderArrayValidations(dataElement, template, v, false); + }; + + renderArrayValidations(dataElement, template, value.map(v => v.value), true); + } + + disposeTemplate(template: IWorkspaceTrustSettingItemTemplate): void { + dispose(template.toDispose); + } + + disposeElement(_element: ITreeNode, _index: number, template: IWorkspaceTrustSettingItemTemplate, _height: number | undefined): void { + if (template.elementDisposables) { + template.elementDisposables.clear(); + } + } +} + +export class WorkspaceTrustTree extends WorkbenchObjectTree { + constructor( + container: HTMLElement, + renderers: ITreeRenderer[], + @IContextKeyService contextKeyService: IContextKeyService, + @IListService listService: IListService, + @IThemeService themeService: IThemeService, + @IConfigurationService configurationService: IConfigurationService, + @IKeybindingService keybindingService: IKeybindingService, + @IAccessibilityService accessibilityService: IAccessibilityService, + @IInstantiationService instantiationService: IInstantiationService, + ) { + super('WorkspaceTrustTree', container, + new WorkspaceTrustTreeDelegate(), + renderers, + { + horizontalScrolling: false, + supportDynamicHeights: true, + identityProvider: { + getId(e) { + return e.id; + } + }, + accessibilityProvider: new WorkspaceTrustTreeAccessibilityProvider(), + styleController: id => new DefaultStyleController(createStyleSheet(container), id), + smoothScrolling: configurationService.getValue('workbench.list.smoothScrolling'), + multipleSelectionSupport: false, + }, + contextKeyService, + listService, + themeService, + configurationService, + keybindingService, + accessibilityService, + ); + + this.disposables.add(registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { + const foregroundColor = theme.getColor(foreground); + if (foregroundColor) { + // Links appear inside other elements in markdown. CSS opacity acts like a mask. So we have to dynamically compute the description color to avoid + // applying an opacity to the link color. + const fgWithOpacity = new Color(new RGBA(foregroundColor.rgba.r, foregroundColor.rgba.g, foregroundColor.rgba.b, 0.9)); + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .setting-item-contents .setting-item-description { color: ${fgWithOpacity}; }`); + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings .settings-toc-container .monaco-list-row:not(.selected) { color: ${fgWithOpacity}; }`); + + // Hack for subpixel antialiasing + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .setting-item-contents .setting-item-title .setting-item-overrides, + .workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .setting-item-contents .setting-item-title .setting-item-ignored { color: ${fgWithOpacity}; }`); + } + + const errorColor = theme.getColor(errorForeground); + if (errorColor) { + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .setting-item-contents .setting-item-deprecation-message { color: ${errorColor}; }`); + } + + const invalidInputBackground = theme.getColor(inputValidationErrorBackground); + if (invalidInputBackground) { + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .setting-item-contents .setting-item-validation-message { background-color: ${invalidInputBackground}; }`); + } + + const invalidInputForeground = theme.getColor(inputValidationErrorForeground); + if (invalidInputForeground) { + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .setting-item-contents .setting-item-validation-message { color: ${invalidInputForeground}; }`); + } + + const invalidInputBorder = theme.getColor(inputValidationErrorBorder); + if (invalidInputBorder) { + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .setting-item-contents .setting-item-validation-message { border-style:solid; border-width: 1px; border-color: ${invalidInputBorder}; }`); + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .setting-item.invalid-input .setting-item-control .monaco-inputbox.idle { outline-width: 0; border-style:solid; border-width: 1px; border-color: ${invalidInputBorder}; }`); + } + + const focusedRowBackgroundColor = theme.getColor(focusedRowBackground); + if (focusedRowBackgroundColor) { + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .monaco-list-row.focused .settings-row-inner-container { background-color: ${focusedRowBackgroundColor}; }`); + } + + const rowHoverBackgroundColor = theme.getColor(rowHoverBackground); + if (rowHoverBackgroundColor) { + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .monaco-list-row:not(.focused) .settings-row-inner-container:hover { background-color: ${rowHoverBackgroundColor}; }`); + } + + const focusedRowBorderColor = theme.getColor(focusedRowBorder); + if (focusedRowBorderColor) { + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .monaco-list:focus-within .monaco-list-row.focused .setting-item-contents::before, + .workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .monaco-list:focus-within .monaco-list-row.focused .setting-item-contents::after { border-top: 1px solid ${focusedRowBorderColor} }`); + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .monaco-list:focus-within .monaco-list-row.focused .settings-group-title-label::before, + .workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .monaco-list:focus-within .monaco-list-row.focused .settings-group-title-label::after { border-top: 1px solid ${focusedRowBorderColor} }`); + } + + const headerForegroundColor = theme.getColor(settingsHeaderForeground); + if (headerForegroundColor) { + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .settings-group-title-label { color: ${headerForegroundColor}; }`); + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .setting-item-label { color: ${headerForegroundColor}; }`); + } + + const focusBorderColor = theme.getColor(focusBorder); + if (focusBorderColor) { + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .setting-item-contents .setting-item-markdown a:focus { outline-color: ${focusBorderColor} }`); + } + })); + + this.getHTMLElement().classList.add('settings-editor-tree'); + + this.disposables.add(attachStyler(themeService, { + listBackground: editorBackground, + listActiveSelectionBackground: editorBackground, + listActiveSelectionForeground: foreground, + listFocusAndSelectionBackground: editorBackground, + listFocusAndSelectionForeground: foreground, + listFocusBackground: editorBackground, + listFocusForeground: foreground, + listHoverForeground: foreground, + listHoverBackground: editorBackground, + listHoverOutline: editorBackground, + listFocusOutline: editorBackground, + listInactiveSelectionBackground: editorBackground, + listInactiveSelectionForeground: foreground, + listInactiveFocusBackground: editorBackground, + listInactiveFocusOutline: editorBackground + }, colors => { + this.style(colors); + })); + + this.disposables.add(configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('workbench.list.smoothScrolling')) { + this.updateOptions({ + smoothScrolling: configurationService.getValue('workbench.list.smoothScrolling') + }); + } + })); + } + + protected createModel(user: string, view: IList>, options: IObjectTreeOptions): ITreeModel { + return new NonCollapsibleObjectTreeModel(user, view, options); + } +} + +export class WorkspaceTrustTreeModel { + + settings: WorkspaceTrustSettingsTreeEntry[] = []; + + update(trustInfo: IWorkspaceTrustStateInfo): void { + this.settings = []; + if (trustInfo.localFolders) { + const trustedFolders = trustInfo.localFolders.filter(folder => folder.trustState === WorkspaceTrustState.Trusted).map(folder => folder.uri); + const untrustedFolders = trustInfo.localFolders.filter(folder => folder.trustState === WorkspaceTrustState.Untrusted).map(folder => folder.uri); + + this.settings.push(new WorkspaceTrustSettingsTreeEntry( + 'trustedFolders', + localize('trustedFolders', "Trusted Folders"), + localize('trustedFoldersDescription', "All workspaces under the following folders will be trusted. In the event of a conflict with untrusted folders, the nearest parent will determine trust."), + trustedFolders)); + + this.settings.push(new WorkspaceTrustSettingsTreeEntry( + 'untrustedFolders', + localize('untrustedFolders', "Untrusted Folders"), + localize('untrustedFoldersDescription', "All workspaces under the following folders will not be trusted. In the event of a conflict with trusted folders, the nearest parent will determine trust."), + untrustedFolders)); + } + } +} + +class WorkspaceTrustTreeAccessibilityProvider implements IListAccessibilityProvider { + getAriaLabel(element: WorkspaceTrustSettingsTreeEntry) { + if (element instanceof WorkspaceTrustSettingsTreeEntry) { + return `element.displayLabel`; + } + + return null; + } + + getWidgetAriaLabel() { + return localize('settings', "Workspace Trust Setting"); + } +} + +class WorkspaceTrustTreeDelegate extends CachedListVirtualDelegate { + + getTemplateId(element: WorkspaceTrustSettingsTreeEntry): string { + return 'template.setting.array'; + } + + hasDynamicHeight(element: WorkspaceTrustSettingsTreeEntry): boolean { + return true; + } + + protected estimateHeight(element: WorkspaceTrustSettingsTreeEntry): number { + return 104; + } +} + +function getListDisplayValue(element: WorkspaceTrustSettingsTreeEntry): IListDataItem[] { + if (!element.value || !isArray(element.value)) { + return []; + } + + return element.value.map((key: string) => { + return { + value: key + }; + }); +} + +function renderArrayValidations(dataElement: WorkspaceTrustSettingsTreeEntry, template: IWorkspaceTrustSettingListItemTemplate, v: string[] | undefined, arg3: boolean) { +} + diff --git a/src/vs/workbench/contrib/workspace/common/workspaceTrustFileSystemProvider.ts b/src/vs/workbench/contrib/workspace/common/workspaceTrustFileSystemProvider.ts deleted file mode 100644 index 4c4c49f9480..00000000000 --- a/src/vs/workbench/contrib/workspace/common/workspaceTrustFileSystemProvider.ts +++ /dev/null @@ -1,86 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Event } from 'vs/base/common/event'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import { URI } from 'vs/base/common/uri'; -import { FileDeleteOptions, FileOverwriteOptions, FileSystemProviderCapabilities, FileType, FileWriteOptions, IFileService, IStat, IWatchOptions, IFileSystemProviderWithFileReadWriteCapability } from 'vs/platform/files/common/files'; -import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { VSBuffer } from 'vs/base/common/buffer'; -import { WORKSPACE_TRUST_STORAGE_KEY } from 'vs/workbench/services/workspaces/common/workspaceTrust'; - -const WORKSPACE_TRUST_SCHEMA = 'workspaceTrust'; - -const TRUSTED_WORKSPACES_STAT: IStat = { - type: FileType.File, - ctime: Date.now(), - mtime: Date.now(), - size: 0 -}; - -const PREPENDED_TEXT = `// The following file is a placeholder UX for managing trusted workspaces. It will be replaced by a rich editor and provide -// additonal information about what trust means. e.g. enabling trust will unblock automatic tasks on startup and list the tasks. It will enable certain extensions -// and list the extensions with associated functionality. -`; - -export class WorkspaceTrustFileSystemProvider implements IFileSystemProviderWithFileReadWriteCapability, IWorkbenchContribution { - readonly capabilities = FileSystemProviderCapabilities.FileReadWrite; - - readonly onDidChangeCapabilities = Event.None; - readonly onDidChangeFile = Event.None; - - constructor( - @IFileService private readonly fileService: IFileService, - @IStorageService private readonly storageService: IStorageService, - ) { - this.fileService.registerProvider(WORKSPACE_TRUST_SCHEMA, this); - } - - stat(resource: URI): Promise { - return Promise.resolve(TRUSTED_WORKSPACES_STAT); - } - - async readFile(resource: URI): Promise { - let workspacesTrustContent = this.storageService.get(WORKSPACE_TRUST_STORAGE_KEY, StorageScope.GLOBAL); - - let objectForm = {}; - try { - objectForm = JSON.parse(workspacesTrustContent || '{}'); - } catch { } - - const buffer = VSBuffer.fromString(PREPENDED_TEXT + JSON.stringify(objectForm, undefined, 2)).buffer; - return buffer; - } - - writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise { - try { - const workspacesTrustContent = VSBuffer.wrap(content).toString().replace(PREPENDED_TEXT, ''); - this.storageService.store(WORKSPACE_TRUST_STORAGE_KEY, workspacesTrustContent, StorageScope.GLOBAL, StorageTarget.MACHINE); - } catch (err) { } - - return Promise.resolve(); - } - - watch(resource: URI, opts: IWatchOptions): IDisposable { - return { - dispose() { - return; - } - }; - } - mkdir(resource: URI): Promise { - return Promise.resolve(undefined!); - } - readdir(resource: URI): Promise<[string, FileType][]> { - return Promise.resolve(undefined!); - } - delete(resource: URI, opts: FileDeleteOptions): Promise { - return Promise.resolve(undefined!); - } - rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise { - return Promise.resolve(undefined!); - } -} diff --git a/src/vs/workbench/electron-browser/desktop.main.ts b/src/vs/workbench/electron-browser/desktop.main.ts index a6b22ab4def..bcd53444318 100644 --- a/src/vs/workbench/electron-browser/desktop.main.ts +++ b/src/vs/workbench/electron-browser/desktop.main.ts @@ -18,11 +18,12 @@ import { NativeWorkbenchEnvironmentService } from 'vs/workbench/services/environ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { INativeWorkbenchConfiguration, INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { IWorkspaceInitializationPayload, reviveIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceInitializationPayload, reviveIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { ILoggerService, ILogService } from 'vs/platform/log/common/log'; import { NativeStorageService } from 'vs/platform/storage/node/storageService'; +import { NativeStorageService2 } from 'vs/platform/storage/electron-sandbox/storageService2'; import { Schemas } from 'vs/base/common/network'; -import { GlobalStorageDatabaseChannelClient } from 'vs/platform/storage/node/storageIpc'; +import { StorageDatabaseChannelClient } from 'vs/platform/storage/common/storageIpc'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -83,7 +84,10 @@ class DesktopMain extends Disposable { private reviveUris() { // Workspace - this.configuration.workspace = reviveIdentifier(this.configuration.workspace); + const workspace = reviveIdentifier(this.configuration.workspace); + if (isWorkspaceIdentifier(workspace) || isSingleFolderWorkspaceIdentifier(workspace)) { + this.configuration.workspace = workspace; + } // Files const filesToWait = this.configuration.filesToWait; @@ -130,14 +134,14 @@ class DesktopMain extends Disposable { services.logService.trace('workbench configuration', JSON.stringify(this.configuration)); } - private registerListeners(workbench: Workbench, storageService: NativeStorageService): void { + private registerListeners(workbench: Workbench, storageService: NativeStorageService | NativeStorageService2): void { // Workbench Lifecycle - this._register(workbench.onShutdown(() => this.dispose())); this._register(workbench.onWillShutdown(event => event.join(storageService.close(), 'join.closeStorage'))); + this._register(workbench.onShutdown(() => this.dispose())); } - private async initServices(): Promise<{ serviceCollection: ServiceCollection, logService: ILogService, storageService: NativeStorageService }> { + private async initServices(): Promise<{ serviceCollection: ServiceCollection, logService: ILogService, storageService: NativeStorageService | NativeStorageService2 }> { const serviceCollection = new ServiceCollection(); @@ -319,12 +323,17 @@ class DesktopMain extends Disposable { } } - private async createStorageService(payload: IWorkspaceInitializationPayload, logService: ILogService, mainProcessService: IMainProcessService): Promise { - const globalStorageDatabase = new GlobalStorageDatabaseChannelClient(mainProcessService.getChannel('storage')); - const storageService = new NativeStorageService(globalStorageDatabase, logService, this.environmentService); + private async createStorageService(payload: IWorkspaceInitializationPayload, logService: ILogService, mainProcessService: IMainProcessService): Promise { + let storageService: NativeStorageService | NativeStorageService2; + if (this.configuration.enableExperimentalMainProcessWorkspaceStorage) { + storageService = new NativeStorageService2(payload, mainProcessService, this.environmentService); + } else { + const storageDataBaseClient = new StorageDatabaseChannelClient(mainProcessService.getChannel('storage'), payload); + storageService = new NativeStorageService(storageDataBaseClient.globalStorage, payload, logService, this.environmentService); + } try { - await storageService.initialize(payload); + await storageService.initialize(); return storageService; } catch (error) { diff --git a/src/vs/workbench/electron-sandbox/desktop.contribution.ts b/src/vs/workbench/electron-sandbox/desktop.contribution.ts index 299561eb5e1..1b1971fd14e 100644 --- a/src/vs/workbench/electron-sandbox/desktop.contribution.ts +++ b/src/vs/workbench/electron-sandbox/desktop.contribution.ts @@ -273,6 +273,12 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; 'scope': ConfigurationScope.APPLICATION, 'description': localize('window.clickThroughInactive', "If enabled, clicking on an inactive window will both activate the window and trigger the element under the mouse if it is clickable. If disabled, clicking anywhere on an inactive window will activate it only and a second click is required on the element."), 'included': isMacintosh + }, + 'window.enableExperimentalMainProcessWorkspaceStorage': { + 'type': 'boolean', + 'default': false, + 'scope': ConfigurationScope.APPLICATION, + 'description': localize('window.localize', "Enables workspace storage access from the main process. Requires a restart to take effect."), } } }); diff --git a/src/vs/workbench/electron-sandbox/desktop.main.ts b/src/vs/workbench/electron-sandbox/desktop.main.ts index d72468c610b..709c3ab30c1 100644 --- a/src/vs/workbench/electron-sandbox/desktop.main.ts +++ b/src/vs/workbench/electron-sandbox/desktop.main.ts @@ -9,35 +9,42 @@ import { Workbench } from 'vs/workbench/browser/workbench'; import { NativeWindow } from 'vs/workbench/electron-sandbox/window'; import { setZoomLevel, setZoomFactor, setFullscreen } from 'vs/base/browser/browser'; import { domContentLoaded } from 'vs/base/browser/dom'; +import { onUnexpectedError } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { INativeWorkbenchConfiguration, INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { reviveIdentifier } from 'vs/platform/workspaces/common/workspaces'; -import { ILogService } from 'vs/platform/log/common/log'; +import { isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceInitializationPayload, reviveIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { ILoggerService, ILogService } from 'vs/platform/log/common/log'; +import { NativeStorageService2 } from 'vs/platform/storage/electron-sandbox/storageService2'; import { Schemas } from 'vs/base/common/network'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { Disposable } from 'vs/base/common/lifecycle'; import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services'; +import { RemoteAuthorityResolverService } from 'vs/platform/remote/electron-sandbox/remoteAuthorityResolverService'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { RemoteAgentService } from 'vs/workbench/services/remote/electron-sandbox/remoteAgentServiceImpl'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { FileService } from 'vs/platform/files/common/fileService'; import { IFileService } from 'vs/platform/files/common/files'; import { RemoteFileSystemProvider } from 'vs/workbench/services/remote/common/remoteAgentFileSystemChannel'; import { ISignService } from 'vs/platform/sign/common/sign'; import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; +import { basename } from 'vs/base/common/path'; import { IProductService } from 'vs/platform/product/common/productService'; import product from 'vs/platform/product/common/product'; +import { NativeLogService } from 'vs/workbench/services/log/electron-sandbox/logService'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { NativeHostService } from 'vs/platform/native/electron-sandbox/nativeHostService'; -import { SimpleConfigurationService, simpleFileSystemProvider, SimpleLogService, SimpleSignService, SimpleStorageService, SimpleNativeWorkbenchEnvironmentService, SimpleWorkspaceService } from 'vs/workbench/electron-sandbox/sandbox.simpleservices'; -import { INativeWorkbenchConfiguration, INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; -import { RemoteAuthorityResolverService } from 'vs/platform/remote/electron-sandbox/remoteAuthorityResolverService'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService'; +import { KeyboardLayoutService } from 'vs/workbench/services/keybinding/electron-sandbox/nativeKeyboardLayout'; +import { IKeyboardLayoutService } from 'vs/platform/keyboardLayout/common/keyboardLayout'; +import { LoggerService } from 'vs/workbench/services/log/electron-sandbox/loggerService'; import { ElectronIPCMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; +import { SimpleConfigurationService, simpleFileSystemProvider, SimpleSignService, SimpleNativeWorkbenchEnvironmentService, SimpleWorkspaceService } from 'vs/workbench/electron-sandbox/sandbox.simpleservices'; class DesktopMain extends Disposable { @@ -65,7 +72,10 @@ class DesktopMain extends Disposable { private reviveUris() { // Workspace - this.configuration.workspace = reviveIdentifier(this.configuration.workspace); + const workspace = reviveIdentifier(this.configuration.workspace); + if (isWorkspaceIdentifier(workspace) || isSingleFolderWorkspaceIdentifier(workspace)) { + this.configuration.workspace = workspace; + } // Files const filesToWait = this.configuration.filesToWait; @@ -107,14 +117,14 @@ class DesktopMain extends Disposable { services.logService.trace('workbench configuration', JSON.stringify(this.configuration)); } - private registerListeners(workbench: Workbench, storageService: SimpleStorageService): void { + private registerListeners(workbench: Workbench, storageService: NativeStorageService2): void { // Workbench Lifecycle - this._register(workbench.onShutdown(() => this.dispose())); this._register(workbench.onWillShutdown(event => event.join(storageService.close(), 'join.closeStorage'))); + this._register(workbench.onShutdown(() => this.dispose())); } - private async initServices(): Promise<{ serviceCollection: ServiceCollection, logService: ILogService, storageService: SimpleStorageService }> { + private async initServices(): Promise<{ serviceCollection: ServiceCollection, logService: ILogService, storageService: NativeStorageService2 }> { const serviceCollection = new ServiceCollection(); @@ -142,8 +152,12 @@ class DesktopMain extends Disposable { // Product serviceCollection.set(IProductService, this.productService); + // Logger + const loggerService = new LoggerService(mainProcessService); + serviceCollection.set(ILoggerService, loggerService); + // Log - const logService = new SimpleLogService(); + const logService = this._register(new NativeLogService(`renderer${this.configuration.windowId}`, loggerService, mainProcessService, this.environmentService)); serviceCollection.set(ILogService, logService); // Remote @@ -209,6 +223,8 @@ class DesktopMain extends Disposable { fileService.registerProvider(Schemas.vscodeRemote, remoteFileSystemProvider); } + const payload = this.resolveWorkspaceInitializationPayload(); + const services = await Promise.all([ this.createWorkspaceService().then(service => { @@ -221,11 +237,19 @@ class DesktopMain extends Disposable { return service; }), - this.createStorageService().then(service => { + this.createStorageService(payload, mainProcessService).then(service => { // Storage serviceCollection.set(IStorageService, service); + return service; + }), + + this.createKeyboardLayoutService(mainProcessService).then(service => { + + // KeyboardLayout + serviceCollection.set(IKeyboardLayoutService, service); + return service; }) ]); @@ -247,12 +271,56 @@ class DesktopMain extends Disposable { return { serviceCollection, logService, storageService: services[1] }; } + private resolveWorkspaceInitializationPayload(): IWorkspaceInitializationPayload { + let workspaceInitializationPayload: IWorkspaceInitializationPayload | undefined = this.configuration.workspace; + + // Fallback to empty workspace if we have no payload yet. + if (!workspaceInitializationPayload) { + let id: string; + if (this.configuration.backupPath) { + id = basename(this.configuration.backupPath); // we know the backupPath must be a unique path so we leverage its name as workspace ID + } else if (this.environmentService.isExtensionDevelopment) { + id = 'ext-dev'; // extension development window never stores backups and is a singleton + } else { + throw new Error('Unexpected window configuration without backupPath'); + } + + workspaceInitializationPayload = { id }; + } + + return workspaceInitializationPayload; + } + private async createWorkspaceService(): Promise { return new SimpleWorkspaceService(); } - private async createStorageService(): Promise { - return new SimpleStorageService(); + private async createStorageService(payload: IWorkspaceInitializationPayload, mainProcessService: IMainProcessService): Promise { + const storageService = new NativeStorageService2(payload, mainProcessService, this.environmentService); + + try { + await storageService.initialize(); + + return storageService; + } catch (error) { + onUnexpectedError(error); + + return storageService; + } + } + + private async createKeyboardLayoutService(mainProcessService: IMainProcessService): Promise { + const keyboardLayoutService = new KeyboardLayoutService(mainProcessService); + + try { + await keyboardLayoutService.initialize(); + + return keyboardLayoutService; + } catch (error) { + onUnexpectedError(error); + + return keyboardLayoutService; + } } } diff --git a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts index 4a2f05f35f9..7b7312d9575 100644 --- a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts +++ b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts @@ -6,7 +6,6 @@ /* eslint-disable code-no-standalone-editor */ /* eslint-disable code-import-patterns */ -import { ConsoleLogger, LogService } from 'vs/platform/log/common/log'; import { ISignService } from 'vs/platform/sign/common/sign'; import { URI } from 'vs/base/common/uri'; import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; @@ -15,7 +14,6 @@ import { IAddressProvider } from 'vs/platform/remote/common/remoteAgentConnectio import { ITelemetryData, ITelemetryInfo, ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IExtension } from 'vs/platform/extensions/common/extensions'; import { SimpleConfigurationService as BaseSimpleConfigurationService } from 'vs/editor/standalone/browser/simpleServices'; -import { InMemoryStorageService } from 'vs/platform/storage/common/storage'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backup/common/backup'; import { ITextSnapshot } from 'vs/editor/common/model'; @@ -177,13 +175,6 @@ export class SimpleWorkspaceService implements IWorkspaceContextService { //#endregion -//#region Configuration - -export class SimpleStorageService extends InMemoryStorageService { } - -//#endregion - - //#region Configuration export class SimpleConfigurationService extends BaseSimpleConfigurationService implements IWorkbenchConfigurationService { @@ -193,15 +184,7 @@ export class SimpleConfigurationService extends BaseSimpleConfigurationService i //#endregion -//#region Logger - -export class SimpleLogService extends LogService { - - constructor() { - super(new ConsoleLogger()); - } - -} +//#region Signing export class SimpleSignService implements ISignService { diff --git a/src/vs/workbench/services/authentication/browser/authenticationService.ts b/src/vs/workbench/services/authentication/browser/authenticationService.ts index 5d6de318988..6c1efc704be 100644 --- a/src/vs/workbench/services/authentication/browser/authenticationService.ts +++ b/src/vs/workbench/services/authentication/browser/authenticationService.ts @@ -124,8 +124,7 @@ export interface IAuthenticationService { declaredProviders: AuthenticationProviderInformation[]; readonly onDidChangeDeclaredProviders: Event; - getSessions(id: string, scopes: string[], activateImmediate?: boolean): Promise>; - getAllSessions(providerId: string, activateImmediate?: boolean): Promise>; + getSessions(id: string, scopes?: string[], activateImmediate?: boolean): Promise>; getLabel(providerId: string): string; supportsMultipleAccounts(providerId: string): boolean; createSession(providerId: string, scopes: string[], activateImmediate?: boolean): Promise; @@ -694,16 +693,7 @@ export class AuthenticationService extends Disposable implements IAuthentication return Promise.race([didRegister, didTimeout]); } - async getAllSessions(id: string, activateImmediate: boolean = false): Promise> { - try { - const authProvider = this._authenticationProviders.get(id) || await this.tryActivateProvider(id, activateImmediate); - return await authProvider.getAllSessions(); - } catch (_) { - throw new Error(`No authentication provider '${id}' is currently registered.`); - } - } - - async getSessions(id: string, scopes: string[], activateImmediate: boolean = false): Promise> { + async getSessions(id: string, scopes?: string[], activateImmediate: boolean = false): Promise> { try { const authProvider = this._authenticationProviders.get(id) || await this.tryActivateProvider(id, activateImmediate); return await authProvider.getSessions(scopes); diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 5577ffbb2b0..fa4552e36f9 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -17,7 +17,7 @@ import { Configuration } from 'vs/workbench/services/configuration/common/config import { FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId, IConfigurationCache, machineSettingsSchemaId, LOCAL_MACHINE_SCOPES, IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions, allSettings, windowSettings, resourceSettings, applicationSettings, machineSettings, machineOverridableSettings, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; -import { IWorkspaceIdentifier, isWorkspaceIdentifier, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, IWorkspaceInitializationPayload, IEmptyWorkspaceInitializationPayload, useSlashForPath, getStoredWorkspaceFolder, isSingleFolderWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, toWorkspaceFolders } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, isWorkspaceIdentifier, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, IWorkspaceInitializationPayload, IEmptyWorkspaceIdentifier, useSlashForPath, getStoredWorkspaceFolder, isSingleFolderWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, toWorkspaceFolders } from 'vs/platform/workspaces/common/workspaces'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ConfigurationEditingService, EditableConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditingService'; import { WorkspaceConfiguration, FolderConfiguration, RemoteUserConfiguration, UserConfiguration } from 'vs/workbench/services/configuration/browser/configuration'; @@ -418,8 +418,8 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat return Promise.resolve(workspace); } - private createEmptyWorkspace(emptyWorkspacePayload: IEmptyWorkspaceInitializationPayload): Promise { - const workspace = new Workspace(emptyWorkspacePayload.id, [], null, uri => this.uriIdentityService.extUri.ignorePathCasing(uri)); + private createEmptyWorkspace(emptyWorkspaceIdentifier: IEmptyWorkspaceIdentifier): Promise { + const workspace = new Workspace(emptyWorkspaceIdentifier.id, [], null, uri => this.uriIdentityService.extUri.ignorePathCasing(uri)); workspace.initialized = true; return Promise.resolve(workspace); } diff --git a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts index 2d39c5bcd34..507dd8595b9 100644 --- a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts @@ -326,7 +326,8 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe default: try { - return this.resolveFromMap(match, variable, commandValueMapping, undefined); + const key = argument ? `${variable}:${argument}` : variable; + return this.resolveFromMap(match, key, commandValueMapping, undefined); } catch (error) { return match; } diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index c8742070a12..94f39dc0dbf 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -21,7 +21,7 @@ import { ExtensionMessageCollector, ExtensionPoint, ExtensionsRegistry, IExtensi import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import { ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol'; import { ExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManager'; -import { ExtensionIdentifier, IExtensionDescription, ExtensionType, ITranslatedScannedExtension, IExtension, ExtensionKind } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription, ExtensionType, ITranslatedScannedExtension, IExtension, ExtensionKind, IExtensionContributions } from 'vs/platform/extensions/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -535,14 +535,14 @@ export abstract class AbstractExtensionService extends Disposable implements IEx }); } - public readExtensionPointContributions(extPoint: IExtensionPoint): Promise[]> { + public readExtensionPointContributions(extPoint: IExtensionPoint): Promise[]> { return this._installedExtensionsReady.wait().then(() => { const availableExtensions = this._registry.getAllExtensionDescriptions(); const result: ExtensionPointContribution[] = []; for (const desc of availableExtensions) { if (desc.contributes && hasOwnProperty.call(desc.contributes, extPoint.name)) { - result.push(new ExtensionPointContribution(desc, desc.contributes[extPoint.name as keyof typeof desc.contributes])); + result.push(new ExtensionPointContribution(desc, desc.contributes[extPoint.name as keyof typeof desc.contributes] as T)); } } @@ -691,13 +691,13 @@ export abstract class AbstractExtensionService extends Disposable implements IEx } } - private static _handleExtensionPoint(extensionPoint: ExtensionPoint, availableExtensions: IExtensionDescription[], messageHandler: (msg: IMessage) => void): void { + private static _handleExtensionPoint(extensionPoint: ExtensionPoint, availableExtensions: IExtensionDescription[], messageHandler: (msg: IMessage) => void): void { const users: IExtensionPointUser[] = []; for (const desc of availableExtensions) { if (desc.contributes && hasOwnProperty.call(desc.contributes, extensionPoint.name)) { users.push({ description: desc, - value: desc.contributes[extensionPoint.name as keyof typeof desc.contributes], + value: desc.contributes[extensionPoint.name as keyof typeof desc.contributes] as T, collector: new ExtensionMessageCollector(messageHandler, desc, extensionPoint.name) }); } diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index 33883bedb25..ed224cb6eca 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -8,7 +8,7 @@ import Severity from 'vs/base/common/severity'; import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription, IExtensionContributions } from 'vs/platform/extensions/common/extensions'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator'; @@ -224,7 +224,7 @@ export interface IExtensionService { /** * Read all contributions to an extension point. */ - readExtensionPointContributions(extPoint: IExtensionPoint): Promise[]>; + readExtensionPointContributions(extPoint: IExtensionPoint): Promise[]>; /** * Get information about extensions status. diff --git a/src/vs/workbench/services/gettingStarted/common/gettingStartedContent.ts b/src/vs/workbench/services/gettingStarted/common/gettingStartedContent.ts index f96301a0577..64e10c2eb92 100644 --- a/src/vs/workbench/services/gettingStarted/common/gettingStartedContent.ts +++ b/src/vs/workbench/services/gettingStarted/common/gettingStartedContent.ts @@ -40,6 +40,26 @@ type GettingStartedCategory = { type GettingStartedContent = GettingStartedCategory[]; export const content: GettingStartedContent = [ + // { + // id: 'topLevelCommandPalette', + // title: localize('gettingStarted.commandPalette.title', "Command Palette"), + // description: localize('gettingStarted.commandPalette.description', "The one keybinding to show you everything VS Code can do."), + // icon: Codicon.symbolColor, + // content: { + // type: 'command', + // command: 'workbench.action.showCommands', + // } + // }, + // { + // id: 'topLevelSeeExtensions', + // title: localize('gettingStarted.languageSupport.title', "Install Language Support"), + // description: localize('gettingStarted.languageSupport.description', "Want even more features? Install extensions to add support for languages like Python, C, or Java."), + // icon: Codicon.extensions, + // content: { + // type: 'command', + // command: 'workbench.extensions.action.showPopularExtensions', + // } + // }, { id: 'Codespaces', title: localize('gettingStarted.codespaces.title', "Primer on Codespaces"), diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts index c9fc28409c0..550b36e9726 100644 --- a/src/vs/workbench/services/label/common/labelService.ts +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -21,7 +21,6 @@ import { match } from 'vs/base/common/glob'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; -import { hasDriveLetter } from 'vs/base/common/extpath'; const resourceLabelFormattersExtPoint = ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'resourceLabelFormatters', @@ -74,6 +73,10 @@ const resourceLabelFormattersExtPoint = ExtensionsRegistry.registerExtensionPoin const sepRegexp = /\//g; const labelMatchingRegexp = /\$\{(scheme|authority|path|(query)\.(.+?))\}/g; +function hasDriveLetterIgnorePlatform(path: string): boolean { + return !!(path && path[2] === ':'); +} + class ResourceLabelFormattersHandler implements IWorkbenchContribution { private formattersDisposables = new Map(); @@ -283,7 +286,7 @@ export class LabelService extends Disposable implements ILabelService { }); // convert \c:\something => C:\something - if (formatting.normalizeDriveLetter && hasDriveLetter(label.substr(1))) { + if (formatting.normalizeDriveLetter && hasDriveLetterIgnorePlatform(label)) { label = label.charAt(1).toUpperCase() + label.substr(2); } diff --git a/src/vs/workbench/services/log/electron-sandbox/logService.ts b/src/vs/workbench/services/log/electron-sandbox/logService.ts index d0fba0e728c..e9f19330624 100644 --- a/src/vs/workbench/services/log/electron-sandbox/logService.ts +++ b/src/vs/workbench/services/log/electron-sandbox/logService.ts @@ -37,5 +37,4 @@ export class NativeLogService extends LogService { this._register(disposables); } - } diff --git a/src/vs/workbench/services/log/electron-sandbox/loggerService.ts b/src/vs/workbench/services/log/electron-sandbox/loggerService.ts index 50795cdf780..fbe8bc70033 100644 --- a/src/vs/workbench/services/log/electron-sandbox/loggerService.ts +++ b/src/vs/workbench/services/log/electron-sandbox/loggerService.ts @@ -40,7 +40,7 @@ class Logger extends AbstractMessageLogger { loggerOptions?: ILoggerOptions, ) { super(loggerOptions?.always); - (this.file ? this.channel.call('createLogger', [file, loggerOptions]) : this.channel.call('createConsoleMainLogger', [file, loggerOptions])) + (this.file ? this.channel.call('createLogger', [file, loggerOptions]) : this.channel.call('createConsoleLogger', [file, loggerOptions])) .then(() => { this._log(this.buffer); this.isLoggerCreated = true; diff --git a/src/vs/workbench/services/remote/common/remoteExplorerService.ts b/src/vs/workbench/services/remote/common/remoteExplorerService.ts index 1ced7e337bf..f3420ff3d97 100644 --- a/src/vs/workbench/services/remote/common/remoteExplorerService.ts +++ b/src/vs/workbench/services/remote/common/remoteExplorerService.ts @@ -23,6 +23,10 @@ export const IRemoteExplorerService = createDecorator('r export const REMOTE_EXPLORER_TYPE_KEY: string = 'remote.explorerType'; const TUNNELS_TO_RESTORE = 'remote.tunnels.toRestore'; export const TUNNEL_VIEW_ID = '~remote.forwardedPorts'; +export const PORT_AUTO_FORWARD_SETTING = 'remote.autoForwardPorts'; +export const PORT_AUTO_SOURCE_SETTING = 'remote.autoForwardPortsSource'; +export const PORT_AUTO_SOURCE_SETTING_PROCESS = 'process'; +export const PORT_AUTO_SOURCE_SETTING_OUTPUT = 'output'; export enum TunnelType { Candidate = 'Candidate', diff --git a/src/vs/workbench/services/search/electron-browser/searchService.ts b/src/vs/workbench/services/search/electron-browser/searchService.ts index 09160857d0f..9e1f8f3f913 100644 --- a/src/vs/workbench/services/search/electron-browser/searchService.ts +++ b/src/vs/workbench/services/search/electron-browser/searchService.ts @@ -61,10 +61,7 @@ export class DiskSearch implements ISearchResultProvider { serverName: 'Search', timeout, args: ['--type=searchService'], - // See https://github.com/microsoft/vscode/issues/27665 // Pass in fresh execArgv to the forked process such that it doesn't inherit them from `process.execArgv`. - // e.g. Launching the extension host process with `--inspect-brk=xxx` and then forking a process from the extension host - // results in the forked process inheriting `--inspect-brk=xxx`. freshExecArgv: true, env: { VSCODE_AMD_ENTRYPOINT: 'vs/workbench/services/search/node/searchApp', diff --git a/src/vs/workbench/services/timer/browser/timerService.ts b/src/vs/workbench/services/timer/browser/timerService.ts index f4bc25a6066..324de644b65 100644 --- a/src/vs/workbench/services/timer/browser/timerService.ts +++ b/src/vs/workbench/services/timer/browser/timerService.ts @@ -46,6 +46,7 @@ export interface IMemoryInfo { "timers.ellapsedExtensions" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "timers.ellapsedExtensionsReady" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "timers.ellapsedRequire" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedStorageInit" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "timers.ellapsedWorkspaceStorageInit" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "timers.ellapsedWorkspaceServiceInit" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "timers.ellapsedRequiredUserDataInit" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, @@ -215,6 +216,14 @@ export interface IStartupMetrics { */ readonly ellapsedWorkspaceStorageInit: number; + /** + * The time it took to init the storage database connection from the workbench. + * + * * Happens in the renderer-process + * * Measured with the `code/willInitStorage` and `code/didInitStorage` performance marks. + */ + readonly ellapsedStorageInit: number; + /** * The time it took to initialize the workspace and configuration service. * @@ -514,6 +523,7 @@ export abstract class AbstractTimerService implements ITimerService { ellapsedWindowLoadToRequire: this._marks.getDuration('code/willOpenNewWindow', 'code/willLoadWorkbenchMain'), ellapsedRequire: this._marks.getDuration('code/willLoadWorkbenchMain', 'code/didLoadWorkbenchMain'), ellapsedWaitForShellEnv: this._marks.getDuration('code/willWaitForShellEnv', 'code/didWaitForShellEnv'), + ellapsedStorageInit: this._marks.getDuration('code/willInitStorage', 'code/didInitStorage'), ellapsedWorkspaceStorageInit: this._marks.getDuration('code/willInitWorkspaceStorage', 'code/didInitWorkspaceStorage'), ellapsedWorkspaceServiceInit: this._marks.getDuration('code/willInitWorkspaceService', 'code/didInitWorkspaceService'), ellapsedRequiredUserDataInit: this._marks.getDuration('code/willInitRequiredUserData', 'code/didInitRequiredUserData'), diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts index cf80241f61a..48602a7f767 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts @@ -206,7 +206,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat let accounts: Map = new Map(); let currentAccount: UserDataSyncAccount | null = null; - const sessions = await this.authenticationService.getAllSessions(authenticationProviderId) || []; + const sessions = await this.authenticationService.getSessions(authenticationProviderId) || []; for (const session of sessions) { const account: UserDataSyncAccount = new UserDataSyncAccount(authenticationProviderId, session); accounts.set(account.accountName, account); diff --git a/src/vs/workbench/services/workspaces/browser/workspaceTrustEditorInput.ts b/src/vs/workbench/services/workspaces/browser/workspaceTrustEditorInput.ts new file mode 100644 index 00000000000..78181c577ee --- /dev/null +++ b/src/vs/workbench/services/workspaces/browser/workspaceTrustEditorInput.ts @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Schemas } from 'vs/base/common/network'; +import { URI } from 'vs/base/common/uri'; +import { localize } from 'vs/nls'; +import { IWorkspaceTrustService } from 'vs/platform/workspace/common/workspaceTrust'; +import { EditorInput } from 'vs/workbench/common/editor'; +import { WorkspaceTrustEditorModel, WorkspaceTrustService } from 'vs/workbench/services/workspaces/common/workspaceTrust'; + +export class WorkspaceTrustEditorInput extends EditorInput { + static readonly ID: string = 'workbench.input.workspaceTrust'; + + readonly resource: URI = URI.from({ + scheme: Schemas.vscodeWorkspaceTrust, + path: `workspaceTrustEditor` + }); + + constructor( + @IWorkspaceTrustService private readonly workspaceTrustService: WorkspaceTrustService + ) { + super(); + } + + getTypeId(): string { + return WorkspaceTrustEditorInput.ID; + } + + matches(otherInput: unknown): boolean { + return otherInput instanceof WorkspaceTrustEditorInput; + } + + getName(): string { + return localize('workspaceTrustEditorInputName', "Workspace Trust"); + } + + async resolve(): Promise { + return this.workspaceTrustService.workspaceTrustEditorModel; + } +} diff --git a/src/vs/workbench/services/workspaces/browser/workspacesService.ts b/src/vs/workbench/services/workspaces/browser/workspacesService.ts index 6d6b1d77002..28d39cec2f6 100644 --- a/src/vs/workbench/services/workspaces/browser/workspacesService.ts +++ b/src/vs/workbench/services/workspaces/browser/workspacesService.ts @@ -26,7 +26,7 @@ export class BrowserWorkspacesService extends Disposable implements IWorkspacesS declare readonly _serviceBrand: undefined; private readonly _onRecentlyOpenedChange = this._register(new Emitter()); - readonly onRecentlyOpenedChange = this._onRecentlyOpenedChange.event; + readonly onDidChangeRecentlyOpened = this._onRecentlyOpenedChange.event; constructor( @IStorageService private readonly storageService: IStorageService, diff --git a/src/vs/workbench/services/workspaces/common/workspaceTrust.ts b/src/vs/workbench/services/workspaces/common/workspaceTrust.ts index f8ea335eae8..f048e4d411d 100644 --- a/src/vs/workbench/services/workspaces/common/workspaceTrust.ts +++ b/src/vs/workbench/services/workspaces/common/workspaceTrust.ts @@ -13,16 +13,28 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag import { IWorkspace, IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceTrustModel, IWorkspaceTrustRequest, IWorkspaceTrustRequestModel, IWorkspaceTrustService, IWorkspaceTrustStateInfo, WorkspaceTrustState, WorkspaceTrustStateChangeEvent } from 'vs/platform/workspace/common/workspaceTrust'; import { isEqual, isEqualOrParent } from 'vs/base/common/extpath'; +import { EditorModel } from 'vs/workbench/common/editor'; export const WORKSPACE_TRUST_ENABLED = 'workspace.trustEnabled'; export const WORKSPACE_TRUST_STORAGE_KEY = 'content.trust.model.key'; -export const WORKSPACE_TRUST_URI = URI.parse('workspaceTrust:/Trusted Workspaces'); export const WorkspaceTrustContext = { PendingRequest: new RawContextKey('workspaceTrustPendingRequest', false), TrustState: new RawContextKey('workspaceTrustState', WorkspaceTrustState.Unknown) }; +export class WorkspaceTrustEditorModel extends EditorModel { + constructor( + readonly dataModel: IWorkspaceTrustModel, + private readonly workspaceTrustService: WorkspaceTrustService + ) { + super(); + } + + get currentWorkspaceTrustState(): WorkspaceTrustState { + return this.workspaceTrustService.getWorkspaceTrustState(); + } +} export class WorkspaceTrustModel extends Disposable implements IWorkspaceTrustModel { private storageKey = WORKSPACE_TRUST_STORAGE_KEY; @@ -82,6 +94,30 @@ export class WorkspaceTrustModel extends Disposable implements IWorkspaceTrustMo this._onDidChangeTrustState.fire(); } + setTrustedFolders(folders: URI[]): void { + this.trustStateInfo.localFolders = this.trustStateInfo.localFolders.filter(folder => folder.trustState !== WorkspaceTrustState.Trusted); + for (const folder of folders) { + this.trustStateInfo.localFolders.push({ + trustState: WorkspaceTrustState.Trusted, + uri: folder.fsPath + }); + } + + this.saveTrustInfo(); + } + + setUntrustedFolders(folders: URI[]): void { + this.trustStateInfo.localFolders = this.trustStateInfo.localFolders.filter(folder => folder.trustState !== WorkspaceTrustState.Untrusted); + for (const folder of folders) { + this.trustStateInfo.localFolders.push({ + trustState: WorkspaceTrustState.Untrusted, + uri: folder.fsPath + }); + } + + this.saveTrustInfo(); + } + setFolderTrustState(folder: URI, trustState: WorkspaceTrustState): void { let changed = false; @@ -135,6 +171,10 @@ export class WorkspaceTrustModel extends Disposable implements IWorkspaceTrustMo return result; } + + getTrustStateInfo(): IWorkspaceTrustStateInfo { + return this.trustStateInfo; + } } export class WorkspaceTrustRequestModel extends Disposable implements IWorkspaceTrustRequestModel { @@ -166,6 +206,7 @@ export class WorkspaceTrustService extends Disposable implements IWorkspaceTrust _serviceBrand: undefined; private readonly dataModel: IWorkspaceTrustModel; readonly requestModel: IWorkspaceTrustRequestModel; + private editorModel?: WorkspaceTrustEditorModel; private readonly _onDidChangeTrustState = this._register(new Emitter()); readonly onDidChangeTrustState = this._onDidChangeTrustState.event; @@ -212,6 +253,14 @@ export class WorkspaceTrustService extends Disposable implements IWorkspaceTrust this._onDidChangeTrustState.fire({ previousTrustState: previousState, currentTrustState: this._currentTrustState }); } + get workspaceTrustEditorModel(): WorkspaceTrustEditorModel { + if (this.editorModel === undefined) { + this.editorModel = this._register(new WorkspaceTrustEditorModel(this.dataModel, this)); + } + + return this.editorModel; + } + private calculateWorkspaceTrustState(): WorkspaceTrustState { if (!this.isWorkspaceTrustEnabled()) { return WorkspaceTrustState.Trusted; diff --git a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts index 7619a7b337b..a85f77ba805 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts @@ -21,6 +21,7 @@ import { nullExtensionDescription } from 'vs/workbench/services/extensions/commo import { isEqual } from 'vs/base/common/resources'; import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; import { generateUuid } from 'vs/base/common/uuid'; +import { Event } from 'vs/base/common/event'; suite('NotebookCell#Document', function () { @@ -33,9 +34,11 @@ suite('NotebookCell#Document', function () { const notebookUri = URI.parse('test:///notebook.file'); const disposables = new DisposableStore(); - setup(async function () { + teardown(function () { disposables.clear(); + }); + setup(async function () { rpcProtocol = new TestRPCProtocol(); rpcProtocol.set(MainContext.MainThreadCommands, new class extends mock() { $registerCommand() { } @@ -305,4 +308,49 @@ suite('NotebookCell#Document', function () { assert.strictEqual(notebook.notebookDocument.cells.length, 3); assert.strictEqual(second.index, 2); }); + + test('ERR MISSING extHostDocument for notebook cell: #116711', async function () { + + const p = Event.toPromise(extHostNotebooks.onDidChangeNotebookCells); + + // DON'T call this, make sure the cell-documents have not been created yet + // assert.strictEqual(notebook.notebookDocument.cells.length, 2); + + extHostNotebooks.$acceptModelChanged(notebook.uri, { + versionId: 100, + rawEvents: [{ + kind: NotebookCellsChangeType.ModelChange, + changes: [[0, 2, [{ + handle: 3, + uri: CellUri.generate(notebookUri, 3), + source: ['### Heading'], + eol: '\n', + language: 'markdown', + cellKind: CellKind.Markdown, + outputs: [], + }, { + handle: 4, + uri: CellUri.generate(notebookUri, 4), + source: ['console.log("aaa")', 'console.log("bbb")'], + eol: '\n', + language: 'javascript', + cellKind: CellKind.Code, + outputs: [], + }]]] + }] + }, false); + + assert.strictEqual(notebook.notebookDocument.cells.length, 2); + + const event = await p; + + assert.strictEqual(event.document === notebook.notebookDocument, true); + assert.strictEqual(event.changes.length, 1); + assert.strictEqual(event.changes[0].deletedCount, 2); + assert.strictEqual(event.changes[0].deletedItems[0].document.isClosed, true); + assert.strictEqual(event.changes[0].deletedItems[1].document.isClosed, true); + assert.strictEqual(event.changes[0].items.length, 2); + assert.strictEqual(event.changes[0].items[0].document.isClosed, false); + assert.strictEqual(event.changes[0].items[1].document.isClosed, false); + }); }); diff --git a/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts b/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts index 651add03080..134e347dd65 100644 --- a/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts +++ b/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts @@ -26,7 +26,7 @@ suite('ExtHostTreeView', function () { onRefresh = new Emitter<{ [treeItemHandle: string]: ITreeItem }>(); - $registerTreeViewDataProvider(treeViewId: string): void { + async $registerTreeViewDataProvider(treeViewId: string): Promise { } $refresh(viewId: string, itemsToRefresh: { [treeItemHandle: string]: ITreeItem }): Promise { @@ -604,7 +604,7 @@ suite('ExtHostTreeView', function () { const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription); return loadCompleteTree('treeDataProvider') .then(() => { - runWithEventMerging((resolve) => { + return runWithEventMerging((resolve) => { tree = { 'a': { 'aa': {}, @@ -633,9 +633,9 @@ suite('ExtHostTreeView', function () { .then(() => { assert.ok(revealTarget.calledOnce); assert.deepStrictEqual('treeDataProvider', revealTarget.args[0][0]); - assert.deepStrictEqual({ handle: '0/0:b/0:bc', label: { label: 'bc' }, collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:b' }, removeUnsetKeys(revealTarget.args[0][1])); - assert.deepStrictEqual([{ handle: '0/0:b', label: { label: 'b' }, collapsibleState: TreeItemCollapsibleState.Collapsed }], (>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg))); - assert.deepStrictEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][3]); + assert.deepStrictEqual({ handle: '0/0:b/0:bc', label: { label: 'bc' }, collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:b' }, removeUnsetKeys(revealTarget.args[0][1].item)); + assert.deepStrictEqual([{ handle: '0/0:b', label: { label: 'b' }, collapsibleState: TreeItemCollapsibleState.Collapsed }], (>revealTarget.args[0][1].parentChain).map(arg => removeUnsetKeys(arg))); + assert.deepStrictEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][2]); }); }); }); diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index e2d267e6704..aa394668a9b 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -1413,7 +1413,7 @@ export class TestTextFileEditorModelManager extends TextFileEditorModelManager { export class TestWorkspacesService implements IWorkspacesService { _serviceBrand: undefined; - onRecentlyOpenedChange = Event.None; + onDidChangeRecentlyOpened = Event.None; async createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise { throw new Error('Method not implemented.'); } async deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise { } diff --git a/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts b/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts index 1935dbb5adc..e5c69a38e4e 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts @@ -713,10 +713,10 @@ suite('ExtHostSearch', () => { match: null // Don't care about this right now } } : { - uri: r.uri.toString(), - text: r.text, - lineNumber: r.lineNumber - }); + uri: r.uri.toString(), + text: r.text, + lineNumber: r.lineNumber + }); return assert.deepEqual( makeComparable(actualTextSearchResults), diff --git a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts index f2c26043db4..6dbb50fa818 100644 --- a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts @@ -55,6 +55,7 @@ export const TestWorkbenchConfiguration: INativeWorkbenchConfiguration = { perfMarks: [], colorScheme: { dark: true, highContrast: false }, os: { release: release() }, + enableExperimentalMainProcessWorkspaceStorage: false, ...parseArgs(process.argv, OPTIONS) }; diff --git a/test/automation/src/code.ts b/test/automation/src/code.ts index 0f04f08fa26..719598b188e 100644 --- a/test/automation/src/code.ts +++ b/test/automation/src/code.ts @@ -138,6 +138,7 @@ export async function spawn(options: SpawnOptions): Promise { '--disable-telemetry', '--no-cached-data', '--disable-updates', + '--disable-keytar', '--disable-crash-reporter', `--extensions-dir=${options.extensionsPath}`, `--user-data-dir=${options.userDataDir}`, diff --git a/test/smoke/src/areas/notebook/notebook.test.ts b/test/smoke/src/areas/notebook/notebook.test.ts index 27f8269954b..1d70ee93a89 100644 --- a/test/smoke/src/areas/notebook/notebook.test.ts +++ b/test/smoke/src/areas/notebook/notebook.test.ts @@ -34,7 +34,7 @@ export function setup() { await app.workbench.notebook.stopEditingCell(); }); - it.skip('inserts/edits markdown cell', async function () { + it('inserts/edits markdown cell', async function () { const app = this.app as Application; await app.workbench.notebook.openNotebook(); await app.workbench.notebook.focusNextCell(); diff --git a/test/ui/splitview/package.json b/test/ui/splitview/package.json deleted file mode 100644 index d6ce0c37374..00000000000 --- a/test/ui/splitview/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "splitview", - "version": "1.0.0", - "main": "index.js", - "license": "MIT", - "devDependencies": { - "koa": "^2.5.1", - "koa-mount": "^3.0.0", - "koa-route": "^3.2.0", - "koa-static": "^5.0.0", - "mz": "^2.7.0" - } -} \ No newline at end of file diff --git a/test/ui/splitview/public/index.html b/test/ui/splitview/public/index.html deleted file mode 100644 index 0951af8ea52..00000000000 --- a/test/ui/splitview/public/index.html +++ /dev/null @@ -1,260 +0,0 @@ - - - - - Splitview - - - - -
-
- - - - - - diff --git a/test/ui/splitview/yarn.lock b/test/ui/splitview/yarn.lock deleted file mode 100644 index 237201a684e..00000000000 --- a/test/ui/splitview/yarn.lock +++ /dev/null @@ -1,341 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -accepts@^1.2.2: - version "1.3.5" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" - integrity sha1-63d99gEXI6OxTopywIBcjoZ0a9I= - dependencies: - mime-types "~2.1.18" - negotiator "0.6.1" - -any-promise@^1.0.0, any-promise@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" - integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= - -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= - -content-disposition@~0.5.0: - version "0.5.2" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" - integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= - -content-type@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== - -cookies@~0.7.0: - version "0.7.1" - resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.7.1.tgz#7c8a615f5481c61ab9f16c833731bcb8f663b99b" - integrity sha1-fIphX1SBxhq58WyDNzG8uPZjuZs= - dependencies: - depd "~1.1.1" - keygrip "~1.0.2" - -debug@*, debug@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== - dependencies: - ms "2.0.0" - -debug@^2.6.1: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -deep-equal@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" - integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= - -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= - -depd@^1.1.0, depd@~1.1.1, depd@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= - -destroy@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= - -error-inject@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/error-inject/-/error-inject-1.0.0.tgz#e2b3d91b54aed672f309d950d154850fa11d4f37" - integrity sha1-4rPZG1Su1nLzCdlQ0VSFD6EdTzc= - -escape-html@~1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= - -fresh@^0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= - -http-assert@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.3.0.tgz#a31a5cf88c873ecbb5796907d4d6f132e8c01e4a" - integrity sha1-oxpc+IyHPsu1eWkH1NbxMujAHko= - dependencies: - deep-equal "~1.0.1" - http-errors "~1.6.1" - -http-errors@^1.2.8, http-errors@^1.6.3, http-errors@~1.6.1, http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" - integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - -is-generator-function@^1.0.3: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522" - integrity sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw== - -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= - -keygrip@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.0.2.tgz#ad3297c557069dea8bcfe7a4fa491b75c5ddeb91" - integrity sha1-rTKXxVcGneqLz+ek+kkbdcXd65E= - -koa-compose@^3.0.0, koa-compose@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-3.2.1.tgz#a85ccb40b7d986d8e5a345b3a1ace8eabcf54de7" - integrity sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec= - dependencies: - any-promise "^1.1.0" - -koa-compose@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-4.1.0.tgz#507306b9371901db41121c812e923d0d67d3e877" - integrity sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw== - -koa-convert@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-1.2.0.tgz#da40875df49de0539098d1700b50820cebcd21d0" - integrity sha1-2kCHXfSd4FOQmNFwC1CCDOvNIdA= - dependencies: - co "^4.6.0" - koa-compose "^3.0.0" - -koa-is-json@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/koa-is-json/-/koa-is-json-1.0.0.tgz#273c07edcdcb8df6a2c1ab7d59ee76491451ec14" - integrity sha1-JzwH7c3Ljfaiwat9We52SRRR7BQ= - -koa-mount@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/koa-mount/-/koa-mount-3.0.0.tgz#08cab3b83d31442ed8b7e75c54b1abeb922ec197" - integrity sha1-CMqzuD0xRC7Yt+dcVLGr65IuwZc= - dependencies: - debug "^2.6.1" - koa-compose "^3.2.1" - -koa-route@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/koa-route/-/koa-route-3.2.0.tgz#76298b99a6bcfa9e38cab6fe5c79a8733e758bce" - integrity sha1-dimLmaa8+p44yrb+XHmocz51i84= - dependencies: - debug "*" - methods "~1.1.0" - path-to-regexp "^1.2.0" - -koa-send@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/koa-send/-/koa-send-5.0.0.tgz#5e8441e07ef55737734d7ced25b842e50646e7eb" - integrity sha512-90ZotV7t0p3uN9sRwW2D484rAaKIsD8tAVtypw/aBU+ryfV+fR2xrcAwhI8Wl6WRkojLUs/cB9SBSCuIb+IanQ== - dependencies: - debug "^3.1.0" - http-errors "^1.6.3" - mz "^2.7.0" - resolve-path "^1.4.0" - -koa-static@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/koa-static/-/koa-static-5.0.0.tgz#5e92fc96b537ad5219f425319c95b64772776943" - integrity sha512-UqyYyH5YEXaJrf9S8E23GoJFQZXkBVJ9zYYMPGz919MSX1KuvAcycIuS0ci150HCoPf4XQVhQ84Qf8xRPWxFaQ== - dependencies: - debug "^3.1.0" - koa-send "^5.0.0" - -koa@^2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/koa/-/koa-2.5.1.tgz#79f8b95f8d72d04fe9a58a8da5ebd6d341103f9c" - integrity sha512-cchwbMeG2dv3E2xTAmheDAuvR53tPgJZN/Hf1h7bTzJLSPcFZp8/t5+bNKJ6GaQZoydhZQ+1GNruhKdj3lIrug== - dependencies: - accepts "^1.2.2" - content-disposition "~0.5.0" - content-type "^1.0.0" - cookies "~0.7.0" - debug "*" - delegates "^1.0.0" - depd "^1.1.0" - destroy "^1.0.3" - error-inject "~1.0.0" - escape-html "~1.0.1" - fresh "^0.5.2" - http-assert "^1.1.0" - http-errors "^1.2.8" - is-generator-function "^1.0.3" - koa-compose "^4.0.0" - koa-convert "^1.2.0" - koa-is-json "^1.0.0" - mime-types "^2.0.7" - on-finished "^2.1.0" - only "0.0.2" - parseurl "^1.3.0" - statuses "^1.2.0" - type-is "^1.5.5" - vary "^1.0.0" - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= - -methods@~1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= - -mime-db@~1.33.0: - version "1.33.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" - integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== - -mime-types@^2.0.7, mime-types@~2.1.18: - version "2.1.18" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" - integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== - dependencies: - mime-db "~1.33.0" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -mz@^2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" - integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== - dependencies: - any-promise "^1.0.0" - object-assign "^4.0.1" - thenify-all "^1.0.0" - -negotiator@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" - integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk= - -object-assign@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -on-finished@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= - dependencies: - ee-first "1.1.1" - -only@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4" - integrity sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q= - -parseurl@^1.3.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" - integrity sha1-/CidTtiZMRlGDBViUyYs3I3mW/M= - -path-is-absolute@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-to-regexp@^1.2.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d" - integrity sha1-Wf3g9DW62suhA6hOnTvGTpa5k30= - dependencies: - isarray "0.0.1" - -resolve-path@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/resolve-path/-/resolve-path-1.4.0.tgz#c4bda9f5efb2fce65247873ab36bb4d834fe16f7" - integrity sha1-xL2p9e+y/OZSR4c6s2u02DT+Fvc= - dependencies: - http-errors "~1.6.2" - path-is-absolute "1.0.1" - -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" - integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== - -"statuses@>= 1.4.0 < 2", statuses@^1.2.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= - -thenify-all@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" - integrity sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY= - dependencies: - thenify ">= 3.1.0 < 4" - -"thenify@>= 3.1.0 < 4": - version "3.3.0" - resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.0.tgz#e69e38a1babe969b0108207978b9f62b88604839" - integrity sha1-5p44obq+lpsBCCB5eLn2K4hgSDk= - dependencies: - any-promise "^1.0.0" - -type-is@^1.5.5: - version "1.6.16" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" - integrity sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.18" - -vary@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= diff --git a/test/ui/tree/package.json b/test/ui/tree/package.json deleted file mode 100644 index 1a3b32d627d..00000000000 --- a/test/ui/tree/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "tree", - "version": "1.0.0", - "main": "index.js", - "license": "MIT", - "devDependencies": { - "koa": "^2.5.1", - "koa-mount": "^3.0.0", - "koa-route": "^3.2.0", - "koa-static": "^5.0.0", - "mz": "^2.7.0" - } -} \ No newline at end of file diff --git a/test/ui/tree/public/compressed.json b/test/ui/tree/public/compressed.json deleted file mode 100644 index c0b5d4d7161..00000000000 --- a/test/ui/tree/public/compressed.json +++ /dev/null @@ -1,15620 +0,0 @@ -[ - { - "element": { - "name": "eclipse.platform.debug" - }, - "children": [ - { - "element": { - "name": ".git" - }, - "children": [ - { - "element": { - "name": "HEAD" - }, - "incompressible": true - }, - { - "element": { - "name": "branches" - }, - "children": [] - }, - { - "element": { - "name": "config" - }, - "incompressible": true - }, - { - "element": { - "name": "description" - }, - "incompressible": true - }, - { - "element": { - "name": "hooks" - }, - "children": [ - { - "element": { - "name": "applypatch-msg.sample" - }, - "incompressible": true - }, - { - "element": { - "name": "commit-msg.sample" - }, - "incompressible": true - }, - { - "element": { - "name": "fsmonitor-watchman.sample" - }, - "incompressible": true - }, - { - "element": { - "name": "post-update.sample" - }, - "incompressible": true - }, - { - "element": { - "name": "pre-applypatch.sample" - }, - "incompressible": true - }, - { - "element": { - "name": "pre-commit.sample" - }, - "incompressible": true - }, - { - "element": { - "name": "pre-push.sample" - }, - "incompressible": true - }, - { - "element": { - "name": "pre-rebase.sample" - }, - "incompressible": true - }, - { - "element": { - "name": "pre-receive.sample" - }, - "incompressible": true - }, - { - "element": { - "name": "prepare-commit-msg.sample" - }, - "incompressible": true - }, - { - "element": { - "name": "update.sample" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "index" - }, - "incompressible": true - }, - { - "element": { - "name": "info" - }, - "children": [ - { - "element": { - "name": "exclude" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "logs" - }, - "children": [ - { - "element": { - "name": "HEAD" - }, - "incompressible": true - }, - { - "element": { - "name": "refs" - }, - "children": [ - { - "element": { - "name": "heads" - }, - "children": [ - { - "element": { - "name": "master" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "remotes" - }, - "children": [ - { - "element": { - "name": "origin" - }, - "children": [ - { - "element": { - "name": "HEAD" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "objects" - }, - "children": [ - { - "element": { - "name": "info" - }, - "children": [] - }, - { - "element": { - "name": "pack" - }, - "children": [ - { - "element": { - "name": "pack-2b1503bd0b85396d8596e9e99d956bfc3fbe497b.idx" - }, - "incompressible": true - }, - { - "element": { - "name": "pack-2b1503bd0b85396d8596e9e99d956bfc3fbe497b.pack" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "packed-refs" - }, - "incompressible": true - }, - { - "element": { - "name": "refs" - }, - "children": [ - { - "element": { - "name": "heads" - }, - "children": [ - { - "element": { - "name": "master" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "remotes" - }, - "children": [ - { - "element": { - "name": "origin" - }, - "children": [ - { - "element": { - "name": "HEAD" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "tags" - }, - "children": [ - { - "element": { - "name": "I20190722-1800" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "shallow" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": ".gitignore" - }, - "incompressible": true - }, - { - "element": { - "name": "CONTRIBUTING" - }, - "incompressible": true - }, - { - "element": { - "name": "LICENSE" - }, - "incompressible": true - }, - { - "element": { - "name": "NOTICE" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.externaltools" - }, - "children": [ - { - "element": { - "name": ".classpath" - }, - "incompressible": true - }, - { - "element": { - "name": ".project" - }, - "incompressible": true - }, - { - "element": { - "name": ".settings" - }, - "children": [ - { - "element": { - "name": "org.eclipse.core.resources.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.runtime.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.core.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.ui.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.api.tools.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.prefs" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "META-INF" - }, - "children": [ - { - "element": { - "name": "MANIFEST.MF" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "about.html" - }, - "incompressible": true - }, - { - "element": { - "name": "build.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "plugin.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "plugin.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "pom.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "src" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "core" - }, - "children": [ - { - "element": { - "name": "externaltools" - }, - "children": [ - { - "element": { - "name": "internal" - }, - "children": [ - { - "element": { - "name": "ExternalToolsCore.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IExternalToolConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "launchConfigurations" - }, - "children": [ - { - "element": { - "name": "BackgroundResourceRefresher.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsCoreUtil.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsProgramMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsProgramMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "ProgramLaunchDelegate.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "model" - }, - "children": [ - { - "element": { - "name": "BuilderCoreUtils.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolBuilder.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsModelMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsModelMessages.properties" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "registry" - }, - "children": [ - { - "element": { - "name": "ExternalToolMigration.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsMigrationMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsMigrationMessages.properties" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "org.eclipse.core.variables" - }, - "children": [ - { - "element": { - "name": ".classpath" - }, - "incompressible": true - }, - { - "element": { - "name": ".project" - }, - "incompressible": true - }, - { - "element": { - "name": ".settings" - }, - "children": [ - { - "element": { - "name": "org.eclipse.core.resources.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.runtime.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.core.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.ui.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.api.tools.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.prefs" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "META-INF" - }, - "children": [ - { - "element": { - "name": "MANIFEST.MF" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "about.html" - }, - "incompressible": true - }, - { - "element": { - "name": "build.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "plugin.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "plugin.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "pom.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "schema" - }, - "children": [ - { - "element": { - "name": "dynamicVariables.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "valueVariables.exsd" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "src" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "core" - }, - "children": [ - { - "element": { - "name": "internal" - }, - "children": [ - { - "element": { - "name": "variables" - }, - "children": [ - { - "element": { - "name": "ContributedValueVariable.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DynamicVariable.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EclipseHomeVariableResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StringSubstitutionEngine.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StringVariable.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StringVariableManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ValueVariable.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariablesMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariablesMessages.properties" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "variables" - }, - "children": [ - { - "element": { - "name": "IDynamicVariable.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDynamicVariableResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStringVariable.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStringVariableManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IValueVariable.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IValueVariableInitializer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IValueVariableListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariablesPlugin.java" - }, - "incompressible": true - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "org.eclipse.debug.core" - }, - "children": [ - { - "element": { - "name": ".classpath" - }, - "incompressible": true - }, - { - "element": { - "name": ".options" - }, - "incompressible": true - }, - { - "element": { - "name": ".project" - }, - "incompressible": true - }, - { - "element": { - "name": ".settings" - }, - "children": [ - { - "element": { - "name": "org.eclipse.core.resources.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.runtime.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.core.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.ui.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.api.tools.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.prefs" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "META-INF" - }, - "children": [ - { - "element": { - "name": "MANIFEST.MF" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "about.html" - }, - "incompressible": true - }, - { - "element": { - "name": "build.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "core" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "debug" - }, - "children": [ - { - "element": { - "name": "core" - }, - "children": [ - { - "element": { - "name": "DebugEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugException.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugPlugin.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IBreakpointListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IBreakpointManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IBreakpointManagerListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IBreakpointsListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugEventFilter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugEventSetListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IExpressionListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IExpressionManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IExpressionsListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunch.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchConfiguration.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchConfigurationListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchConfigurationMigrationDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchConfigurationType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchConfigurationWorkingCopy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchMode.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchesListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchesListener2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILogicalStructureProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILogicalStructureType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryBlockListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryBlockManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IProcessFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IPrototypeAttributesLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IRequest.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStatusHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStreamListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Launch.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RefreshUtil.java" - }, - "incompressible": true - }, - { - "element": { - "name": "commands" - }, - "children": [ - { - "element": { - "name": "AbstractDebugCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugCommandHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugCommandRequest.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDisconnectHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDropToFrameHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IEnabledStateRequest.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IRestartHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IResumeHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStepFiltersHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStepIntoHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStepOverHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStepReturnHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISuspendHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ITerminateHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "model" - }, - "children": [ - { - "element": { - "name": "Breakpoint.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugElement.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IBreakpoint.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IBreakpointImportParticipant.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugElement.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugModelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugTarget.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDisconnect.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDropToFrame.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IErrorReportingExpression.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IExpression.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IFilteredStep.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IFlushableStreamMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IIndexedValue.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchConfigurationDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchConfigurationDelegate2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILineBreakpoint.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILogicalStructureTypeDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILogicalStructureTypeDelegate2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryBlock.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryBlockExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryBlockRetrieval.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryBlockRetrievalExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IPersistableSourceLocator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IProcess.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IRegister.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IRegisterGroup.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISourceLocator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStackFrame.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStep.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStepFilter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStepFilters.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStreamMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStreamsProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStreamsProxy2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISuspendResume.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ITerminate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IThread.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ITriggerPoint.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IValue.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IValueModification.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IVariable.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IWatchExpression.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IWatchExpressionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IWatchExpressionListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IWatchExpressionResult.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IWatchpoint.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LineBreakpoint.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryByte.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RuntimeProcess.java" - }, - "incompressible": true - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - }, - { - "element": { - "name": "sourcelookup" - }, - "children": [ - { - "element": { - "name": "AbstractSourceLookupDirector.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractSourceLookupParticipant.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IPersistableSourceLocator2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISourceContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISourceContainerType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISourceContainerTypeDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISourceLookupDirector.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISourceLookupParticipant.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISourcePathComputer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISourcePathComputerDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "containers" - }, - "children": [ - { - "element": { - "name": "AbstractSourceContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractSourceContainerTypeDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ArchiveSourceContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CompositeSourceContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ContainerSourceContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultSourceContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DirectorySourceContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalArchiveSourceContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FolderSourceContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LocalFileStorage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProjectSourceContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkspaceSourceContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ZipEntryStorage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "internal" - }, - "children": [ - { - "element": { - "name": "core" - }, - "children": [ - { - "element": { - "name": "BreakpointImportParticipantDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugCoreMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugCoreMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugOptions.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugPreferenceInitializer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EnvironmentVariableResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExpressionManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConfigurationElementConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IExpressionsListener2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IInternalDebugCoreConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMementoConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "InputStreamMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfiguration.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationComparator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationInfo.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationWorkingCopy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchMode.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchablePropertyTester.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LogicalStructureManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LogicalStructureProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LogicalStructureType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryBlockManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "NullStreamsProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "OutputStreamMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Preferences.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PreferredDelegateModifyListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RefreshScopeComparator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ResourceFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepFilter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepFilterManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StreamsProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SystemPropertyResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SystemVariableResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WatchExpression.java" - }, - "incompressible": true - }, - { - "element": { - "name": "XMLMemento.java" - }, - "incompressible": true - }, - { - "element": { - "name": "commands" - }, - "children": [ - { - "element": { - "name": "CommandAdapterFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugCommandRequest.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DisconnectCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DropToFrameCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ForEachCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Request.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ResumeCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepFiltersCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepIntoCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepOverCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepReturnCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SuspendCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TerminateCommand.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "groups" - }, - "children": [ - { - "element": { - "name": "GroupLaunch.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GroupLaunchConfigurationDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GroupLaunchElement.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GroupMemberChangeListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "observer" - }, - "children": [ - { - "element": { - "name": "ProcessObserver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StreamObserver.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "sourcelookup" - }, - "children": [ - { - "element": { - "name": "SourceContainerType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLocatorMementoComparator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLookupMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLookupMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLookupUtils.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourcePathComputer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "containers" - }, - "children": [ - { - "element": { - "name": "ArchiveSourceContainerType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultSourceContainerType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DirectorySourceContainerType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalArchiveSourceContainerType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FolderSourceContainerType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProjectSourceContainerType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkspaceSourceContainerType.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "variables" - }, - "children": [ - { - "element": { - "name": "ContainerResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DateTimeResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Messages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Messages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "ProjectResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ResourceResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkspaceResolver.java" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "plugin.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "plugin.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "pom.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "schema" - }, - "children": [ - { - "element": { - "name": "breakpointImportParticipants.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "breakpoints.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "launchConfigurationComparators.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "launchConfigurationTypes.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "launchDelegates.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "launchModes.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "launchers.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "logicalStructureProviders.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "logicalStructureTypes.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "processFactories.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "sourceContainerTypes.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "sourceLocators.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "sourcePathComputers.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "statusHandlers.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "stepFilters.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "watchExpressionDelegates.exsd" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "scripts" - }, - "children": [ - { - "element": { - "name": "exportplugin.xml" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "org.eclipse.debug.examples.core" - }, - "children": [ - { - "element": { - "name": ".classpath" - }, - "incompressible": true - }, - { - "element": { - "name": ".project" - }, - "incompressible": true - }, - { - "element": { - "name": ".settings" - }, - "children": [ - { - "element": { - "name": "org.eclipse.core.resources.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.runtime.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.core.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.ui.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.api.tools.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.prefs" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "META-INF" - }, - "children": [ - { - "element": { - "name": "MANIFEST.MF" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "OSGI-INF" - }, - "children": [ - { - "element": { - "name": "l10n" - }, - "children": [ - { - "element": { - "name": "bundle.properties" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "about.html" - }, - "incompressible": true - }, - { - "element": { - "name": "build.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "forceQualifierUpdate.txt" - }, - "incompressible": true - }, - { - "element": { - "name": "pdavm" - }, - "children": [ - { - "element": { - "name": "src" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "debug" - }, - "children": [ - { - "element": { - "name": "examples" - }, - "children": [ - { - "element": { - "name": "pdavm" - }, - "children": [ - { - "element": { - "name": "PDAVirtualMachine.java" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "tests" - }, - "children": [ - { - "element": { - "name": "vmtest10.pda" - }, - "incompressible": true - }, - { - "element": { - "name": "vmtest2.pda" - }, - "incompressible": true - }, - { - "element": { - "name": "vmtest3.pda" - }, - "incompressible": true - }, - { - "element": { - "name": "vmtest6.pda" - }, - "incompressible": true - }, - { - "element": { - "name": "vmtest8.pda" - }, - "incompressible": true - }, - { - "element": { - "name": "vmtest9.pda" - }, - "incompressible": true - }, - { - "element": { - "name": "vmtest_children.pda" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "plugin.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "pom.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "readme.html" - }, - "incompressible": true - }, - { - "element": { - "name": "samples" - }, - "children": [ - { - "element": { - "name": "counter.pda" - }, - "incompressible": true - }, - { - "element": { - "name": "drop.pda" - }, - "incompressible": true - }, - { - "element": { - "name": "example.pda" - }, - "incompressible": true - }, - { - "element": { - "name": "fibonacci.pda" - }, - "incompressible": true - }, - { - "element": { - "name": "registers.pda" - }, - "incompressible": true - }, - { - "element": { - "name": "stack.pda" - }, - "incompressible": true - }, - { - "element": { - "name": "structures.pda" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "scripts" - }, - "children": [ - { - "element": { - "name": "build.xml" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "src" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "debug" - }, - "children": [ - { - "element": { - "name": "examples" - }, - "children": [ - { - "element": { - "name": "core" - }, - "children": [ - { - "element": { - "name": "midi" - }, - "children": [ - { - "element": { - "name": "launcher" - }, - "children": [ - { - "element": { - "name": "ClockControl.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LengthControl.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MidiLaunch.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MidiLaunchDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SequencerControl.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TempoControl.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TimeControl.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "pda" - }, - "children": [ - { - "element": { - "name": "DebugCorePlugin.java" - }, - "incompressible": true - }, - { - "element": { - "name": "breakpoints" - }, - "children": [ - { - "element": { - "name": "PDALineBreakpoint.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDARunToLineBreakpoint.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAWatchpoint.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "launcher" - }, - "children": [ - { - "element": { - "name": "PDALaunchDelegate.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "model" - }, - "children": [ - { - "element": { - "name": "IPDAEventListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAArray.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAArrayEntry.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDADebugElement.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDADebugTarget.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAMemoryBlock.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAStackFrame.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAStackValue.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAThread.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAValue.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAVariable.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WordStructureDelegate.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "protocol" - }, - "children": [ - { - "element": { - "name": "PDABitFieldData.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAChildrenCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAClearBreakpointCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDACommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDACommandResult.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDADataCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDADropFrameCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAEvalCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAEvalResultEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAEventStopCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAExitedEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAFrameCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAFrameCommandResult.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAFrameData.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAGroupsCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAListResult.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDANoSuchLabelEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAPopDataCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAPushDataCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDARegisterData.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDARegistersCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDARegistersCommandResult.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDARegistersEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDARestartCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAResumeCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAResumedEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDARunControlEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDASetBreakpointCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDASetDataCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDASetVarCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAStackCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAStackCommandResult.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAStackDepthCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAStackDepthCommandResult.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAStartedEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAStepCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAStepReturnCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDASuspendCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDASuspendedEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDATerminateCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDATerminatedEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAUnimplementedInstructionEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAVMResumeCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAVMResumedEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAVMStartedEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAVMSuspendCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAVMSuspendedEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAVMTerminatedEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAVarCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAWatchCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "sourcelookup" - }, - "children": [ - { - "element": { - "name": "PDASourceLookupDirector.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDASourceLookupParticipant.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDASourcePathComputerDelegate.java" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "src_ant" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "debug" - }, - "children": [ - { - "element": { - "name": "examples" - }, - "children": [ - { - "element": { - "name": "ant" - }, - "children": [ - { - "element": { - "name": "tasks" - }, - "children": [ - { - "element": { - "name": "PreProcessor.java" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "org.eclipse.debug.examples.memory" - }, - "children": [ - { - "element": { - "name": ".classpath" - }, - "incompressible": true - }, - { - "element": { - "name": ".project" - }, - "incompressible": true - }, - { - "element": { - "name": ".settings" - }, - "children": [ - { - "element": { - "name": "org.eclipse.core.resources.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.runtime.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.core.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.ui.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.api.tools.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.prefs" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "META-INF" - }, - "children": [ - { - "element": { - "name": "MANIFEST.MF" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "build.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "icons" - }, - "children": [ - { - "element": { - "name": "full" - }, - "children": [ - { - "element": { - "name": "obj16" - }, - "children": [ - { - "element": { - "name": "hex_tree.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "launch.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "memory_segment.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "memory_unit.gif" - }, - "incompressible": true - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "plugin.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "plugin.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "pom.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "src" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "debug" - }, - "children": [ - { - "element": { - "name": "examples" - }, - "children": [ - { - "element": { - "name": "internal" - }, - "children": [ - { - "element": { - "name": "memory" - }, - "children": [ - { - "element": { - "name": "MemoryViewSamplePlugin.java" - }, - "incompressible": true - }, - { - "element": { - "name": "core" - }, - "children": [ - { - "element": { - "name": "Messages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SampleDebugTarget.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SampleMemoryBlock.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SampleRegister.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SampleRegisterGroup.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SampleStackFrame.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SampleThread.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SampleValue.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SampleVariable.java" - }, - "incompressible": true - }, - { - "element": { - "name": "messages.properties" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "engine" - }, - "children": [ - { - "element": { - "name": "SampleEngine.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SampleMemoryUnit.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "launchconfig" - }, - "children": [ - { - "element": { - "name": "SampleLaunchConfigurationDelegateEx.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SampleLaunchTabGroup.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SampleModelPresentation.java" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "org.eclipse.debug.examples.mixedmode" - }, - "children": [ - { - "element": { - "name": ".classpath" - }, - "incompressible": true - }, - { - "element": { - "name": ".project" - }, - "incompressible": true - }, - { - "element": { - "name": ".settings" - }, - "children": [ - { - "element": { - "name": "org.eclipse.core.resources.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.runtime.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.core.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.ui.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.api.tools.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.prefs" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "META-INF" - }, - "children": [ - { - "element": { - "name": "MANIFEST.MF" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "OSGI-INF" - }, - "children": [ - { - "element": { - "name": "l10n" - }, - "children": [ - { - "element": { - "name": "bundle.properties" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "about.html" - }, - "incompressible": true - }, - { - "element": { - "name": "build.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "plugin.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "pom.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "src" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "debug" - }, - "children": [ - { - "element": { - "name": "internal" - }, - "children": [ - { - "element": { - "name": "examples" - }, - "children": [ - { - "element": { - "name": "mixedmode" - }, - "children": [ - { - "element": { - "name": "Activator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AntExtraTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ClearPreferredDelegatesHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DoNothingLaunchConfigurationDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DoNothingMainTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Messages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "messages.properties" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "org.eclipse.debug.examples.ui" - }, - "children": [ - { - "element": { - "name": ".classpath" - }, - "incompressible": true - }, - { - "element": { - "name": ".project" - }, - "incompressible": true - }, - { - "element": { - "name": ".settings" - }, - "children": [ - { - "element": { - "name": "org.eclipse.core.resources.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.runtime.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.core.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.ui.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.api.tools.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.prefs" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "META-INF" - }, - "children": [ - { - "element": { - "name": "MANIFEST.MF" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "OSGI-INF" - }, - "children": [ - { - "element": { - "name": "l10n" - }, - "children": [ - { - "element": { - "name": "bundle.properties" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "about.html" - }, - "incompressible": true - }, - { - "element": { - "name": "build.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "icons" - }, - "children": [ - { - "element": { - "name": "full" - }, - "children": [ - { - "element": { - "name": "dlcl16" - }, - "children": [ - { - "element": { - "name": "pop.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "push.gif" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "elcl16" - }, - "children": [ - { - "element": { - "name": "pop.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "push.gif" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "obj16" - }, - "children": [ - { - "element": { - "name": "clef.png" - }, - "incompressible": true - }, - { - "element": { - "name": "note.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "pda.gif" - }, - "incompressible": true - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "plugin.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "pom.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "src" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "debug" - }, - "children": [ - { - "element": { - "name": "examples" - }, - "children": [ - { - "element": { - "name": "ui" - }, - "children": [ - { - "element": { - "name": "midi" - }, - "children": [ - { - "element": { - "name": "adapters" - }, - "children": [ - { - "element": { - "name": "CheckboxModelProxyFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ControlCellModifier.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ControlEditor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ControlEventHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ControlLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ControlsMementoProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MidiAdapterFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MidiEventLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MidiEventModelProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MidiStepOverHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SequencerColumnFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SequencerColumnPresentation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SequencerContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SequencerControlsModelProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SequencerModelProxyFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TrackColumnFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TrackColumnPresentation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TrackContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TrackLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TrackModelProxy.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "detailpanes" - }, - "children": [ - { - "element": { - "name": "ClockSliderDetailPane.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ControlDetailPaneFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TempoSliderDetailPane.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "launcher" - }, - "children": [ - { - "element": { - "name": "ExampleLaunchStatusHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MidiLaunchShortcut.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MidiMainTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MidiTabGroup.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "pda" - }, - "children": [ - { - "element": { - "name": "DebugUIPlugin.java" - }, - "incompressible": true - }, - { - "element": { - "name": "adapters" - }, - "children": [ - { - "element": { - "name": "AdapterFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AddPDAMemoryBlockAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CommandAdapterFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ModelProxyFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDADebugTargetContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDADebugTargetProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDARestartDebugCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAThreadEventHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAViewActionProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAVirtualFindAction.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "breakpoints" - }, - "children": [ - { - "element": { - "name": "PDABreakpointAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAEditorAdapterFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDARunToLineAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAToggleWatchpointsTarget.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAToggleWatchpointsTargetFactory.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "editor" - }, - "children": [ - { - "element": { - "name": "AnnotationHover.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAContentAssistProcessor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAContentAssistant.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAEditor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAEditorMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAScanner.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDASourceViewerConfiguration.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PopFrameActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TextHover.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WordFinder.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "launcher" - }, - "children": [ - { - "element": { - "name": "PDALaunchShortcut.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAMainTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDATabGroup.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "presentation" - }, - "children": [ - { - "element": { - "name": "PDAModelPresentation.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "views" - }, - "children": [ - { - "element": { - "name": "AbstractDataStackViewHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CanPushTester.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CheckboxView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DataStackView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PopHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PushHandler.java" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "org.eclipse.debug.tests" - }, - "children": [ - { - "element": { - "name": ".classpath" - }, - "incompressible": true - }, - { - "element": { - "name": ".project" - }, - "incompressible": true - }, - { - "element": { - "name": ".settings" - }, - "children": [ - { - "element": { - "name": "org.eclipse.core.resources.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.runtime.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.core.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.ui.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.prefs" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "META-INF" - }, - "children": [ - { - "element": { - "name": "MANIFEST.MF" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "Platform Debug Test Suite.launch" - }, - "incompressible": true - }, - { - "element": { - "name": "about.html" - }, - "incompressible": true - }, - { - "element": { - "name": "build.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "forceQualifierUpdate.txt" - }, - "incompressible": true - }, - { - "element": { - "name": "icons" - }, - "children": [ - { - "element": { - "name": "image1.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "image2.gif" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "plugin.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "plugin.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "pom.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "src" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "debug" - }, - "children": [ - { - "element": { - "name": "tests" - }, - "children": [ - { - "element": { - "name": "AbstractDebugTest.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AutomatedSuite.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LocalSuite.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PerformanceSuite.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TestUtil.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TestsPlugin.java" - }, - "incompressible": true - }, - { - "element": { - "name": "breakpoint" - }, - "children": [ - { - "element": { - "name": "BreakpointOrderingTests.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "console" - }, - "children": [ - { - "element": { - "name": "ConsoleDocumentAdapterTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleManagerTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IOConsoleTestUtil.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IOConsoleTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MockProcess.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProcessConsoleManagerTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProcessConsoleTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StreamsProxyTests.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "expressions" - }, - "children": [ - { - "element": { - "name": "ExpressionManagerTests.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "launching" - }, - "children": [ - { - "element": { - "name": "AbstractLaunchTest.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AcceleratorSubstitutionTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ArgumentParsingTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ArgumentsPrinter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CancellingLaunchDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugFileStore.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugFileSystem.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchFavoriteTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchGroupTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchHistoryTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchManagerTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RefreshTabTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TestLaunchDelegate.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "sourcelookup" - }, - "children": [ - { - "element": { - "name": "SourceLookupFacilityTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TestLaunch.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TestSourceDirector.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TestSourceLocator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TestStackFrame.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "statushandlers" - }, - "children": [ - { - "element": { - "name": "StatusHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StatusHandlerTests.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "stepfilters" - }, - "children": [ - { - "element": { - "name": "StepFiltersTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TestStepFilter.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "view" - }, - "children": [ - { - "element": { - "name": "memory" - }, - "children": [ - { - "element": { - "name": "DynamicRenderingBindings.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryBlock.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryBlockDynamic.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryBlockOne.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryBlockThree.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryBlockTwo.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryRenderingTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RenderingTypeDelegate.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "viewer" - }, - "children": [ - { - "element": { - "name": "model" - }, - "children": [ - { - "element": { - "name": "AbstractViewerModelTest.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CheckTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ChildrenUpdateTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ColumnPresentationTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ContentTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DeltaTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FilterTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FilterTransformTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ITestModelUpdatesListenerConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "JFaceViewerCheckTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "JFaceViewerContentTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "JFaceViewerDeltaTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "JFaceViewerFilterTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "JFaceViewerLazyTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "JFaceViewerPerformanceTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "JFaceViewerPopupTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "JFaceViewerSelectionTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "JFaceViewerStateTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "JFaceViewerTopIndexTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "JFaceViewerUpdateTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LazyTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PerformanceTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PopupTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PresentationContextTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectionTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StateTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TestModel.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TestModelUpdatesListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TreeModelViewerAutopopulateAgent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TreePathWrapper.java" - }, - "incompressible": true - }, - { - "element": { - "name": "UpdateTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualViewerContentTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualViewerDeltaTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualViewerFilterTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualViewerLazyModeTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualViewerPerformanceTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualViewerPopupTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualViewerSelectionTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualViewerStateTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualViewerUpdateTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VisibleVirtualItemValidator.java" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "test-import" - }, - "children": [ - { - "element": { - "name": "Import1.launch" - }, - "incompressible": true - }, - { - "element": { - "name": "Import2.launch" - }, - "incompressible": true - }, - { - "element": { - "name": "Import3.launch" - }, - "incompressible": true - }, - { - "element": { - "name": "Import4.launch" - }, - "incompressible": true - }, - { - "element": { - "name": "Import5.launch" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "test.xml" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "org.eclipse.debug.ui" - }, - "children": [ - { - "element": { - "name": ".classpath" - }, - "incompressible": true - }, - { - "element": { - "name": ".options" - }, - "incompressible": true - }, - { - "element": { - "name": ".project" - }, - "incompressible": true - }, - { - "element": { - "name": ".settings" - }, - "children": [ - { - "element": { - "name": ".api_filters" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.resources.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.runtime.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.core.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.ui.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.api.tools.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.prefs" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "META-INF" - }, - "children": [ - { - "element": { - "name": "MANIFEST.MF" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "about.html" - }, - "incompressible": true - }, - { - "element": { - "name": "build.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "css" - }, - "children": [ - { - "element": { - "name": "e4-dark_debug_prefstyle.css" - }, - "incompressible": true - }, - { - "element": { - "name": "e4-light_debug_prefstyle.css" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "forceQualifierUpdate.txt" - }, - "incompressible": true - }, - { - "element": { - "name": "icons" - }, - "children": [ - { - "element": { - "name": "full" - }, - "children": [ - { - "element": { - "name": "dlcl16" - }, - "children": [ - { - "element": { - "name": "changevariablevalue_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "changevariablevalue_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "clear_co.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "collapseall.png" - }, - "incompressible": true - }, - { - "element": { - "name": "collapseall@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "copy_edit_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "copy_edit_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "copyviewtoclipboard_tsk.png" - }, - "incompressible": true - }, - { - "element": { - "name": "copyviewtoclipboard_tsk@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view_auto.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view_auto@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view_compact.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view_compact@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view_tree.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view_tree@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debuglast_co.gif.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debuglast_co.gif@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debuglast_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debuglast_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "delete_config.png" - }, - "incompressible": true - }, - { - "element": { - "name": "delete_config@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_auto.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_auto@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_hide.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_hide@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_right.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_right@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_under.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_under@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "disabled_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "disabled_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "disconnect_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "disconnect_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "display_selected_mb.png" - }, - "incompressible": true - }, - { - "element": { - "name": "display_selected_mb@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "dissolve_group.png" - }, - "incompressible": true - }, - { - "element": { - "name": "dissolve_group@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "drop_to_frame.png" - }, - "incompressible": true - }, - { - "element": { - "name": "drop_to_frame@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "edtsrclkup_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "edtsrclkup_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "enabled_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "enabled_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "expandall.png" - }, - "incompressible": true - }, - { - "element": { - "name": "expandall@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "export_brkpts.png" - }, - "incompressible": true - }, - { - "element": { - "name": "export_brkpts@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "export_config.png" - }, - "incompressible": true - }, - { - "element": { - "name": "export_config@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "filter_ps.png" - }, - "incompressible": true - }, - { - "element": { - "name": "filter_ps@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "hierarchicalLayout.png" - }, - "incompressible": true - }, - { - "element": { - "name": "hierarchicalLayout@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "import_brkpts.png" - }, - "incompressible": true - }, - { - "element": { - "name": "import_brkpts@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "link_proto.png" - }, - "incompressible": true - }, - { - "element": { - "name": "link_proto@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lock_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lock_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "memoryreset_tsk.png" - }, - "incompressible": true - }, - { - "element": { - "name": "memoryreset_tsk@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "metharg_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "metharg_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "monitorexpression_tsk.png" - }, - "incompressible": true - }, - { - "element": { - "name": "monitorexpression_tsk@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "new_con.png" - }, - "incompressible": true - }, - { - "element": { - "name": "new_con@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "new_proto.png" - }, - "incompressible": true - }, - { - "element": { - "name": "new_proto@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "next_thread_nav.png" - }, - "incompressible": true - }, - { - "element": { - "name": "next_thread_nav@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "pin.png" - }, - "incompressible": true - }, - { - "element": { - "name": "pin@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "prev_thread_nav.png" - }, - "incompressible": true - }, - { - "element": { - "name": "prev_thread_nav@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "printview_tsk.png" - }, - "incompressible": true - }, - { - "element": { - "name": "printview_tsk@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "prop_ps.png" - }, - "incompressible": true - }, - { - "element": { - "name": "prop_ps@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_all_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_all_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_all_triggers.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "removememory_tsk.png" - }, - "incompressible": true - }, - { - "element": { - "name": "removememory_tsk@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "reset_proto.png" - }, - "incompressible": true - }, - { - "element": { - "name": "reset_proto@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "restart_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "restart_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "resume_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "resume_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "runlast_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "runlast_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "runtoline_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "runtoline_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "skip_brkp.png" - }, - "incompressible": true - }, - { - "element": { - "name": "skip_brkp@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepbystep_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepbystep_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepinto_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepinto_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepover_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepover_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepreturn_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepreturn_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "suspend_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "suspend_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "synced.png" - }, - "incompressible": true - }, - { - "element": { - "name": "synced@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminate_all_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminate_all_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminate_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminate_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminate_rem_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminate_rem_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "tnames_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "tnames_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "toggledetailpane_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "toggledetailpane_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "unlink_proto.png" - }, - "incompressible": true - }, - { - "element": { - "name": "unlink_proto@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "var_cntnt_prvdr.png" - }, - "incompressible": true - }, - { - "element": { - "name": "var_cntnt_prvdr@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "writeerr_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "writeerr_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "writeout_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "writeout_co@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "dtool16" - }, - "children": [ - { - "element": { - "name": "debug_exc.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_exc@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "environment_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "environment_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "profile_exc.png" - }, - "incompressible": true - }, - { - "element": { - "name": "profile_exc@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "run_exc.png" - }, - "incompressible": true - }, - { - "element": { - "name": "run_exc@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "term_restart.png" - }, - "incompressible": true - }, - { - "element": { - "name": "term_restart@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "watch_exp.png" - }, - "incompressible": true - }, - { - "element": { - "name": "watch_exp@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "dview16" - }, - "children": [ - { - "element": { - "name": "breakpoint_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "breakpoint_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_persp.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_persp@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "details_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "details_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "memory_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "memory_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "module_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "module_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "register_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "register_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "variable_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "variable_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "watchlist_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "watchlist_view@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "elcl16" - }, - "children": [ - { - "element": { - "name": "changevariablevalue_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "changevariablevalue_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "collapseall.png" - }, - "incompressible": true - }, - { - "element": { - "name": "collapseall@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "copy_edit_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "copy_edit_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "copyviewtoclipboard_tsk.png" - }, - "incompressible": true - }, - { - "element": { - "name": "copyviewtoclipboard_tsk@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view_auto.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view_auto@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view_compact.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view_compact@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view_tree.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view_tree@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debuglast_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debuglast_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "delete_config.png" - }, - "incompressible": true - }, - { - "element": { - "name": "delete_config@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_auto.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_auto@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_hide.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_hide@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_right.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_right@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_under.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_under@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "disabled_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "disabled_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "disconnect_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "disconnect_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "display_selected_mb.png" - }, - "incompressible": true - }, - { - "element": { - "name": "display_selected_mb@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "dissolve_group.png" - }, - "incompressible": true - }, - { - "element": { - "name": "dissolve_group@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "drop_to_frame.png" - }, - "incompressible": true - }, - { - "element": { - "name": "drop_to_frame@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "edtsrclkup_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "edtsrclkup_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "enabled_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "enabled_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "expandall.png" - }, - "incompressible": true - }, - { - "element": { - "name": "expandall@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "export_brkpts.png" - }, - "incompressible": true - }, - { - "element": { - "name": "export_brkpts@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "export_config.png" - }, - "incompressible": true - }, - { - "element": { - "name": "export_config@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "filter_ps.png" - }, - "incompressible": true - }, - { - "element": { - "name": "filter_ps@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "hierarchicalLayout.png" - }, - "incompressible": true - }, - { - "element": { - "name": "hierarchicalLayout@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "import_brkpts.png" - }, - "incompressible": true - }, - { - "element": { - "name": "import_brkpts@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "link_proto.png" - }, - "incompressible": true - }, - { - "element": { - "name": "link_proto@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lock_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lock_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "memoryreset_tsk.png" - }, - "incompressible": true - }, - { - "element": { - "name": "memoryreset_tsk@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "metharg_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "metharg_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "monitorexpression_tsk.png" - }, - "incompressible": true - }, - { - "element": { - "name": "monitorexpression_tsk@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "new_con.png" - }, - "incompressible": true - }, - { - "element": { - "name": "new_con@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "new_proto.png" - }, - "incompressible": true - }, - { - "element": { - "name": "new_proto@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "next_thread_nav.png" - }, - "incompressible": true - }, - { - "element": { - "name": "next_thread_nav@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "pin.png" - }, - "incompressible": true - }, - { - "element": { - "name": "pin@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "prev_thread_nav.png" - }, - "incompressible": true - }, - { - "element": { - "name": "prev_thread_nav@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "printview_tsk.png" - }, - "incompressible": true - }, - { - "element": { - "name": "printview_tsk@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "prop_ps.png" - }, - "incompressible": true - }, - { - "element": { - "name": "prop_ps@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_all_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_all_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_all_triggers.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "removememory_tsk.png" - }, - "incompressible": true - }, - { - "element": { - "name": "removememory_tsk@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "reset_proto.png" - }, - "incompressible": true - }, - { - "element": { - "name": "reset_proto@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "restart_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "restart_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "resume_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "resume_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "runlast_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "runlast_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "runtoline_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "runtoline_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "skip_brkp.png" - }, - "incompressible": true - }, - { - "element": { - "name": "skip_brkp@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepbystep_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepbystep_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepinto_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepinto_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepover_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepover_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepreturn_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepreturn_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "suspend_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "suspend_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "synced.png" - }, - "incompressible": true - }, - { - "element": { - "name": "synced@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminate_all_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminate_all_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminate_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminate_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminate_rem_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminate_rem_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "tnames_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "tnames_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "toggledetailpane_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "toggledetailpane_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "unlink_proto.png" - }, - "incompressible": true - }, - { - "element": { - "name": "unlink_proto@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "var_cntnt_prvdr.png" - }, - "incompressible": true - }, - { - "element": { - "name": "var_cntnt_prvdr@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "writeerr_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "writeerr_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "writeout_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "writeout_co@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "etool16" - }, - "children": [ - { - "element": { - "name": "debug_exc.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_exc@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "environment_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "environment_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "profile_exc.png" - }, - "incompressible": true - }, - { - "element": { - "name": "profile_exc@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "run_exc.png" - }, - "incompressible": true - }, - { - "element": { - "name": "run_exc@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "term_restart.png" - }, - "incompressible": true - }, - { - "element": { - "name": "term_restart@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "watch_exp.png" - }, - "incompressible": true - }, - { - "element": { - "name": "watch_exp@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "eview16" - }, - "children": [ - { - "element": { - "name": "breakpoint_view.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "breakpoint_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "breakpoint_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_persp.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_persp.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_persp@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "details_view.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "details_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "details_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "memory_view.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "memory_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "memory_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "module_view.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "module_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "module_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "register_view.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "register_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "register_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "variable_view.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "variable_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "variable_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "watchlist_view.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "watchlist_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "watchlist_view@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "obj16" - }, - "children": [ - { - "element": { - "name": "arraypartition_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "arraypartition_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "brkp_grp.png" - }, - "incompressible": true - }, - { - "element": { - "name": "brkp_grp@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "brkp_grp_disabled.png" - }, - "incompressible": true - }, - { - "element": { - "name": "brkp_grp_disabled@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "brkp_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "brkp_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "brkp_type.png" - }, - "incompressible": true - }, - { - "element": { - "name": "brkp_type@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "brkpd_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "brkpd_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "check.png" - }, - "incompressible": true - }, - { - "element": { - "name": "check@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "common_tab.png" - }, - "incompressible": true - }, - { - "element": { - "name": "common_tab@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debugt_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debugt_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debugts_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debugts_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debugtt_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debugtt_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "environment_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "environment_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "envvar_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "envvar_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "export_config_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "export_config_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "expression_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "expression_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "file_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "file_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "fldr_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "fldr_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "genericreggroup_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "genericreggroup_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "genericregister_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "genericregister_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "genericvariable_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "genericvariable_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "import_config_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "import_config_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "inst_ptr.png" - }, - "incompressible": true - }, - { - "element": { - "name": "inst_ptr@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "inst_ptr_top.png" - }, - "incompressible": true - }, - { - "element": { - "name": "inst_ptr_top@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "jar_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "jar_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "ldebug_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "ldebug_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lgroup_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lgroup_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lrun_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lrun_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "memory_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "memory_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "memorychanged_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "memorychanged_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "osprc_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "osprc_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "osprct_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "osprct_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "persp_tab.png" - }, - "incompressible": true - }, - { - "element": { - "name": "persp_tab@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "prj_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "prj_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "proto_tab.png" - }, - "incompressible": true - }, - { - "element": { - "name": "proto_tab@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "read_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "read_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "read_obj_disabled.png" - }, - "incompressible": true - }, - { - "element": { - "name": "read_obj_disabled@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "readwrite_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "readwrite_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "readwrite_obj_disabled.png" - }, - "incompressible": true - }, - { - "element": { - "name": "readwrite_obj_disabled@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "refresh_tab.png" - }, - "incompressible": true - }, - { - "element": { - "name": "refresh_tab@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rundebug.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rundebug@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stckframe_obj.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "stckframe_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stckframe_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stckframe_running_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stckframe_running_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminatedlaunch_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminatedlaunch_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "thread_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "thread_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "threads_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "threads_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "threadt_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "threadt_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "uncheck.png" - }, - "incompressible": true - }, - { - "element": { - "name": "uncheck@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "workset.png" - }, - "incompressible": true - }, - { - "element": { - "name": "workset@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "write_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "write_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "write_obj_disabled.png" - }, - "incompressible": true - }, - { - "element": { - "name": "write_obj_disabled@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "ovr16" - }, - "children": [ - { - "element": { - "name": "error.png" - }, - "incompressible": true - }, - { - "element": { - "name": "error@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "prototype.png" - }, - "incompressible": true - }, - { - "element": { - "name": "prototype@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "skip_breakpoint_ov.png" - }, - "incompressible": true - }, - { - "element": { - "name": "skip_breakpoint_ov@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stcksync_ov.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stcksync_ov@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "transparent.png" - }, - "incompressible": true - }, - { - "element": { - "name": "transparent@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "var_cntnt_prvdr_ov.png" - }, - "incompressible": true - }, - { - "element": { - "name": "var_cntnt_prvdr_ov@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "wizban" - }, - "children": [ - { - "element": { - "name": "adddir_wiz.png" - }, - "incompressible": true - }, - { - "element": { - "name": "addsrcloc_wiz.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_wiz.png" - }, - "incompressible": true - }, - { - "element": { - "name": "editdir_wiz.png" - }, - "incompressible": true - }, - { - "element": { - "name": "edtsrclkup_wiz.png" - }, - "incompressible": true - }, - { - "element": { - "name": "export_brkpts_wizban.png" - }, - "incompressible": true - }, - { - "element": { - "name": "export_config_wizban.png" - }, - "incompressible": true - }, - { - "element": { - "name": "import_brkpts_wizban.png" - }, - "incompressible": true - }, - { - "element": { - "name": "import_config_wizban.png" - }, - "incompressible": true - }, - { - "element": { - "name": "profile_wiz.png" - }, - "incompressible": true - }, - { - "element": { - "name": "run_wiz.png" - }, - "incompressible": true - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "plugin.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "plugin.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "pom.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "schema" - }, - "children": [ - { - "element": { - "name": "breakpointOrganizers.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "consoleColorProviders.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "consoleLineTrackers.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "contextViewBindings.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "debugModelContextBindings.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "debugModelPresentations.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "detailPaneFactories.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "launchConfigurationTabGroups.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "launchConfigurationTabs.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "launchConfigurationTypeImages.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "launchGroups.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "launchShortcuts.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "memoryRenderings.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "sourceContainerPresentations.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "stringVariablePresentations.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "toggleBreakpointsTargetFactories.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "variableValueEditors.exsd" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "scripts" - }, - "children": [ - { - "element": { - "name": "exportplugin.xml" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "ui" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "debug" - }, - "children": [ - { - "element": { - "name": "internal" - }, - "children": [ - { - "element": { - "name": "ui" - }, - "children": [ - { - "element": { - "name": "AbstractDebugCheckboxSelectionDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractDebugListSelectionDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractDebugSelectionDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointImageProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BuildBeforeLaunchStatusHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ColorManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CompositeDebugImageDescriptor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugModelPropertyTester.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugPerspectiveFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugPluginImages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugUIAdapterFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugUIMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugUIMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugUIPlugin.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugUIPreferenceInitializer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DelegatingModelPresentation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DynamicInstructionPointerAnnotation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugHelpContextIds.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IInternalDebugUIConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchHistoryChangedListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchLabelChangedListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ImageDescriptorRegistry.java" - }, - "incompressible": true - }, - { - "element": { - "name": "InstructionPointerAnnotation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "InstructionPointerContext.java" - }, - "incompressible": true - }, - { - "element": { - "name": "InstructionPointerImageProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "InstructionPointerManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationTabExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LazyModelPresentation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MultipleInputDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Pair.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ResourceExtender.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SWTFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TerminateToggleValue.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TextGetSetEditingSupport.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariableValueEditorManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariablesViewModelPresentation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkingDirectoryStatusHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "actions" - }, - "children": [ - { - "element": { - "name": "AbstractDebugActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractRemoveAllActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractSelectionActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ActionMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ActionMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "AddToFavoritesAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CollapseAllAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConfigureColumnsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugAsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugContextualLaunchAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugHistoryMenuAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugLastAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugToolbarAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EditLaunchConfigurationAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExecutionAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchShortcutAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchablePropertyTester.java" - }, - "incompressible": true - }, - { - "element": { - "name": "OpenDebugConfigurations.java" - }, - "incompressible": true - }, - { - "element": { - "name": "OpenProfileConfigurations.java" - }, - "incompressible": true - }, - { - "element": { - "name": "OpenRunConfigurations.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProfileAsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProfileContextualLaunchAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProfileHistoryMenuAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProfileLastAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProfileToolbarAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RelaunchActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RelaunchLastAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RemoveAllTerminatedAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RetargetAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RetargetRunToLineAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RunAsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RunContextualLaunchAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RunHistoryMenuAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RunLastAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RunToolbarAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectAllAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StatusInfo.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleBreakpointsTargetManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleFilterAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewManagementAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "breakpointGroups" - }, - "children": [ - { - "element": { - "name": "AbstractBreakpointsViewAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AdvancedGroupBreakpointsByAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointGroupMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointGroupMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointSelectionAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointWorkingSetAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ClearDefaultBreakpointGroupAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CopyBreakpointsActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EditBreakpointGroupAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GroupBreakpointsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GroupBreakpointsByAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GroupBreakpointsByDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PasteBreakpointsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RemoveFromWorkingSetAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectBreakpointWorkingsetDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SetDefaultBreakpointGroupAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleDefaultGroupAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkingSetsAction.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "breakpointSortBy" - }, - "children": [ - { - "element": { - "name": "Messages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Messages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "SortBreakpointsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SortBreakpointsByAction.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "breakpoints" - }, - "children": [ - { - "element": { - "name": "AccessWatchpointToggleAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointsCollapseAllAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointsExpandAllAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DeleteWorkingsetsMessageDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DisableBreakpointsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EnableBreakpointsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LinkBreakpointsWithDebugViewAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Messages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Messages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "ModificationWatchpointToggleAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ModifyWatchpointAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "OpenBreakpointMarkerAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RemoveAllBreakpointsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RemoveAllTriggerPointsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RemoveBreakpointAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RetargetBreakpointAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RetargetMethodBreakpointAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RetargetToggleBreakpointAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RetargetToggleLineBreakpointAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RetargetWatchpointAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RulerEnableDisableBreakpointAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectAllBreakpointsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ShowSupportedBreakpointsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ShowTargetBreakpointsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SkipAllBreakpointsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleBreakpointObjectActionDelegate.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "expressions" - }, - "children": [ - { - "element": { - "name": "AddWatchExpressionAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConvertToWatchExpressionAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CopyExpressionsToClipboardActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DisableWatchExpressionAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EditWatchExpressinInPlaceAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EditWatchExpressionAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EnableWatchExpressionAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PasteWatchExpressionsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ReevaluateWatchExpressionAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RemoveAllExpressionsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RemoveExpressionAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectAllExpressionsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WatchExpressionAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WatchExpressionDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WatchExpressionFactoryTester.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WatchHandler.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "variables" - }, - "children": [ - { - "element": { - "name": "ChangeVariableValueAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ChangeVariableValueInputDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectAllVariablesAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ShowTypesAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleDetailPaneAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "details" - }, - "children": [ - { - "element": { - "name": "DetailPaneAssignValueAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DetailPaneMaxLengthAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DetailPaneMaxLengthDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DetailPaneWordWrapAction.java" - }, - "incompressible": true - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "breakpoints" - }, - "children": [ - { - "element": { - "name": "provisional" - }, - "children": [ - { - "element": { - "name": "IBreakpointContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IBreakpointOrganizer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IBreakpointUIConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "OtherBreakpointCategory.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "commands" - }, - "children": [ - { - "element": { - "name": "actions" - }, - "children": [ - { - "element": { - "name": "AbstractRequestMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ActionsUpdater.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugActionHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugCommandActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugCommandService.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DisconnectCommandAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DisconnectCommandActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DisconnectCommandHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DropToFrameCommandAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DropToFrameCommandActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DropToFrameCommandHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExecuteActionRequest.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ICommandParticipant.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IEnabledTarget.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RestartCommandAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RestartCommandActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RestartCommandHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ResumeCommandAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ResumeCommandActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ResumeCommandHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepIntoCommandAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepIntoCommandActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepIntoCommandHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepOverCommandAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepOverCommandActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepOverCommandHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepReturnCommandAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepReturnCommandActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepReturnCommandHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SuspendCommandAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SuspendCommandActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SuspendCommandHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TerminateAllAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TerminateAllActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TerminateAndRelaunchAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TerminateAndRelaunchHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TerminateAndRemoveAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TerminateCommandAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TerminateCommandActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TerminateCommandHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleStepFiltersAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleStepFiltersCommandActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleStepFiltersCommandHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "UpdateActionsRequest.java" - }, - "incompressible": true - }, - { - "element": { - "name": "UpdateHandlerRequest.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "contextlaunching" - }, - "children": [ - { - "element": { - "name": "ContextMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ContextMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "ContextRunner.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchingResourceManager.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "contexts" - }, - "children": [ - { - "element": { - "name": "DebugContextManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugContextSourceProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugModelContextBindingManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugWindowContextService.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchSuspendTrigger.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SuspendTriggerAdapterFactory.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "elements" - }, - "children": [ - { - "element": { - "name": "adapters" - }, - "children": [ - { - "element": { - "name": "AsynchronousDebugLabelAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultBreakpointsViewInput.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultVariableCellModifier.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultViewerInputProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryBlockContentAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryBlockLabelAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryRetrievalContentAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemorySegmentLabelAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Messages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Messages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "RegisterGroupProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StackFrameSourceDisplayAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StackFrameViewerInputProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariableColumnFactoryAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariableColumnPresentation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WatchExpressionCellModifier.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "groups" - }, - "children": [ - { - "element": { - "name": "CommonTabLite.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GroupCycleHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GroupElementLaunchedHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GroupLaunchConfigurationSelectionDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GroupLaunchConfigurationTabGroup.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GroupLaunchHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "UnsupportedModeHandler.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "hover" - }, - "children": [ - { - "element": { - "name": "DebugTextHover.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExpressionInformationControlCreator.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "importexport" - }, - "children": [ - { - "element": { - "name": "breakpoints" - }, - "children": [ - { - "element": { - "name": "BreakpointImportExport.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointsPathDecorator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EmbeddedBreakpointsViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExportBreakpoints.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IImportExportConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ImportBreakpoints.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ImportExportMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WizardExportBreakpoints.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WizardExportBreakpointsPage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WizardImportBreakpoints.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WizardImportBreakpointsPage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WizardImportBreakpointsSelectionPage.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "launchconfigurations" - }, - "children": [ - { - "element": { - "name": "ExportLaunchConfigurationsWizard.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExportLaunchConfigurationsWizardPage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ImportLaunchConfigurationsWizard.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ImportLaunchConfigurationsWizardPage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WizardMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WizardMessages.properties" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "launchConfigurations" - }, - "children": [ - { - "element": { - "name": "AbstractLaunchConfigurationAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ClosedProjectFilter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CollapseAllLaunchConfigurationAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CompileErrorProjectPromptStatusHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CompileErrorPromptStatusHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CreateLaunchConfigurationAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CreateLaunchConfigurationPrototypeAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugModePromptStatusHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DeleteLaunchConfigurationAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DeletedProjectFilter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DuplicateLaunchConfigurationAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DuplicateLaunchDelegatesStatusHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EnvironmentVariable.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExportLaunchConfigurationAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FavoritesDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FilterDropDownMenuCreator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FilterLaunchConfigurationAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchCategoryFilter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationComparator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationEditDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationFilteredTree.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationPresentationManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationPropertiesDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationSelectionDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationTabGroupExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationTabGroupViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationTabGroupWrapper.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationTabImageDescriptor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationTreeContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationTypeContribution.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationTypeFilter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationsDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationsMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationsMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchDelegateContribution.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchDelegateNotAvailableHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchGroupExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchGroupFilter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchHistory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchShortcutExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchShortcutSelectionDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchTabContribution.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LinkPrototypeAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "OrganizeFavoritesAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PerspectiveManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ResetWithPrototypeValuesAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SaveScopeResourcesHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectFavoritesDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectLaunchModesDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectLaunchersDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ShowCommandLineDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "UnlinkPrototypeAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkingSetComparator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkingSetsFilter.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "memory" - }, - "children": [ - { - "element": { - "name": "IMemoryBlockConnection.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryRenderingUpdater.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IPersistableDebugElement.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryRenderingManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryRenderingType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RenderingBindings.java" - }, - "incompressible": true - }, - { - "element": { - "name": "provisional" - }, - "children": [ - { - "element": { - "name": "AbstractAsyncTableRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractAsyncTextRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryViewPresentationContext.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "model" - }, - "children": [ - { - "element": { - "name": "elements" - }, - "children": [ - { - "element": { - "name": "BreakpointContainerLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointContainerMementoProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointManagerContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointManagerInputMementoProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointMementoProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugElementLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugElementMementoProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugTargetContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ElementContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ElementLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ElementMementoProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExpressionContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExpressionLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExpressionManagerContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExpressionManagerMementoProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExpressionMementoProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchManagerContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryBlockContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryBlockLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryRetrievalContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryViewElementMementoProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProcessContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RegisterGroupContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RegisterGroupLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RegisterGroupMementoProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StackFrameContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StackFrameMementoProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ThreadContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariableContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariableEditor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariableLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariableMementoProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewerInputProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WatchExpressionEditor.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "preferences" - }, - "children": [ - { - "element": { - "name": "BooleanFieldEditor2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsolePreferencePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugPreferencePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugPreferencesMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugPreferencesMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugPreferenceConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationsPreferencePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchPerspectivePreferencePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchersPreferencePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchingPreferencePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProcessPropertyPage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RunDebugPropertiesPage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StringVariablePreferencePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewManagementPreferencePage.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "quickaccess" - }, - "children": [ - { - "element": { - "name": "AbstractLaunchQuickAccessComputer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugQuickAccessComputer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchQuickAccessElement.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProfileQuickAccessComputer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RunQuickAccessComputer.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "sourcelookup" - }, - "children": [ - { - "element": { - "name": "AddContainerAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AddSourceContainerDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BasicContainerContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DownAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EditContainerAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EditSourceLookupPathAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LookupSourceAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Prompter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RemoveAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ResolveDuplicatesHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RestoreDefaultAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceContainerAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceContainerAdapterFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceContainerLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceContainerViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceContainerWorkbenchAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceElementAdapterFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceElementLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceElementWorkbenchAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLookupFacility.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLookupManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLookupPanel.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLookupResult.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLookupService.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLookupUIMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLookupUIMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLookupUIUtils.java" - }, - "incompressible": true - }, - { - "element": { - "name": "UpAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkingSetSourceContainerType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "browsers" - }, - "children": [ - { - "element": { - "name": "ArchiveFilter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ArchiveSourceContainerBrowser.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DirectorySourceContainerBrowser.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DirectorySourceContainerDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalArchiveSourceContainerBrowser.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FolderSourceContainerBrowser.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FolderSourceContainerDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProjectSourceContainerBrowser.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProjectSourceContainerDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkingSetSourceContainerBrowser.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkspaceSourceContainerBrowser.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "stringsubstitution" - }, - "children": [ - { - "element": { - "name": "FilePrompt.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FolderPrompt.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IArgumentSelector.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PasswordPrompt.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PromptingResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ResourceSelector.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectedResourceManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectedResourceResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectedTextResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StringPrompt.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StringSubstitutionMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StringSubstitutionMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "StringVariableLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StringVariablePresentationManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SystemPropertyArgumentSelector.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "viewers" - }, - "children": [ - { - "element": { - "name": "AbstractUpdatePolicy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsynchronousModel.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsynchronousRequestMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsynchronousSchedulingRuleFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsynchronousTableModel.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsynchronousTableViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsynchronousViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ChildrenRequestMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FindElementDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILabelResult.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LabelRequestMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LabelResult.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ModelNode.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PartPresentationContext.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableAddRequestMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableEditorImpl.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableInsertRequestMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableRemoveRequestMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableReplaceRequestMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableUpdatePolicy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "breadcrumb" - }, - "children": [ - { - "element": { - "name": "AbstractBreadcrumb.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreadcrumbItem.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreadcrumbItemDetails.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreadcrumbItemDropDown.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreadcrumbMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreadcrumbMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "BreadcrumbViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IBreadcrumbDropDownSite.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TreeViewerDropDown.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "model" - }, - "children": [ - { - "element": { - "name": "ChildrenCountUpdate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ChildrenUpdate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ElementCompareRequest.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ElementMementoRequest.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FilterTransform.java" - }, - "incompressible": true - }, - { - "element": { - "name": "HasChildrenUpdate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IInternalTreeModelViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILabelUpdateListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ITreeModelContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ITreeModelLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ITreeModelViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "InternalTreeModelViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "InternalVirtualTreeModelViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LabelUpdate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MementoUpdate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SubTreeModelViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TimeTriggeredProgressMonitorDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TreeModelContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TreeModelLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewerAdapterService.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewerInputUpdate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewerStateTracker.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewerUpdateMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualCopyToClipboardActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualFindAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "provisional" - }, - "children": [ - { - "element": { - "name": "ICheckUpdate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ICheckboxModelProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IChildrenCountUpdate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IChildrenUpdate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IColumnPresentation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IColumnPresentation2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IColumnPresentationFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IElementCompareRequest.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IElementContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IElementEditor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IElementLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IElementMementoProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IElementMementoRequest.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IHasChildrenUpdate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILabelUpdate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IModelChangedListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IModelDelta.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IModelDeltaVisitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IModelProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IModelProxy2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IModelProxyFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IModelProxyFactory2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IModelSelectionPolicy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IModelSelectionPolicyFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IPresentationContext.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStateUpdateListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStatusMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ITreeModelViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IViewActionProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IViewerInputProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IViewerInputRequestor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IViewerInputUpdate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IViewerUpdate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IViewerUpdateListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IVirtualItemListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IVirtualItemValidator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ModelDelta.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PresentationContext.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TreeModelViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TreeModelViewerFilter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewerInputService.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualItem.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualTree.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualTreeModelViewer.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "provisional" - }, - "children": [ - { - "element": { - "name": "AbstractColumnPresentation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractModelProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsynchronousContentAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsynchronousLabelAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IAsynchronousContentAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IAsynchronousLabelAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IChildrenRequestMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IContainerRequestMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILabelRequestMonitor.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "update" - }, - "children": [ - { - "element": { - "name": "BreakpointContainerProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointManagerProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugEventHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugTargetEventHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugTargetProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultExpressionModelProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultModelProxyFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultModelSelectionPolicyFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultSelectionPolicy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultVariableViewModelProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultWatchExpressionModelProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EventHandlerModelProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExpressionEventHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExpressionManagerModelProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchManagerProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryBlockProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryRetrievalProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProcessProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StackFrameEventHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ThreadEventHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariablesViewEventHandler.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "views" - }, - "children": [ - { - "element": { - "name": "DebugModelPresentationContext.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugUIViewsMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugUIViewsMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugExceptionHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewContextManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewContextService.java" - }, - "incompressible": true - }, - { - "element": { - "name": "breakpoints" - }, - "children": [ - { - "element": { - "name": "BreakpointContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointContainerWorkbenchAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointOrganizerExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointOrganizerManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointPersistableElementAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointSetOrganizer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointTypeOrganizer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointWorkingSetCache.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointWorkingSetElementAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointWorkingSetPage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointsComparator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointsContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointsDragAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointsDropAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointsLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointsView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointsViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ElementComparator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FileBreakpointOrganizer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProjectBreakpointOrganizer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkingSetBreakpointOrganizer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkingSetCategory.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "console" - }, - "children": [ - { - "element": { - "name": "ConsoleLineNotifier.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleRemoveAllTerminatedAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleRemoveLaunchAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleShowPreferencesAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleTerminateAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProcessConsole.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProcessConsoleManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProcessConsolePageParticipant.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProcessTypePropertyTester.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ShowStandardErrorAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ShowStandardOutAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ShowWhenContentChangesAction.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "expression" - }, - "children": [ - { - "element": { - "name": "ExpressionDropAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExpressionView.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "launch" - }, - "children": [ - { - "element": { - "name": "BreadcrumbDropDownAutoExpandAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreadcrumbWorkbenchPart.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugElementAdapterFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugElementHelper.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugToolBarAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugViewModeAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Decoration.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DecorationManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ImageImageDescriptor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchViewBreadcrumb.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchViewCopyToClipboardActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchViewMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchViewMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceNotFoundEditor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceNotFoundEditorInput.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StandardDecoration.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TerminateAndRemoveHandler.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "memory" - }, - "children": [ - { - "element": { - "name": "AbstractMemoryViewPane.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AddMemoryBlockAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AddMemoryRenderingAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AddMemoryRenderingContextAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AddMemoryRenderingDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CodePagesPreferencePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryViewPane.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryViewTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LinkRenderingPanesAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryBlocksTreeViewPane.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryViewIdRegistry.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryViewPrefAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryViewSynchronizationService.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryViewTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryViewTreeModelContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryViewTreeViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryViewUtil.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MonitorMemoryBlockDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "NewMemoryViewAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PinMemoryBlockAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PropertyChangeNotifier.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RemoveMemoryRenderingAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RemoveRenderingContextAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RenderingViewPane.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ResetMemoryBlockAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ResetMemoryBlockPreferencePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RetargetAddMemoryBlockAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SetPaddedStringPreferencePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SwitchMemoryBlockAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SynchronizeInfo.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleMemoryMonitorsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleSplitPaneAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleViewPaneAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewPaneOrientationAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewPaneRenderingMgr.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewPaneSelectionProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewTabEnablementManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "renderings" - }, - "children": [ - { - "element": { - "name": "ASCIIRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ASCIIRenderingTypeDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractBaseTableRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractIntegerRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractTableRenderingLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractVirtualContentTableModel.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsyncCopyTableRenderingAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsyncPrintTableRenderingAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsyncTableRenderingCellModifier.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsyncTableRenderingUpdatePolicy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsyncTableRenderingViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsyncVirtualContentTableViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BasicDebugViewContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BigEndianAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CopyTableRenderingToClipboardAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CreateRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultEndianessAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ErrorRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FormatTableRenderingAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FormatTableRenderingDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GoToAddressAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GoToAddressComposite.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GoToAddressDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "HexIntegerRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "HexIntegerRenderingDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "HexRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "HexRenderingTypeDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IContentChangeComputer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IPresentationErrorListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IVirtualContentListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LittleEndianAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemorySegment.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PendingPropertyChanges.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PrintTableRenderingAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ReformatAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RenderingsUtil.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ResetToBaseAddressAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SignedIntegerRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SignedIntegerRenderingTypeDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableRenderingCellModifier.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableRenderingContentDescriptor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableRenderingContentInput.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableRenderingContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableRenderingLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableRenderingLabelProviderEx.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableRenderingLine.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableRenderingModel.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableRenderingPrefAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableRenderingPreferencePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableRenderingPropertiesPage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "UnsignedIntegerRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "UnsignedIntegerRenderingTypeDelegate.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "modules" - }, - "children": [ - { - "element": { - "name": "IHelpContextIdProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ModulesView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ModulesViewMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ModulesViewMessages.properties" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "registers" - }, - "children": [ - { - "element": { - "name": "RegistersView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RegistersViewMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RegistersViewMessages.properties" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "variables" - }, - "children": [ - { - "element": { - "name": "AvailableLogicalStructuresAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EditVariableLogicalStructureAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IndexedValuePartition.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IndexedVariablePartition.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LogicalStructureCache.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectLogicalStructureAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectionDragAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleLogicalStructureAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleShowColumnsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariableViewToggleAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariablesView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariablesViewMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariablesViewMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "VariablesViewResourceBundleMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "details" - }, - "children": [ - { - "element": { - "name": "AbstractDetailPane.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AvailableDetailPanesAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultDetailPane.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultDetailPaneFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DetailMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DetailMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "DetailPaneManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DetailPaneProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDetailPaneContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDetailPaneContainer2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MessageDetailPane.java" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "ui" - }, - "children": [ - { - "element": { - "name": "AbstractBreakpointOrganizerDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractDebugView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractLaunchConfigurationTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractLaunchConfigurationTabGroup.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointTypeCategory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CommonTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugElementWorkbenchAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugPopup.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugUITools.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DeferredDebugElementWorkbenchAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EnvironmentTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IBreakpointOrganizerDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IBreakpointOrganizerDelegateExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IBreakpointTypeCategory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugEditorPresentation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugModelPresentation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugModelPresentationExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugUIConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDetailPane.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDetailPane2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDetailPane3.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDetailPaneFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IInstructionPointerPresentation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchConfigurationDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchConfigurationTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchConfigurationTab2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchConfigurationTabGroup.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchGroup.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchShortcut.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchShortcut2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISourcePresentation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IValueDetailListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "InspectPopupDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PrototypeDecorator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PrototypeTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RefreshTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StringVariableSelectionDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkingDirectoryBlock.java" - }, - "incompressible": true - }, - { - "element": { - "name": "actions" - }, - "children": [ - { - "element": { - "name": "AbstractLaunchHistoryAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractLaunchToolbarAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AddMemoryRenderingActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointTypesContribution.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ContextualLaunchAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugCommandAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugCommandHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExportBreakpointsOperation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IAddMemoryBlocksTarget.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IAddMemoryRenderingsTarget.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchable.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IRunToLineTarget.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IToggleBreakpointsTarget.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IToggleBreakpointsTargetExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IToggleBreakpointsTargetExtension2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IToggleBreakpointsTargetFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IToggleBreakpointsTargetManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IToggleBreakpointsTargetManagerListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IVariableValueEditor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IWatchExpressionFactoryAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IWatchExpressionFactoryAdapter2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IWatchExpressionFactoryAdapterExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ImportBreakpointsOperation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchAsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchShortcutsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "OpenLaunchDialogAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RelaunchLastAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RulerBreakpointAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RulerBreakpointTypesActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RulerEnableDisableBreakpointActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RulerRunToLineActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RulerToggleBreakpointActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RunAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RunToLineAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RunToLineActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RunToLineHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleBreakpointAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleMethodBreakpointActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleWatchpointActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "console" - }, - "children": [ - { - "element": { - "name": "ConsoleColorProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FileLink.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsole.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsoleColorProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsoleHyperlink.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsoleLineTracker.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsoleLineTrackerExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "contexts" - }, - "children": [ - { - "element": { - "name": "AbstractDebugContextProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugContextEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugContextListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugContextManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugContextProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugContextProvider2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugContextService.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISuspendTrigger.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISuspendTriggerListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "memory" - }, - "children": [ - { - "element": { - "name": "AbstractMemoryRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractMemoryRenderingBindingsProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractTableRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractTextRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryBlockTablePresentation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryRenderingBindingsListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryRenderingBindingsProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryRenderingContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryRenderingManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryRenderingSite.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryRenderingSite2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryRenderingSynchronizationService.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryRenderingType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryRenderingTypeDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IRepositionableMemoryRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IResettableMemoryRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryRenderingElement.java" - }, - "incompressible": true - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - }, - { - "element": { - "name": "sourcelookup" - }, - "children": [ - { - "element": { - "name": "AbstractSourceContainerBrowser.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CommonSourceNotFoundEditor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CommonSourceNotFoundEditorInput.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISourceContainerBrowser.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISourceDisplay.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISourceLookupResult.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLookupDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLookupTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkingSetSourceContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "stringsubstitution" - }, - "children": [ - { - "element": { - "name": "IArgumentSelector.java" - }, - "incompressible": true - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "org.eclipse.ui.console" - }, - "children": [ - { - "element": { - "name": ".classpath" - }, - "incompressible": true - }, - { - "element": { - "name": ".project" - }, - "incompressible": true - }, - { - "element": { - "name": ".settings" - }, - "children": [ - { - "element": { - "name": "org.eclipse.core.resources.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.runtime.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.core.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.ui.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.api.tools.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.prefs" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "META-INF" - }, - "children": [ - { - "element": { - "name": "MANIFEST.MF" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "about.html" - }, - "incompressible": true - }, - { - "element": { - "name": "build.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "icons" - }, - "children": [ - { - "element": { - "name": "full" - }, - "children": [ - { - "element": { - "name": "clcl16" - }, - "children": [ - { - "element": { - "name": "clear_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "clear_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lock_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lock_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "pin.png" - }, - "incompressible": true - }, - { - "element": { - "name": "pin@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "wordwrap.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "cview16" - }, - "children": [ - { - "element": { - "name": "console_view.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "console_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "console_view@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "dlcl16" - }, - "children": [ - { - "element": { - "name": "clear_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "clear_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lock_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lock_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "new_con.png" - }, - "incompressible": true - }, - { - "element": { - "name": "new_con@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "pin.png" - }, - "incompressible": true - }, - { - "element": { - "name": "pin@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "wordwrap.png" - }, - "incompressible": true - }, - { - "element": { - "name": "wordwrap@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "dview16" - }, - "children": [ - { - "element": { - "name": "console_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "console_view@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "elcl16" - }, - "children": [ - { - "element": { - "name": "clear_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "clear_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lock_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lock_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "new_con.png" - }, - "incompressible": true - }, - { - "element": { - "name": "new_con@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "pin.png" - }, - "incompressible": true - }, - { - "element": { - "name": "pin@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "wordwrap.png" - }, - "incompressible": true - }, - { - "element": { - "name": "wordwrap@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "eview16" - }, - "children": [ - { - "element": { - "name": "console_view.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "console_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "console_view@2x.png" - }, - "incompressible": true - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "plugin.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "plugin.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "pom.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "schema" - }, - "children": [ - { - "element": { - "name": "consoleFactories.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "consolePageParticipants.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "consolePatternMatchListeners.exsd" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "scripts" - }, - "children": [ - { - "element": { - "name": "exportplugin.xml" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "src" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "ui" - }, - "children": [ - { - "element": { - "name": "console" - }, - "children": [ - { - "element": { - "name": "AbstractConsole.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsolePlugin.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsole.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsoleConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsoleDocumentPartitioner.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsoleFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsoleListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsoleManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsolePageParticipant.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsoleView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IHyperlink.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IHyperlink2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IOConsole.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IOConsoleInputStream.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IOConsoleOutputStream.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IPatternMatchListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IPatternMatchListenerDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IScrollLockStateProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MessageConsole.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MessageConsoleStream.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PatternMatchEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TextConsole.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TextConsolePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TextConsoleViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "actions" - }, - "children": [ - { - "element": { - "name": "ClearOutputAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CloseConsoleAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TextViewerAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TextViewerGotoLineAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "internal" - }, - "children": [ - { - "element": { - "name": "console" - }, - "children": [ - { - "element": { - "name": "ConsoleDocument.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleDocumentAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleDropDownAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleFactoryExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleHyperlinkPosition.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsolePageParticipantExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsolePatternMatcher.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsolePluginImages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleResourceBundleMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleResourceBundleMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleTypePropertyTester.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleUIPreferenceInitializer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleViewConsoleFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleWorkbenchPart.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FollowHyperlinkAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "HyperlinkUpdater.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsoleHelpContextIds.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IInternalConsoleConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IOConsolePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IOConsolePartition.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IOConsolePartitioner.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IOConsoleViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "OpenConsoleAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PatternMatchListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PatternMatchListenerExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PinConsoleAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ScrollLockAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ShowConsoleAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StreamDecoder.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WordWrapAction.java" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "org.eclipse.ui.externaltools" - }, - "children": [ - { - "element": { - "name": ".classpath" - }, - "incompressible": true - }, - { - "element": { - "name": ".gitignore" - }, - "incompressible": true - }, - { - "element": { - "name": ".project" - }, - "incompressible": true - }, - { - "element": { - "name": ".settings" - }, - "children": [ - { - "element": { - "name": ".api_filters" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.resources.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.runtime.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.core.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.ui.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.api.tools.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.prefs" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "External Tools Base" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "ui" - }, - "children": [ - { - "element": { - "name": "externaltools" - }, - "children": [ - { - "element": { - "name": "internal" - }, - "children": [ - { - "element": { - "name": "launchConfigurations" - }, - "children": [ - { - "element": { - "name": "ExternalToolsBuildTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsBuilderTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsLaunchConfigurationMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsLaunchConfigurationMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsMainTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsUtil.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IgnoreWhiteSpaceComparator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkingSetComparator.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "menu" - }, - "children": [ - { - "element": { - "name": "ExternalToolMenuDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "OpenExternalToolsConfigurations.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "model" - }, - "children": [ - { - "element": { - "name": "BuilderUtils.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsImages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsModelMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsModelMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsPlugin.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsPreferenceInitializer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IExternalToolConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IExternalToolsHelpContextIds.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IPreferenceConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ImageDescriptorRegistry.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "ui" - }, - "children": [ - { - "element": { - "name": "BuilderLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BuilderPropertyPage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EditCommandDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsPreferencePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsUIMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsUIMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "FileSelectionDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TreeAndListGroup.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "variables" - }, - "children": [ - { - "element": { - "name": "BuildFilesResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BuildProjectResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BuildTypeResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SystemPathResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariableMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariableMessages.properties" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "META-INF" - }, - "children": [ - { - "element": { - "name": "MANIFEST.MF" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "Program Tools Support" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "ui" - }, - "children": [ - { - "element": { - "name": "externaltools" - }, - "children": [ - { - "element": { - "name": "internal" - }, - "children": [ - { - "element": { - "name": "program" - }, - "children": [ - { - "element": { - "name": "launchConfigurations" - }, - "children": [ - { - "element": { - "name": "ExternalToolsProgramMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsProgramMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "ProgramBuilderTabGroup.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProgramMainTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProgramTabGroup.java" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "about.html" - }, - "incompressible": true - }, - { - "element": { - "name": "build.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "icons" - }, - "children": [ - { - "element": { - "name": "full" - }, - "children": [ - { - "element": { - "name": "dtool16" - }, - "children": [ - { - "element": { - "name": "external_tools.png" - }, - "incompressible": true - }, - { - "element": { - "name": "external_tools@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "etool16" - }, - "children": [ - { - "element": { - "name": "external_tools.png" - }, - "incompressible": true - }, - { - "element": { - "name": "external_tools@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "obj16" - }, - "children": [ - { - "element": { - "name": "build_tab.png" - }, - "incompressible": true - }, - { - "element": { - "name": "build_tab@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "builder.png" - }, - "incompressible": true - }, - { - "element": { - "name": "builder@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "classpath.png" - }, - "incompressible": true - }, - { - "element": { - "name": "classpath@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "external_tools.png" - }, - "incompressible": true - }, - { - "element": { - "name": "external_tools@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "invalid_build_tool.png" - }, - "incompressible": true - }, - { - "element": { - "name": "invalid_build_tool@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "main_tab.png" - }, - "incompressible": true - }, - { - "element": { - "name": "main_tab@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "wizban" - }, - "children": [ - { - "element": { - "name": "ext_tools_wiz.png" - }, - "incompressible": true - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "plugin.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "plugin.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "pom.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "schema" - }, - "children": [ - { - "element": { - "name": "configurationDuplicationMaps.exsd" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "pom.xml" - }, - "incompressible": true - } - ] - } -] \ No newline at end of file diff --git a/test/ui/tree/public/index.html b/test/ui/tree/public/index.html deleted file mode 100644 index 3f66d505016..00000000000 --- a/test/ui/tree/public/index.html +++ /dev/null @@ -1,403 +0,0 @@ - - - - - Tree - - - - - - - - - -
- - - - - - diff --git a/test/ui/tree/server.js b/test/ui/tree/server.js deleted file mode 100644 index 5b5dd3f1a6c..00000000000 --- a/test/ui/tree/server.js +++ /dev/null @@ -1,68 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -const fs = require('mz/fs'); -const path = require('path'); -const Koa = require('koa'); -const _ = require('koa-route'); -const serve = require('koa-static'); -const mount = require('koa-mount'); - -const app = new Koa(); -const root = path.dirname(path.dirname(__dirname)); - -async function getTree(fsPath, level) { - const element = path.basename(fsPath); - const stat = await fs.stat(fsPath); - - if (!stat.isDirectory() || element === '.git' || element === '.build' || level >= 4) { - return { element }; - } - - const childNames = await fs.readdir(fsPath); - const children = await Promise.all(childNames.map(async childName => await getTree(path.join(fsPath, childName), level + 1))); - return { element, collapsible: true, collapsed: false, children }; -} - -async function readdir(relativePath) { - const absolutePath = relativePath ? path.join(root, relativePath) : root; - const childNames = await fs.readdir(absolutePath); - const childStats = await Promise.all(childNames.map(async name => await fs.stat(path.join(absolutePath, name)))); - const result = []; - - for (let i = 0; i < childNames.length; i++) { - const name = childNames[i]; - const path = relativePath ? `${relativePath}/${name}` : name; - const stat = childStats[i]; - - if (stat.isFile()) { - result.push({ type: 'file', name, path }); - } else if (!stat.isDirectory() || name === '.git' || name === '.build') { - continue; - } else { - result.push({ type: 'dir', name, path }); - } - } - - return result; -} - -app.use(serve('public')); -app.use(mount('/static', serve('../../out'))); -app.use(_.get('/api/ls', async ctx => { - const relativePath = ctx.query.path; - const absolutePath = path.join(root, relativePath); - - ctx.body = await getTree(absolutePath, 0); -})); - -app.use(_.get('/api/readdir', async ctx => { - const relativePath = ctx.query.path; - - ctx.body = await readdir(relativePath); -})); - -app.listen(3000); -console.log('http://localhost:3000'); \ No newline at end of file diff --git a/test/ui/tree/tree.js b/test/ui/tree/tree.js deleted file mode 100644 index 4d5a9b5f271..00000000000 --- a/test/ui/tree/tree.js +++ /dev/null @@ -1,24 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -const path = require('path'); -const fs = require('fs'); - -function collect(location) { - const element = { name: path.basename(location) }; - const stat = fs.statSync(location); - - if (!stat.isDirectory()) { - return { element, incompressible: true }; - } - - const children = fs.readdirSync(location) - .map(child => path.join(location, child)) - .map(collect); - - return { element, children }; -} - -console.log(JSON.stringify(collect(process.cwd()))); \ No newline at end of file diff --git a/test/ui/tree/yarn.lock b/test/ui/tree/yarn.lock deleted file mode 100644 index 237201a684e..00000000000 --- a/test/ui/tree/yarn.lock +++ /dev/null @@ -1,341 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -accepts@^1.2.2: - version "1.3.5" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" - integrity sha1-63d99gEXI6OxTopywIBcjoZ0a9I= - dependencies: - mime-types "~2.1.18" - negotiator "0.6.1" - -any-promise@^1.0.0, any-promise@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" - integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= - -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= - -content-disposition@~0.5.0: - version "0.5.2" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" - integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= - -content-type@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== - -cookies@~0.7.0: - version "0.7.1" - resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.7.1.tgz#7c8a615f5481c61ab9f16c833731bcb8f663b99b" - integrity sha1-fIphX1SBxhq58WyDNzG8uPZjuZs= - dependencies: - depd "~1.1.1" - keygrip "~1.0.2" - -debug@*, debug@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== - dependencies: - ms "2.0.0" - -debug@^2.6.1: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -deep-equal@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" - integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= - -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= - -depd@^1.1.0, depd@~1.1.1, depd@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= - -destroy@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= - -error-inject@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/error-inject/-/error-inject-1.0.0.tgz#e2b3d91b54aed672f309d950d154850fa11d4f37" - integrity sha1-4rPZG1Su1nLzCdlQ0VSFD6EdTzc= - -escape-html@~1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= - -fresh@^0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= - -http-assert@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.3.0.tgz#a31a5cf88c873ecbb5796907d4d6f132e8c01e4a" - integrity sha1-oxpc+IyHPsu1eWkH1NbxMujAHko= - dependencies: - deep-equal "~1.0.1" - http-errors "~1.6.1" - -http-errors@^1.2.8, http-errors@^1.6.3, http-errors@~1.6.1, http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" - integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - -is-generator-function@^1.0.3: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522" - integrity sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw== - -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= - -keygrip@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.0.2.tgz#ad3297c557069dea8bcfe7a4fa491b75c5ddeb91" - integrity sha1-rTKXxVcGneqLz+ek+kkbdcXd65E= - -koa-compose@^3.0.0, koa-compose@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-3.2.1.tgz#a85ccb40b7d986d8e5a345b3a1ace8eabcf54de7" - integrity sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec= - dependencies: - any-promise "^1.1.0" - -koa-compose@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-4.1.0.tgz#507306b9371901db41121c812e923d0d67d3e877" - integrity sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw== - -koa-convert@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-1.2.0.tgz#da40875df49de0539098d1700b50820cebcd21d0" - integrity sha1-2kCHXfSd4FOQmNFwC1CCDOvNIdA= - dependencies: - co "^4.6.0" - koa-compose "^3.0.0" - -koa-is-json@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/koa-is-json/-/koa-is-json-1.0.0.tgz#273c07edcdcb8df6a2c1ab7d59ee76491451ec14" - integrity sha1-JzwH7c3Ljfaiwat9We52SRRR7BQ= - -koa-mount@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/koa-mount/-/koa-mount-3.0.0.tgz#08cab3b83d31442ed8b7e75c54b1abeb922ec197" - integrity sha1-CMqzuD0xRC7Yt+dcVLGr65IuwZc= - dependencies: - debug "^2.6.1" - koa-compose "^3.2.1" - -koa-route@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/koa-route/-/koa-route-3.2.0.tgz#76298b99a6bcfa9e38cab6fe5c79a8733e758bce" - integrity sha1-dimLmaa8+p44yrb+XHmocz51i84= - dependencies: - debug "*" - methods "~1.1.0" - path-to-regexp "^1.2.0" - -koa-send@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/koa-send/-/koa-send-5.0.0.tgz#5e8441e07ef55737734d7ced25b842e50646e7eb" - integrity sha512-90ZotV7t0p3uN9sRwW2D484rAaKIsD8tAVtypw/aBU+ryfV+fR2xrcAwhI8Wl6WRkojLUs/cB9SBSCuIb+IanQ== - dependencies: - debug "^3.1.0" - http-errors "^1.6.3" - mz "^2.7.0" - resolve-path "^1.4.0" - -koa-static@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/koa-static/-/koa-static-5.0.0.tgz#5e92fc96b537ad5219f425319c95b64772776943" - integrity sha512-UqyYyH5YEXaJrf9S8E23GoJFQZXkBVJ9zYYMPGz919MSX1KuvAcycIuS0ci150HCoPf4XQVhQ84Qf8xRPWxFaQ== - dependencies: - debug "^3.1.0" - koa-send "^5.0.0" - -koa@^2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/koa/-/koa-2.5.1.tgz#79f8b95f8d72d04fe9a58a8da5ebd6d341103f9c" - integrity sha512-cchwbMeG2dv3E2xTAmheDAuvR53tPgJZN/Hf1h7bTzJLSPcFZp8/t5+bNKJ6GaQZoydhZQ+1GNruhKdj3lIrug== - dependencies: - accepts "^1.2.2" - content-disposition "~0.5.0" - content-type "^1.0.0" - cookies "~0.7.0" - debug "*" - delegates "^1.0.0" - depd "^1.1.0" - destroy "^1.0.3" - error-inject "~1.0.0" - escape-html "~1.0.1" - fresh "^0.5.2" - http-assert "^1.1.0" - http-errors "^1.2.8" - is-generator-function "^1.0.3" - koa-compose "^4.0.0" - koa-convert "^1.2.0" - koa-is-json "^1.0.0" - mime-types "^2.0.7" - on-finished "^2.1.0" - only "0.0.2" - parseurl "^1.3.0" - statuses "^1.2.0" - type-is "^1.5.5" - vary "^1.0.0" - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= - -methods@~1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= - -mime-db@~1.33.0: - version "1.33.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" - integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== - -mime-types@^2.0.7, mime-types@~2.1.18: - version "2.1.18" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" - integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== - dependencies: - mime-db "~1.33.0" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -mz@^2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" - integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== - dependencies: - any-promise "^1.0.0" - object-assign "^4.0.1" - thenify-all "^1.0.0" - -negotiator@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" - integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk= - -object-assign@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -on-finished@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= - dependencies: - ee-first "1.1.1" - -only@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4" - integrity sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q= - -parseurl@^1.3.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" - integrity sha1-/CidTtiZMRlGDBViUyYs3I3mW/M= - -path-is-absolute@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-to-regexp@^1.2.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d" - integrity sha1-Wf3g9DW62suhA6hOnTvGTpa5k30= - dependencies: - isarray "0.0.1" - -resolve-path@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/resolve-path/-/resolve-path-1.4.0.tgz#c4bda9f5efb2fce65247873ab36bb4d834fe16f7" - integrity sha1-xL2p9e+y/OZSR4c6s2u02DT+Fvc= - dependencies: - http-errors "~1.6.2" - path-is-absolute "1.0.1" - -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" - integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== - -"statuses@>= 1.4.0 < 2", statuses@^1.2.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= - -thenify-all@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" - integrity sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY= - dependencies: - thenify ">= 3.1.0 < 4" - -"thenify@>= 3.1.0 < 4": - version "3.3.0" - resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.0.tgz#e69e38a1babe969b0108207978b9f62b88604839" - integrity sha1-5p44obq+lpsBCCB5eLn2K4hgSDk= - dependencies: - any-promise "^1.0.0" - -type-is@^1.5.5: - version "1.6.16" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" - integrity sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.18" - -vary@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= diff --git a/yarn.lock b/yarn.lock index 8695942fcec..4fbbc3752b8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9687,11 +9687,6 @@ typescript-formatter@7.1.0: commandpost "^1.0.0" editorconfig "^0.15.0" -typescript@4.2.0-dev.20201207: - version "4.2.0-dev.20201207" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.0-dev.20201207.tgz#19a34bc7d2d42a7467c512c63f135587ac848807" - integrity sha512-fPHBDi/fgdX4WiRC7cFVv/aL069PgUaDWuLYUSHatWZujz/Lkc9bkf/zL3rKdNSCxlNKAMs3fhJv/yompOphZA== - typescript@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4" @@ -9702,6 +9697,11 @@ typescript@^3.0.1: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== +typescript@^4.3.0-dev.20210216: + version "4.3.0-dev.20210216" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.0-dev.20210216.tgz#233327e6094008c02265ba140f8d9ece9133421e" + integrity sha512-pJLcC/kqnE+0rftTRc2/gYBkz9nl+kJfaU8sSOLYnzUvD8p+LOZMzXfaLoKPdGFJ6U9+Ox/sYV9HBTJVEjSTYg== + typical@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4"