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 73acbb00426..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: @@ -247,6 +247,9 @@ jobs: - name: Run Monaco Editor Checks run: yarn monaco-compile-check + - name: Run Trusted Types Checks + run: yarn tsec-compile-check + - name: Editor Distro & ESM Bundle run: yarn gulp editor-esm-bundle diff --git a/.github/workflows/deep-classifier-runner.yml b/.github/workflows/deep-classifier-runner.yml index 674b35d0b78..19a8a705971 100644 --- a/.github/workflows/deep-classifier-runner.yml +++ b/.github/workflows/deep-classifier-runner.yml @@ -38,7 +38,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install --upgrade numpy scipy scikit-learn joblib nltk simpletransformers torch torchvision + pip install --upgrade numpy==1.20.0 scipy==1.6.0 scikit-learn==0.24.1 joblib==1.0.0 nltk==3.5 simpletransformers==0.51.16 torch==1.7.1 torchvision==0.8.2 - name: "Run Classifier: Generator" run: python ./actions/classifier-deep/apply/generate-labels/main.py - name: "Run Classifier: Labeler" 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 c7134b3a289..69c1b7b0aa9 100644 --- a/.vscode/notebooks/inbox.github-issues +++ b/.vscode/notebooks/inbox.github-issues @@ -3,48 +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\"", - "editable": true + "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 ", - "editable": true + "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", - "editable": true + "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 a566e7309fa..c72d5cde062 100644 --- a/.vscode/notebooks/my-work.github-issues +++ b/.vscode/notebooks/my-work.github-issues @@ -3,114 +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\"", - "editable": true + "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", - "editable": false + "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", - "editable": true + "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", - "editable": true + "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", - "editable": true + "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", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", "value": "$repos assignee:@me is:open milestone:\"Backlog Candidates\"", - "editable": false + "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", - "editable": true + "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\"", - "editable": false + "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/.moduleignore b/build/.moduleignore index cde700b8556..d1f9194ba2a 100644 --- a/build/.moduleignore +++ b/build/.moduleignore @@ -72,7 +72,6 @@ keytar/node_modules/** node-pty/binding.gyp node-pty/build/** -node-pty/lib/** node-pty/src/** node-pty/tools/** node-pty/deps/** 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/lib/electron.ts b/build/lib/electron.ts index beb1b571f52..06c8033edb4 100644 --- a/build/lib/electron.ts +++ b/build/lib/electron.ts @@ -32,7 +32,7 @@ export const config = { version: util.getElectronVersion(), productAppName: product.nameLong, companyName: 'Microsoft Corporation', - copyright: 'Copyright (C) 2019 Microsoft. All rights reserved', + copyright: 'Copyright (C) 2021 Microsoft. All rights reserved', darwinIcon: 'resources/darwin/code.icns', darwinBundleIdentifier: product.darwinBundleIdentifier, darwinApplicationCategoryType: 'public.app-category.developer-tools', 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/package.json b/extensions/configuration-editing/package.json index 6d99ab0d5b3..f1670e836c3 100644 --- a/extensions/configuration-editing/package.json +++ b/extensions/configuration-editing/package.json @@ -40,8 +40,12 @@ "keybindings.json", "extensions.json", "argv.json", - "profiles.json" - ] + "profiles.json", + ".devcontainer.json" + ], + "filenamePatterns": [ + "**/.devcontainer/devcontainer.json" + ] } ], "jsonValidation": [ 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..5804c797484 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -212,6 +212,16 @@ "type": "number", "default": 0.3, "description": "%emmetPreferencesCssFuzzySearchMinScore%" + }, + "output.reverseAttributes": { + "type": "boolean", + "default": false, + "description": "%emmetPreferencesOutputReverseAttributes%" + }, + "css.color.short": { + "type": "boolean", + "default": true, + "description": "%emmetPreferencesCssColorShort%" } } }, @@ -430,8 +440,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 +448,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..0c0d875d87c 100644 --- a/extensions/emmet/package.nls.json +++ b/extensions/emmet/package.nls.json @@ -55,5 +55,7 @@ "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.", + "emmetPreferencesCssColorShort": "If `true`, color values like #f will be expanded to #fff instead of #ffffff." } diff --git a/extensions/emmet/yarn.lock b/extensions/emmet/yarn.lock index bebce02cf48..117f331df39 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.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.1.tgz#63d36c10e162666f0107f247cdca76542c3c7472" + integrity sha512-tCkE96/ZTO+cWbln2xfyvd6ngHLanvVlJ3e5BeirJ3BYI5GbAyubIrmV4JjjugDly5D9fHjOL5MNsqsCnqwW6g== -"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.1" + resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-2.3.1.tgz#f4f1d3b656441d96ded901536c014dcbf2419d9c" + integrity sha512-05zamij6z60pF6z4Dg6csbypc/Uxi9mBPzaPkfjOwdkn6+lYSrsiOME74MZLE4rhyWd46bvEtuGxAuR6gRKDpw== 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/common/utils.ts b/extensions/github-authentication/src/common/utils.ts index b6fc3361315..6ebfe7b08b3 100644 --- a/extensions/github-authentication/src/common/utils.ts +++ b/extensions/github-authentication/src/common/utils.ts @@ -71,3 +71,25 @@ export async function promiseFromEvent( } ); } + +export function arrayEquals(one: ReadonlyArray | undefined, other: ReadonlyArray | undefined, itemEquals: (a: T, b: T) => boolean = (a, b) => a === b): boolean { + if (one === other) { + return true; + } + + if (!one || !other) { + return false; + } + + if (one.length !== other.length) { + return false; + } + + for (let i = 0, len = one.length; i < len; i++) { + if (!itemEquals(one[i], other[i])) { + return false; + } + } + + return true; +} diff --git a/extensions/github-authentication/src/extension.ts b/extensions/github-authentication/src/extension.ts index fd981fb3fe8..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,15 +24,15 @@ export async function activate(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.authentication.registerAuthenticationProvider('github', 'GitHub', { onDidChangeSessions: onDidChangeSessions.event, - getSessions: () => Promise.resolve(loginService.sessions), - login: async (scopeList: string[]) => { + getSessions: (scopes?: string[]) => loginService.getSessions(scopes), + createSession: async (scopeList: string[]) => { try { /* __GDPR__ "login" : { } */ telemetryReporter.sendTelemetryEvent('login'); - const session = await loginService.login(scopeList.sort().join(' ')); + const session = await loginService.createSession(scopeList.sort().join(' ')); Logger.info('Login success!'); onDidChangeSessions.fire({ added: [session], removed: [], changed: [] }); return session; @@ -56,14 +56,14 @@ export async function activate(context: vscode.ExtensionContext) { throw e; } }, - logout: async (id: string) => { + removeSession: async (id: string) => { try { /* __GDPR__ "logout" : { } */ telemetryReporter.sendTelemetryEvent('logout'); - const session = await loginService.logout(id); + const session = await loginService.removeSession(id); if (session) { onDidChangeSessions.fire({ added: [], removed: [session], changed: [] }); } diff --git a/extensions/github-authentication/src/github.ts b/extensions/github-authentication/src/github.ts index 250a7bdfd00..595d13147a1 100644 --- a/extensions/github-authentication/src/github.ts +++ b/extensions/github-authentication/src/github.ts @@ -8,6 +8,8 @@ import { v4 as uuid } from 'uuid'; 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(); @@ -24,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 { @@ -43,11 +46,18 @@ export class GitHubAuthenticationProvider { context.subscriptions.push(context.secrets.onDidChange(() => this.checkForUpdates())); } + async getSessions(scopes?: string[]): Promise { + return scopes + ? this._sessions.filter(session => arrayEquals(session.scopes, scopes)) + : this._sessions; + } + private async verifySessions(): Promise { const verifiedSessions: vscode.AuthenticationSession[] = []; 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 @@ -153,9 +163,10 @@ export class GitHubAuthenticationProvider { return this._sessions; } - public async login(scopes: string): Promise { + 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; } @@ -185,7 +196,7 @@ export class GitHubAuthenticationProvider { await this.storeSessions(); } - public async logout(id: string): Promise { + public async removeSession(id: string): Promise { Logger.info(`Logging out of ${id}`); const sessionIndex = this._sessions.findIndex(session => session.id === id); let session: vscode.AuthenticationSession | undefined; diff --git a/extensions/github-authentication/src/githubServer.ts b/extensions/github-authentication/src/githubServer.ts index b50285c9ab2..6a0ecfa357f 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": "NonIdentifiableDemographicInfo", "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/markdown-language-features/notebook-out/index.js b/extensions/markdown-language-features/notebook-out/index.js index 00ad3efad5c..66df08f4026 100644 --- a/extensions/markdown-language-features/notebook-out/index.js +++ b/extensions/markdown-language-features/notebook-out/index.js @@ -1,964 +1 @@ -/******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) { -/******/ return installedModules[moduleId].exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ i: moduleId, -/******/ l: false, -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Flag the module as loaded -/******/ module.l = true; -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; -/******/ -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; -/******/ -/******/ // define getter function for harmony exports -/******/ __webpack_require__.d = function(exports, name, getter) { -/******/ if(!__webpack_require__.o(exports, name)) { -/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); -/******/ } -/******/ }; -/******/ -/******/ // define __esModule on exports -/******/ __webpack_require__.r = function(exports) { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ -/******/ // create a fake namespace object -/******/ // mode & 1: value is a module id, require it -/******/ // mode & 2: merge all properties of value into the ns -/******/ // mode & 4: return value when already ns object -/******/ // mode & 8|1: behave like require -/******/ __webpack_require__.t = function(value, mode) { -/******/ if(mode & 1) value = __webpack_require__(value); -/******/ if(mode & 8) return value; -/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; -/******/ var ns = Object.create(null); -/******/ __webpack_require__.r(ns); -/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); -/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); -/******/ return ns; -/******/ }; -/******/ -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = function(module) { -/******/ var getter = module && module.__esModule ? -/******/ function getDefault() { return module['default']; } : -/******/ function getModuleExports() { return module; }; -/******/ __webpack_require__.d(getter, 'a', getter); -/******/ return getter; -/******/ }; -/******/ -/******/ // Object.prototype.hasOwnProperty.call -/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; -/******/ -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; -/******/ -/******/ -/******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = "./notebook/index.ts"); -/******/ }) -/************************************************************************/ -/******/ ({ - -/***/ "../../node_modules/punycode/punycode.js": -/*!*********************************************************************!*\ - !*** /Users/matb/projects/vscode/node_modules/punycode/punycode.js ***! - \*********************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -eval("/* WEBPACK VAR INJECTION */(function(module, global) {var __WEBPACK_AMD_DEFINE_RESULT__;/*! https://mths.be/punycode v1.4.1 by @mathias */\n;(function(root) {\n\n\t/** Detect free variables */\n\tvar freeExports = true && exports &&\n\t\t!exports.nodeType && exports;\n\tvar freeModule = true && module &&\n\t\t!module.nodeType && module;\n\tvar freeGlobal = typeof global == 'object' && global;\n\tif (\n\t\tfreeGlobal.global === freeGlobal ||\n\t\tfreeGlobal.window === freeGlobal ||\n\t\tfreeGlobal.self === freeGlobal\n\t) {\n\t\troot = freeGlobal;\n\t}\n\n\t/**\n\t * The `punycode` object.\n\t * @name punycode\n\t * @type Object\n\t */\n\tvar punycode,\n\n\t/** Highest positive signed 32-bit float value */\n\tmaxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1\n\n\t/** Bootstring parameters */\n\tbase = 36,\n\ttMin = 1,\n\ttMax = 26,\n\tskew = 38,\n\tdamp = 700,\n\tinitialBias = 72,\n\tinitialN = 128, // 0x80\n\tdelimiter = '-', // '\\x2D'\n\n\t/** Regular expressions */\n\tregexPunycode = /^xn--/,\n\tregexNonASCII = /[^\\x20-\\x7E]/, // unprintable ASCII chars + non-ASCII chars\n\tregexSeparators = /[\\x2E\\u3002\\uFF0E\\uFF61]/g, // RFC 3490 separators\n\n\t/** Error messages */\n\terrors = {\n\t\t'overflow': 'Overflow: input needs wider integers to process',\n\t\t'not-basic': 'Illegal input >= 0x80 (not a basic code point)',\n\t\t'invalid-input': 'Invalid input'\n\t},\n\n\t/** Convenience shortcuts */\n\tbaseMinusTMin = base - tMin,\n\tfloor = Math.floor,\n\tstringFromCharCode = String.fromCharCode,\n\n\t/** Temporary variable */\n\tkey;\n\n\t/*--------------------------------------------------------------------------*/\n\n\t/**\n\t * A generic error utility function.\n\t * @private\n\t * @param {String} type The error type.\n\t * @returns {Error} Throws a `RangeError` with the applicable error message.\n\t */\n\tfunction error(type) {\n\t\tthrow new RangeError(errors[type]);\n\t}\n\n\t/**\n\t * A generic `Array#map` utility function.\n\t * @private\n\t * @param {Array} array The array to iterate over.\n\t * @param {Function} callback The function that gets called for every array\n\t * item.\n\t * @returns {Array} A new array of values returned by the callback function.\n\t */\n\tfunction map(array, fn) {\n\t\tvar length = array.length;\n\t\tvar result = [];\n\t\twhile (length--) {\n\t\t\tresult[length] = fn(array[length]);\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * A simple `Array#map`-like wrapper to work with domain name strings or email\n\t * addresses.\n\t * @private\n\t * @param {String} domain The domain name or email address.\n\t * @param {Function} callback The function that gets called for every\n\t * character.\n\t * @returns {Array} A new string of characters returned by the callback\n\t * function.\n\t */\n\tfunction mapDomain(string, fn) {\n\t\tvar parts = string.split('@');\n\t\tvar result = '';\n\t\tif (parts.length > 1) {\n\t\t\t// In email addresses, only the domain name should be punycoded. Leave\n\t\t\t// the local part (i.e. everything up to `@`) intact.\n\t\t\tresult = parts[0] + '@';\n\t\t\tstring = parts[1];\n\t\t}\n\t\t// Avoid `split(regex)` for IE8 compatibility. See #17.\n\t\tstring = string.replace(regexSeparators, '\\x2E');\n\t\tvar labels = string.split('.');\n\t\tvar encoded = map(labels, fn).join('.');\n\t\treturn result + encoded;\n\t}\n\n\t/**\n\t * Creates an array containing the numeric code points of each Unicode\n\t * character in the string. While JavaScript uses UCS-2 internally,\n\t * this function will convert a pair of surrogate halves (each of which\n\t * UCS-2 exposes as separate characters) into a single code point,\n\t * matching UTF-16.\n\t * @see `punycode.ucs2.encode`\n\t * @see \n\t * @memberOf punycode.ucs2\n\t * @name decode\n\t * @param {String} string The Unicode input string (UCS-2).\n\t * @returns {Array} The new array of code points.\n\t */\n\tfunction ucs2decode(string) {\n\t\tvar output = [],\n\t\t counter = 0,\n\t\t length = string.length,\n\t\t value,\n\t\t extra;\n\t\twhile (counter < length) {\n\t\t\tvalue = string.charCodeAt(counter++);\n\t\t\tif (value >= 0xD800 && value <= 0xDBFF && counter < length) {\n\t\t\t\t// high surrogate, and there is a next character\n\t\t\t\textra = string.charCodeAt(counter++);\n\t\t\t\tif ((extra & 0xFC00) == 0xDC00) { // low surrogate\n\t\t\t\t\toutput.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);\n\t\t\t\t} else {\n\t\t\t\t\t// unmatched surrogate; only append this code unit, in case the next\n\t\t\t\t\t// code unit is the high surrogate of a surrogate pair\n\t\t\t\t\toutput.push(value);\n\t\t\t\t\tcounter--;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\toutput.push(value);\n\t\t\t}\n\t\t}\n\t\treturn output;\n\t}\n\n\t/**\n\t * Creates a string based on an array of numeric code points.\n\t * @see `punycode.ucs2.decode`\n\t * @memberOf punycode.ucs2\n\t * @name encode\n\t * @param {Array} codePoints The array of numeric code points.\n\t * @returns {String} The new Unicode string (UCS-2).\n\t */\n\tfunction ucs2encode(array) {\n\t\treturn map(array, function(value) {\n\t\t\tvar output = '';\n\t\t\tif (value > 0xFFFF) {\n\t\t\t\tvalue -= 0x10000;\n\t\t\t\toutput += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);\n\t\t\t\tvalue = 0xDC00 | value & 0x3FF;\n\t\t\t}\n\t\t\toutput += stringFromCharCode(value);\n\t\t\treturn output;\n\t\t}).join('');\n\t}\n\n\t/**\n\t * Converts a basic code point into a digit/integer.\n\t * @see `digitToBasic()`\n\t * @private\n\t * @param {Number} codePoint The basic numeric code point value.\n\t * @returns {Number} The numeric value of a basic code point (for use in\n\t * representing integers) in the range `0` to `base - 1`, or `base` if\n\t * the code point does not represent a value.\n\t */\n\tfunction basicToDigit(codePoint) {\n\t\tif (codePoint - 48 < 10) {\n\t\t\treturn codePoint - 22;\n\t\t}\n\t\tif (codePoint - 65 < 26) {\n\t\t\treturn codePoint - 65;\n\t\t}\n\t\tif (codePoint - 97 < 26) {\n\t\t\treturn codePoint - 97;\n\t\t}\n\t\treturn base;\n\t}\n\n\t/**\n\t * Converts a digit/integer into a basic code point.\n\t * @see `basicToDigit()`\n\t * @private\n\t * @param {Number} digit The numeric value of a basic code point.\n\t * @returns {Number} The basic code point whose value (when used for\n\t * representing integers) is `digit`, which needs to be in the range\n\t * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is\n\t * used; else, the lowercase form is used. The behavior is undefined\n\t * if `flag` is non-zero and `digit` has no uppercase form.\n\t */\n\tfunction digitToBasic(digit, flag) {\n\t\t// 0..25 map to ASCII a..z or A..Z\n\t\t// 26..35 map to ASCII 0..9\n\t\treturn digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);\n\t}\n\n\t/**\n\t * Bias adaptation function as per section 3.4 of RFC 3492.\n\t * https://tools.ietf.org/html/rfc3492#section-3.4\n\t * @private\n\t */\n\tfunction adapt(delta, numPoints, firstTime) {\n\t\tvar k = 0;\n\t\tdelta = firstTime ? floor(delta / damp) : delta >> 1;\n\t\tdelta += floor(delta / numPoints);\n\t\tfor (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {\n\t\t\tdelta = floor(delta / baseMinusTMin);\n\t\t}\n\t\treturn floor(k + (baseMinusTMin + 1) * delta / (delta + skew));\n\t}\n\n\t/**\n\t * Converts a Punycode string of ASCII-only symbols to a string of Unicode\n\t * symbols.\n\t * @memberOf punycode\n\t * @param {String} input The Punycode string of ASCII-only symbols.\n\t * @returns {String} The resulting string of Unicode symbols.\n\t */\n\tfunction decode(input) {\n\t\t// Don't use UCS-2\n\t\tvar output = [],\n\t\t inputLength = input.length,\n\t\t out,\n\t\t i = 0,\n\t\t n = initialN,\n\t\t bias = initialBias,\n\t\t basic,\n\t\t j,\n\t\t index,\n\t\t oldi,\n\t\t w,\n\t\t k,\n\t\t digit,\n\t\t t,\n\t\t /** Cached calculation results */\n\t\t baseMinusT;\n\n\t\t// Handle the basic code points: let `basic` be the number of input code\n\t\t// points before the last delimiter, or `0` if there is none, then copy\n\t\t// the first basic code points to the output.\n\n\t\tbasic = input.lastIndexOf(delimiter);\n\t\tif (basic < 0) {\n\t\t\tbasic = 0;\n\t\t}\n\n\t\tfor (j = 0; j < basic; ++j) {\n\t\t\t// if it's not a basic code point\n\t\t\tif (input.charCodeAt(j) >= 0x80) {\n\t\t\t\terror('not-basic');\n\t\t\t}\n\t\t\toutput.push(input.charCodeAt(j));\n\t\t}\n\n\t\t// Main decoding loop: start just after the last delimiter if any basic code\n\t\t// points were copied; start at the beginning otherwise.\n\n\t\tfor (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) {\n\n\t\t\t// `index` is the index of the next character to be consumed.\n\t\t\t// Decode a generalized variable-length integer into `delta`,\n\t\t\t// which gets added to `i`. The overflow checking is easier\n\t\t\t// if we increase `i` as we go, then subtract off its starting\n\t\t\t// value at the end to obtain `delta`.\n\t\t\tfor (oldi = i, w = 1, k = base; /* no condition */; k += base) {\n\n\t\t\t\tif (index >= inputLength) {\n\t\t\t\t\terror('invalid-input');\n\t\t\t\t}\n\n\t\t\t\tdigit = basicToDigit(input.charCodeAt(index++));\n\n\t\t\t\tif (digit >= base || digit > floor((maxInt - i) / w)) {\n\t\t\t\t\terror('overflow');\n\t\t\t\t}\n\n\t\t\t\ti += digit * w;\n\t\t\t\tt = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);\n\n\t\t\t\tif (digit < t) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tbaseMinusT = base - t;\n\t\t\t\tif (w > floor(maxInt / baseMinusT)) {\n\t\t\t\t\terror('overflow');\n\t\t\t\t}\n\n\t\t\t\tw *= baseMinusT;\n\n\t\t\t}\n\n\t\t\tout = output.length + 1;\n\t\t\tbias = adapt(i - oldi, out, oldi == 0);\n\n\t\t\t// `i` was supposed to wrap around from `out` to `0`,\n\t\t\t// incrementing `n` each time, so we'll fix that now:\n\t\t\tif (floor(i / out) > maxInt - n) {\n\t\t\t\terror('overflow');\n\t\t\t}\n\n\t\t\tn += floor(i / out);\n\t\t\ti %= out;\n\n\t\t\t// Insert `n` at position `i` of the output\n\t\t\toutput.splice(i++, 0, n);\n\n\t\t}\n\n\t\treturn ucs2encode(output);\n\t}\n\n\t/**\n\t * Converts a string of Unicode symbols (e.g. a domain name label) to a\n\t * Punycode string of ASCII-only symbols.\n\t * @memberOf punycode\n\t * @param {String} input The string of Unicode symbols.\n\t * @returns {String} The resulting Punycode string of ASCII-only symbols.\n\t */\n\tfunction encode(input) {\n\t\tvar n,\n\t\t delta,\n\t\t handledCPCount,\n\t\t basicLength,\n\t\t bias,\n\t\t j,\n\t\t m,\n\t\t q,\n\t\t k,\n\t\t t,\n\t\t currentValue,\n\t\t output = [],\n\t\t /** `inputLength` will hold the number of code points in `input`. */\n\t\t inputLength,\n\t\t /** Cached calculation results */\n\t\t handledCPCountPlusOne,\n\t\t baseMinusT,\n\t\t qMinusT;\n\n\t\t// Convert the input in UCS-2 to Unicode\n\t\tinput = ucs2decode(input);\n\n\t\t// Cache the length\n\t\tinputLength = input.length;\n\n\t\t// Initialize the state\n\t\tn = initialN;\n\t\tdelta = 0;\n\t\tbias = initialBias;\n\n\t\t// Handle the basic code points\n\t\tfor (j = 0; j < inputLength; ++j) {\n\t\t\tcurrentValue = input[j];\n\t\t\tif (currentValue < 0x80) {\n\t\t\t\toutput.push(stringFromCharCode(currentValue));\n\t\t\t}\n\t\t}\n\n\t\thandledCPCount = basicLength = output.length;\n\n\t\t// `handledCPCount` is the number of code points that have been handled;\n\t\t// `basicLength` is the number of basic code points.\n\n\t\t// Finish the basic string - if it is not empty - with a delimiter\n\t\tif (basicLength) {\n\t\t\toutput.push(delimiter);\n\t\t}\n\n\t\t// Main encoding loop:\n\t\twhile (handledCPCount < inputLength) {\n\n\t\t\t// All non-basic code points < n have been handled already. Find the next\n\t\t\t// larger one:\n\t\t\tfor (m = maxInt, j = 0; j < inputLength; ++j) {\n\t\t\t\tcurrentValue = input[j];\n\t\t\t\tif (currentValue >= n && currentValue < m) {\n\t\t\t\t\tm = currentValue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Increase `delta` enough to advance the decoder's state to ,\n\t\t\t// but guard against overflow\n\t\t\thandledCPCountPlusOne = handledCPCount + 1;\n\t\t\tif (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {\n\t\t\t\terror('overflow');\n\t\t\t}\n\n\t\t\tdelta += (m - n) * handledCPCountPlusOne;\n\t\t\tn = m;\n\n\t\t\tfor (j = 0; j < inputLength; ++j) {\n\t\t\t\tcurrentValue = input[j];\n\n\t\t\t\tif (currentValue < n && ++delta > maxInt) {\n\t\t\t\t\terror('overflow');\n\t\t\t\t}\n\n\t\t\t\tif (currentValue == n) {\n\t\t\t\t\t// Represent delta as a generalized variable-length integer\n\t\t\t\t\tfor (q = delta, k = base; /* no condition */; k += base) {\n\t\t\t\t\t\tt = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);\n\t\t\t\t\t\tif (q < t) {\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tqMinusT = q - t;\n\t\t\t\t\t\tbaseMinusT = base - t;\n\t\t\t\t\t\toutput.push(\n\t\t\t\t\t\t\tstringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))\n\t\t\t\t\t\t);\n\t\t\t\t\t\tq = floor(qMinusT / baseMinusT);\n\t\t\t\t\t}\n\n\t\t\t\t\toutput.push(stringFromCharCode(digitToBasic(q, 0)));\n\t\t\t\t\tbias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);\n\t\t\t\t\tdelta = 0;\n\t\t\t\t\t++handledCPCount;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t++delta;\n\t\t\t++n;\n\n\t\t}\n\t\treturn output.join('');\n\t}\n\n\t/**\n\t * Converts a Punycode string representing a domain name or an email address\n\t * to Unicode. Only the Punycoded parts of the input will be converted, i.e.\n\t * it doesn't matter if you call it on a string that has already been\n\t * converted to Unicode.\n\t * @memberOf punycode\n\t * @param {String} input The Punycoded domain name or email address to\n\t * convert to Unicode.\n\t * @returns {String} The Unicode representation of the given Punycode\n\t * string.\n\t */\n\tfunction toUnicode(input) {\n\t\treturn mapDomain(input, function(string) {\n\t\t\treturn regexPunycode.test(string)\n\t\t\t\t? decode(string.slice(4).toLowerCase())\n\t\t\t\t: string;\n\t\t});\n\t}\n\n\t/**\n\t * Converts a Unicode string representing a domain name or an email address to\n\t * Punycode. Only the non-ASCII parts of the domain name will be converted,\n\t * i.e. it doesn't matter if you call it with a domain that's already in\n\t * ASCII.\n\t * @memberOf punycode\n\t * @param {String} input The domain name or email address to convert, as a\n\t * Unicode string.\n\t * @returns {String} The Punycode representation of the given domain name or\n\t * email address.\n\t */\n\tfunction toASCII(input) {\n\t\treturn mapDomain(input, function(string) {\n\t\t\treturn regexNonASCII.test(string)\n\t\t\t\t? 'xn--' + encode(string)\n\t\t\t\t: string;\n\t\t});\n\t}\n\n\t/*--------------------------------------------------------------------------*/\n\n\t/** Define the public API */\n\tpunycode = {\n\t\t/**\n\t\t * A string representing the current Punycode.js version number.\n\t\t * @memberOf punycode\n\t\t * @type String\n\t\t */\n\t\t'version': '1.4.1',\n\t\t/**\n\t\t * An object of methods to convert from JavaScript's internal character\n\t\t * representation (UCS-2) to Unicode code points, and back.\n\t\t * @see \n\t\t * @memberOf punycode\n\t\t * @type Object\n\t\t */\n\t\t'ucs2': {\n\t\t\t'decode': ucs2decode,\n\t\t\t'encode': ucs2encode\n\t\t},\n\t\t'decode': decode,\n\t\t'encode': encode,\n\t\t'toASCII': toASCII,\n\t\t'toUnicode': toUnicode\n\t};\n\n\t/** Expose `punycode` */\n\t// Some AMD build optimizers, like r.js, check for specific condition patterns\n\t// like the following:\n\tif (\n\t\ttrue\n\t) {\n\t\t!(__WEBPACK_AMD_DEFINE_RESULT__ = (function() {\n\t\t\treturn punycode;\n\t\t}).call(exports, __webpack_require__, exports, module),\n\t\t\t\t__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t} else {}\n\n}(this));\n\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../webpack/buildin/module.js */ \"../../node_modules/webpack/buildin/module.js\")(module), __webpack_require__(/*! ./../webpack/buildin/global.js */ \"../../node_modules/webpack/buildin/global.js\")))\n\n//# sourceURL=webpack:////Users/matb/projects/vscode/node_modules/punycode/punycode.js?"); - -/***/ }), - -/***/ "../../node_modules/webpack/buildin/global.js": -/*!***********************************!*\ - !*** (webpack)/buildin/global.js ***! - \***********************************/ -/*! no static exports found */ -/***/ (function(module, exports) { - -eval("var g;\n\n// This works in non-strict mode\ng = (function() {\n\treturn this;\n})();\n\ntry {\n\t// This works if eval is allowed (see CSP)\n\tg = g || new Function(\"return this\")();\n} catch (e) {\n\t// This works if the window reference is available\n\tif (typeof window === \"object\") g = window;\n}\n\n// g can still be undefined, but nothing to do about it...\n// We return undefined, instead of nothing here, so it's\n// easier to handle this case. if(!global) { ...}\n\nmodule.exports = g;\n\n\n//# sourceURL=webpack:///(webpack)/buildin/global.js?"); - -/***/ }), - -/***/ "../../node_modules/webpack/buildin/module.js": -/*!***********************************!*\ - !*** (webpack)/buildin/module.js ***! - \***********************************/ -/*! no static exports found */ -/***/ (function(module, exports) { - -eval("module.exports = function(module) {\n\tif (!module.webpackPolyfill) {\n\t\tmodule.deprecate = function() {};\n\t\tmodule.paths = [];\n\t\t// module.parent = undefined by default\n\t\tif (!module.children) module.children = [];\n\t\tObject.defineProperty(module, \"loaded\", {\n\t\t\tenumerable: true,\n\t\t\tget: function() {\n\t\t\t\treturn module.l;\n\t\t\t}\n\t\t});\n\t\tObject.defineProperty(module, \"id\", {\n\t\t\tenumerable: true,\n\t\t\tget: function() {\n\t\t\t\treturn module.i;\n\t\t\t}\n\t\t});\n\t\tmodule.webpackPolyfill = 1;\n\t}\n\treturn module;\n};\n\n\n//# sourceURL=webpack:///(webpack)/buildin/module.js?"); - -/***/ }), - -/***/ "./node_modules/entities/lib/maps/entities.json": -/*!******************************************************!*\ - !*** ./node_modules/entities/lib/maps/entities.json ***! - \******************************************************/ -/*! exports provided: Aacute, aacute, Abreve, abreve, ac, acd, acE, Acirc, acirc, acute, Acy, acy, AElig, aelig, af, Afr, afr, Agrave, agrave, alefsym, aleph, Alpha, alpha, Amacr, amacr, amalg, amp, AMP, andand, And, and, andd, andslope, andv, ang, ange, angle, angmsdaa, angmsdab, angmsdac, angmsdad, angmsdae, angmsdaf, angmsdag, angmsdah, angmsd, angrt, angrtvb, angrtvbd, angsph, angst, angzarr, Aogon, aogon, Aopf, aopf, apacir, ap, apE, ape, apid, apos, ApplyFunction, approx, approxeq, Aring, aring, Ascr, ascr, Assign, ast, asymp, asympeq, Atilde, atilde, Auml, auml, awconint, awint, backcong, backepsilon, backprime, backsim, backsimeq, Backslash, Barv, barvee, barwed, Barwed, barwedge, bbrk, bbrktbrk, bcong, Bcy, bcy, bdquo, becaus, because, Because, bemptyv, bepsi, bernou, Bernoullis, Beta, beta, beth, between, Bfr, bfr, bigcap, bigcirc, bigcup, bigodot, bigoplus, bigotimes, bigsqcup, bigstar, bigtriangledown, bigtriangleup, biguplus, bigvee, bigwedge, bkarow, blacklozenge, blacksquare, blacktriangle, blacktriangledown, blacktriangleleft, blacktriangleright, blank, blk12, blk14, blk34, block, bne, bnequiv, bNot, bnot, Bopf, bopf, bot, bottom, bowtie, boxbox, boxdl, boxdL, boxDl, boxDL, boxdr, boxdR, boxDr, boxDR, boxh, boxH, boxhd, boxHd, boxhD, boxHD, boxhu, boxHu, boxhU, boxHU, boxminus, boxplus, boxtimes, boxul, boxuL, boxUl, boxUL, boxur, boxuR, boxUr, boxUR, boxv, boxV, boxvh, boxvH, boxVh, boxVH, boxvl, boxvL, boxVl, boxVL, boxvr, boxvR, boxVr, boxVR, bprime, breve, Breve, brvbar, bscr, Bscr, bsemi, bsim, bsime, bsolb, bsol, bsolhsub, bull, bullet, bump, bumpE, bumpe, Bumpeq, bumpeq, Cacute, cacute, capand, capbrcup, capcap, cap, Cap, capcup, capdot, CapitalDifferentialD, caps, caret, caron, Cayleys, ccaps, Ccaron, ccaron, Ccedil, ccedil, Ccirc, ccirc, Cconint, ccups, ccupssm, Cdot, cdot, cedil, Cedilla, cemptyv, cent, centerdot, CenterDot, cfr, Cfr, CHcy, chcy, check, checkmark, Chi, chi, circ, circeq, circlearrowleft, circlearrowright, circledast, circledcirc, circleddash, CircleDot, circledR, circledS, CircleMinus, CirclePlus, CircleTimes, cir, cirE, cire, cirfnint, cirmid, cirscir, ClockwiseContourIntegral, CloseCurlyDoubleQuote, CloseCurlyQuote, clubs, clubsuit, colon, Colon, Colone, colone, coloneq, comma, commat, comp, compfn, complement, complexes, cong, congdot, Congruent, conint, Conint, ContourIntegral, copf, Copf, coprod, Coproduct, copy, COPY, copysr, CounterClockwiseContourIntegral, crarr, cross, Cross, Cscr, cscr, csub, csube, csup, csupe, ctdot, cudarrl, cudarrr, cuepr, cuesc, cularr, cularrp, cupbrcap, cupcap, CupCap, cup, Cup, cupcup, cupdot, cupor, cups, curarr, curarrm, curlyeqprec, curlyeqsucc, curlyvee, curlywedge, curren, curvearrowleft, curvearrowright, cuvee, cuwed, cwconint, cwint, cylcty, dagger, Dagger, daleth, darr, Darr, dArr, dash, Dashv, dashv, dbkarow, dblac, Dcaron, dcaron, Dcy, dcy, ddagger, ddarr, DD, dd, DDotrahd, ddotseq, deg, Del, Delta, delta, demptyv, dfisht, Dfr, dfr, dHar, dharl, dharr, DiacriticalAcute, DiacriticalDot, DiacriticalDoubleAcute, DiacriticalGrave, DiacriticalTilde, diam, diamond, Diamond, diamondsuit, diams, die, DifferentialD, digamma, disin, div, divide, divideontimes, divonx, DJcy, djcy, dlcorn, dlcrop, dollar, Dopf, dopf, Dot, dot, DotDot, doteq, doteqdot, DotEqual, dotminus, dotplus, dotsquare, doublebarwedge, DoubleContourIntegral, DoubleDot, DoubleDownArrow, DoubleLeftArrow, DoubleLeftRightArrow, DoubleLeftTee, DoubleLongLeftArrow, DoubleLongLeftRightArrow, DoubleLongRightArrow, DoubleRightArrow, DoubleRightTee, DoubleUpArrow, DoubleUpDownArrow, DoubleVerticalBar, DownArrowBar, downarrow, DownArrow, Downarrow, DownArrowUpArrow, DownBreve, downdownarrows, downharpoonleft, downharpoonright, DownLeftRightVector, DownLeftTeeVector, DownLeftVectorBar, DownLeftVector, DownRightTeeVector, DownRightVectorBar, DownRightVector, DownTeeArrow, DownTee, drbkarow, drcorn, drcrop, Dscr, dscr, DScy, dscy, dsol, Dstrok, dstrok, dtdot, dtri, dtrif, duarr, duhar, dwangle, DZcy, dzcy, dzigrarr, Eacute, eacute, easter, Ecaron, ecaron, Ecirc, ecirc, ecir, ecolon, Ecy, ecy, eDDot, Edot, edot, eDot, ee, efDot, Efr, efr, eg, Egrave, egrave, egs, egsdot, el, Element, elinters, ell, els, elsdot, Emacr, emacr, empty, emptyset, EmptySmallSquare, emptyv, EmptyVerySmallSquare, emsp13, emsp14, emsp, ENG, eng, ensp, Eogon, eogon, Eopf, eopf, epar, eparsl, eplus, epsi, Epsilon, epsilon, epsiv, eqcirc, eqcolon, eqsim, eqslantgtr, eqslantless, Equal, equals, EqualTilde, equest, Equilibrium, equiv, equivDD, eqvparsl, erarr, erDot, escr, Escr, esdot, Esim, esim, Eta, eta, ETH, eth, Euml, euml, euro, excl, exist, Exists, expectation, exponentiale, ExponentialE, fallingdotseq, Fcy, fcy, female, ffilig, fflig, ffllig, Ffr, ffr, filig, FilledSmallSquare, FilledVerySmallSquare, fjlig, flat, fllig, fltns, fnof, Fopf, fopf, forall, ForAll, fork, forkv, Fouriertrf, fpartint, frac12, frac13, frac14, frac15, frac16, frac18, frac23, frac25, frac34, frac35, frac38, frac45, frac56, frac58, frac78, frasl, frown, fscr, Fscr, gacute, Gamma, gamma, Gammad, gammad, gap, Gbreve, gbreve, Gcedil, Gcirc, gcirc, Gcy, gcy, Gdot, gdot, ge, gE, gEl, gel, geq, geqq, geqslant, gescc, ges, gesdot, gesdoto, gesdotol, gesl, gesles, Gfr, gfr, gg, Gg, ggg, gimel, GJcy, gjcy, gla, gl, glE, glj, gnap, gnapprox, gne, gnE, gneq, gneqq, gnsim, Gopf, gopf, grave, GreaterEqual, GreaterEqualLess, GreaterFullEqual, GreaterGreater, GreaterLess, GreaterSlantEqual, GreaterTilde, Gscr, gscr, gsim, gsime, gsiml, gtcc, gtcir, gt, GT, Gt, gtdot, gtlPar, gtquest, gtrapprox, gtrarr, gtrdot, gtreqless, gtreqqless, gtrless, gtrsim, gvertneqq, gvnE, Hacek, hairsp, half, hamilt, HARDcy, hardcy, harrcir, harr, hArr, harrw, Hat, hbar, Hcirc, hcirc, hearts, heartsuit, hellip, hercon, hfr, Hfr, HilbertSpace, hksearow, hkswarow, hoarr, homtht, hookleftarrow, hookrightarrow, hopf, Hopf, horbar, HorizontalLine, hscr, Hscr, hslash, Hstrok, hstrok, HumpDownHump, HumpEqual, hybull, hyphen, Iacute, iacute, ic, Icirc, icirc, Icy, icy, Idot, IEcy, iecy, iexcl, iff, ifr, Ifr, Igrave, igrave, ii, iiiint, iiint, iinfin, iiota, IJlig, ijlig, Imacr, imacr, image, ImaginaryI, imagline, imagpart, imath, Im, imof, imped, Implies, incare, in, infin, infintie, inodot, intcal, int, Int, integers, Integral, intercal, Intersection, intlarhk, intprod, InvisibleComma, InvisibleTimes, IOcy, iocy, Iogon, iogon, Iopf, iopf, Iota, iota, iprod, iquest, iscr, Iscr, isin, isindot, isinE, isins, isinsv, isinv, it, Itilde, itilde, Iukcy, iukcy, Iuml, iuml, Jcirc, jcirc, Jcy, jcy, Jfr, jfr, jmath, Jopf, jopf, Jscr, jscr, Jsercy, jsercy, Jukcy, jukcy, Kappa, kappa, kappav, Kcedil, kcedil, Kcy, kcy, Kfr, kfr, kgreen, KHcy, khcy, KJcy, kjcy, Kopf, kopf, Kscr, kscr, lAarr, Lacute, lacute, laemptyv, lagran, Lambda, lambda, lang, Lang, langd, langle, lap, Laplacetrf, laquo, larrb, larrbfs, larr, Larr, lArr, larrfs, larrhk, larrlp, larrpl, larrsim, larrtl, latail, lAtail, lat, late, lates, lbarr, lBarr, lbbrk, lbrace, lbrack, lbrke, lbrksld, lbrkslu, Lcaron, lcaron, Lcedil, lcedil, lceil, lcub, Lcy, lcy, ldca, ldquo, ldquor, ldrdhar, ldrushar, ldsh, le, lE, LeftAngleBracket, LeftArrowBar, leftarrow, LeftArrow, Leftarrow, LeftArrowRightArrow, leftarrowtail, LeftCeiling, LeftDoubleBracket, LeftDownTeeVector, LeftDownVectorBar, LeftDownVector, LeftFloor, leftharpoondown, leftharpoonup, leftleftarrows, leftrightarrow, LeftRightArrow, Leftrightarrow, leftrightarrows, leftrightharpoons, leftrightsquigarrow, LeftRightVector, LeftTeeArrow, LeftTee, LeftTeeVector, leftthreetimes, LeftTriangleBar, LeftTriangle, LeftTriangleEqual, LeftUpDownVector, LeftUpTeeVector, LeftUpVectorBar, LeftUpVector, LeftVectorBar, LeftVector, lEg, leg, leq, leqq, leqslant, lescc, les, lesdot, lesdoto, lesdotor, lesg, lesges, lessapprox, lessdot, lesseqgtr, lesseqqgtr, LessEqualGreater, LessFullEqual, LessGreater, lessgtr, LessLess, lesssim, LessSlantEqual, LessTilde, lfisht, lfloor, Lfr, lfr, lg, lgE, lHar, lhard, lharu, lharul, lhblk, LJcy, ljcy, llarr, ll, Ll, llcorner, Lleftarrow, llhard, lltri, Lmidot, lmidot, lmoustache, lmoust, lnap, lnapprox, lne, lnE, lneq, lneqq, lnsim, loang, loarr, lobrk, longleftarrow, LongLeftArrow, Longleftarrow, longleftrightarrow, LongLeftRightArrow, Longleftrightarrow, longmapsto, longrightarrow, LongRightArrow, Longrightarrow, looparrowleft, looparrowright, lopar, Lopf, lopf, loplus, lotimes, lowast, lowbar, LowerLeftArrow, LowerRightArrow, loz, lozenge, lozf, lpar, lparlt, lrarr, lrcorner, lrhar, lrhard, lrm, lrtri, lsaquo, lscr, Lscr, lsh, Lsh, lsim, lsime, lsimg, lsqb, lsquo, lsquor, Lstrok, lstrok, ltcc, ltcir, lt, LT, Lt, ltdot, lthree, ltimes, ltlarr, ltquest, ltri, ltrie, ltrif, ltrPar, lurdshar, luruhar, lvertneqq, lvnE, macr, male, malt, maltese, Map, map, mapsto, mapstodown, mapstoleft, mapstoup, marker, mcomma, Mcy, mcy, mdash, mDDot, measuredangle, MediumSpace, Mellintrf, Mfr, mfr, mho, micro, midast, midcir, mid, middot, minusb, minus, minusd, minusdu, MinusPlus, mlcp, mldr, mnplus, models, Mopf, mopf, mp, mscr, Mscr, mstpos, Mu, mu, multimap, mumap, nabla, Nacute, nacute, nang, nap, napE, napid, napos, napprox, natural, naturals, natur, nbsp, nbump, nbumpe, ncap, Ncaron, ncaron, Ncedil, ncedil, ncong, ncongdot, ncup, Ncy, ncy, ndash, nearhk, nearr, neArr, nearrow, ne, nedot, NegativeMediumSpace, NegativeThickSpace, NegativeThinSpace, NegativeVeryThinSpace, nequiv, nesear, nesim, NestedGreaterGreater, NestedLessLess, NewLine, nexist, nexists, Nfr, nfr, ngE, nge, ngeq, ngeqq, ngeqslant, nges, nGg, ngsim, nGt, ngt, ngtr, nGtv, nharr, nhArr, nhpar, ni, nis, nisd, niv, NJcy, njcy, nlarr, nlArr, nldr, nlE, nle, nleftarrow, nLeftarrow, nleftrightarrow, nLeftrightarrow, nleq, nleqq, nleqslant, nles, nless, nLl, nlsim, nLt, nlt, nltri, nltrie, nLtv, nmid, NoBreak, NonBreakingSpace, nopf, Nopf, Not, not, NotCongruent, NotCupCap, NotDoubleVerticalBar, NotElement, NotEqual, NotEqualTilde, NotExists, NotGreater, NotGreaterEqual, NotGreaterFullEqual, NotGreaterGreater, NotGreaterLess, NotGreaterSlantEqual, NotGreaterTilde, NotHumpDownHump, NotHumpEqual, notin, notindot, notinE, notinva, notinvb, notinvc, NotLeftTriangleBar, NotLeftTriangle, NotLeftTriangleEqual, NotLess, NotLessEqual, NotLessGreater, NotLessLess, NotLessSlantEqual, NotLessTilde, NotNestedGreaterGreater, NotNestedLessLess, notni, notniva, notnivb, notnivc, NotPrecedes, NotPrecedesEqual, NotPrecedesSlantEqual, NotReverseElement, NotRightTriangleBar, NotRightTriangle, NotRightTriangleEqual, NotSquareSubset, NotSquareSubsetEqual, NotSquareSuperset, NotSquareSupersetEqual, NotSubset, NotSubsetEqual, NotSucceeds, NotSucceedsEqual, NotSucceedsSlantEqual, NotSucceedsTilde, NotSuperset, NotSupersetEqual, NotTilde, NotTildeEqual, NotTildeFullEqual, NotTildeTilde, NotVerticalBar, nparallel, npar, nparsl, npart, npolint, npr, nprcue, nprec, npreceq, npre, nrarrc, nrarr, nrArr, nrarrw, nrightarrow, nRightarrow, nrtri, nrtrie, nsc, nsccue, nsce, Nscr, nscr, nshortmid, nshortparallel, nsim, nsime, nsimeq, nsmid, nspar, nsqsube, nsqsupe, nsub, nsubE, nsube, nsubset, nsubseteq, nsubseteqq, nsucc, nsucceq, nsup, nsupE, nsupe, nsupset, nsupseteq, nsupseteqq, ntgl, Ntilde, ntilde, ntlg, ntriangleleft, ntrianglelefteq, ntriangleright, ntrianglerighteq, Nu, nu, num, numero, numsp, nvap, nvdash, nvDash, nVdash, nVDash, nvge, nvgt, nvHarr, nvinfin, nvlArr, nvle, nvlt, nvltrie, nvrArr, nvrtrie, nvsim, nwarhk, nwarr, nwArr, nwarrow, nwnear, Oacute, oacute, oast, Ocirc, ocirc, ocir, Ocy, ocy, odash, Odblac, odblac, odiv, odot, odsold, OElig, oelig, ofcir, Ofr, ofr, ogon, Ograve, ograve, ogt, ohbar, ohm, oint, olarr, olcir, olcross, oline, olt, Omacr, omacr, Omega, omega, Omicron, omicron, omid, ominus, Oopf, oopf, opar, OpenCurlyDoubleQuote, OpenCurlyQuote, operp, oplus, orarr, Or, or, ord, order, orderof, ordf, ordm, origof, oror, orslope, orv, oS, Oscr, oscr, Oslash, oslash, osol, Otilde, otilde, otimesas, Otimes, otimes, Ouml, ouml, ovbar, OverBar, OverBrace, OverBracket, OverParenthesis, para, parallel, par, parsim, parsl, part, PartialD, Pcy, pcy, percnt, period, permil, perp, pertenk, Pfr, pfr, Phi, phi, phiv, phmmat, phone, Pi, pi, pitchfork, piv, planck, planckh, plankv, plusacir, plusb, pluscir, plus, plusdo, plusdu, pluse, PlusMinus, plusmn, plussim, plustwo, pm, Poincareplane, pointint, popf, Popf, pound, prap, Pr, pr, prcue, precapprox, prec, preccurlyeq, Precedes, PrecedesEqual, PrecedesSlantEqual, PrecedesTilde, preceq, precnapprox, precneqq, precnsim, pre, prE, precsim, prime, Prime, primes, prnap, prnE, prnsim, prod, Product, profalar, profline, profsurf, prop, Proportional, Proportion, propto, prsim, prurel, Pscr, pscr, Psi, psi, puncsp, Qfr, qfr, qint, qopf, Qopf, qprime, Qscr, qscr, quaternions, quatint, quest, questeq, quot, QUOT, rAarr, race, Racute, racute, radic, raemptyv, rang, Rang, rangd, range, rangle, raquo, rarrap, rarrb, rarrbfs, rarrc, rarr, Rarr, rArr, rarrfs, rarrhk, rarrlp, rarrpl, rarrsim, Rarrtl, rarrtl, rarrw, ratail, rAtail, ratio, rationals, rbarr, rBarr, RBarr, rbbrk, rbrace, rbrack, rbrke, rbrksld, rbrkslu, Rcaron, rcaron, Rcedil, rcedil, rceil, rcub, Rcy, rcy, rdca, rdldhar, rdquo, rdquor, rdsh, real, realine, realpart, reals, Re, rect, reg, REG, ReverseElement, ReverseEquilibrium, ReverseUpEquilibrium, rfisht, rfloor, rfr, Rfr, rHar, rhard, rharu, rharul, Rho, rho, rhov, RightAngleBracket, RightArrowBar, rightarrow, RightArrow, Rightarrow, RightArrowLeftArrow, rightarrowtail, RightCeiling, RightDoubleBracket, RightDownTeeVector, RightDownVectorBar, RightDownVector, RightFloor, rightharpoondown, rightharpoonup, rightleftarrows, rightleftharpoons, rightrightarrows, rightsquigarrow, RightTeeArrow, RightTee, RightTeeVector, rightthreetimes, RightTriangleBar, RightTriangle, RightTriangleEqual, RightUpDownVector, RightUpTeeVector, RightUpVectorBar, RightUpVector, RightVectorBar, RightVector, ring, risingdotseq, rlarr, rlhar, rlm, rmoustache, rmoust, rnmid, roang, roarr, robrk, ropar, ropf, Ropf, roplus, rotimes, RoundImplies, rpar, rpargt, rppolint, rrarr, Rrightarrow, rsaquo, rscr, Rscr, rsh, Rsh, rsqb, rsquo, rsquor, rthree, rtimes, rtri, rtrie, rtrif, rtriltri, RuleDelayed, ruluhar, rx, Sacute, sacute, sbquo, scap, Scaron, scaron, Sc, sc, sccue, sce, scE, Scedil, scedil, Scirc, scirc, scnap, scnE, scnsim, scpolint, scsim, Scy, scy, sdotb, sdot, sdote, searhk, searr, seArr, searrow, sect, semi, seswar, setminus, setmn, sext, Sfr, sfr, sfrown, sharp, SHCHcy, shchcy, SHcy, shcy, ShortDownArrow, ShortLeftArrow, shortmid, shortparallel, ShortRightArrow, ShortUpArrow, shy, Sigma, sigma, sigmaf, sigmav, sim, simdot, sime, simeq, simg, simgE, siml, simlE, simne, simplus, simrarr, slarr, SmallCircle, smallsetminus, smashp, smeparsl, smid, smile, smt, smte, smtes, SOFTcy, softcy, solbar, solb, sol, Sopf, sopf, spades, spadesuit, spar, sqcap, sqcaps, sqcup, sqcups, Sqrt, sqsub, sqsube, sqsubset, sqsubseteq, sqsup, sqsupe, sqsupset, sqsupseteq, square, Square, SquareIntersection, SquareSubset, SquareSubsetEqual, SquareSuperset, SquareSupersetEqual, SquareUnion, squarf, squ, squf, srarr, Sscr, sscr, ssetmn, ssmile, sstarf, Star, star, starf, straightepsilon, straightphi, strns, sub, Sub, subdot, subE, sube, subedot, submult, subnE, subne, subplus, subrarr, subset, Subset, subseteq, subseteqq, SubsetEqual, subsetneq, subsetneqq, subsim, subsub, subsup, succapprox, succ, succcurlyeq, Succeeds, SucceedsEqual, SucceedsSlantEqual, SucceedsTilde, succeq, succnapprox, succneqq, succnsim, succsim, SuchThat, sum, Sum, sung, sup1, sup2, sup3, sup, Sup, supdot, supdsub, supE, supe, supedot, Superset, SupersetEqual, suphsol, suphsub, suplarr, supmult, supnE, supne, supplus, supset, Supset, supseteq, supseteqq, supsetneq, supsetneqq, supsim, supsub, supsup, swarhk, swarr, swArr, swarrow, swnwar, szlig, Tab, target, Tau, tau, tbrk, Tcaron, tcaron, Tcedil, tcedil, Tcy, tcy, tdot, telrec, Tfr, tfr, there4, therefore, Therefore, Theta, theta, thetasym, thetav, thickapprox, thicksim, ThickSpace, ThinSpace, thinsp, thkap, thksim, THORN, thorn, tilde, Tilde, TildeEqual, TildeFullEqual, TildeTilde, timesbar, timesb, times, timesd, tint, toea, topbot, topcir, top, Topf, topf, topfork, tosa, tprime, trade, TRADE, triangle, triangledown, triangleleft, trianglelefteq, triangleq, triangleright, trianglerighteq, tridot, trie, triminus, TripleDot, triplus, trisb, tritime, trpezium, Tscr, tscr, TScy, tscy, TSHcy, tshcy, Tstrok, tstrok, twixt, twoheadleftarrow, twoheadrightarrow, Uacute, uacute, uarr, Uarr, uArr, Uarrocir, Ubrcy, ubrcy, Ubreve, ubreve, Ucirc, ucirc, Ucy, ucy, udarr, Udblac, udblac, udhar, ufisht, Ufr, ufr, Ugrave, ugrave, uHar, uharl, uharr, uhblk, ulcorn, ulcorner, ulcrop, ultri, Umacr, umacr, uml, UnderBar, UnderBrace, UnderBracket, UnderParenthesis, Union, UnionPlus, Uogon, uogon, Uopf, uopf, UpArrowBar, uparrow, UpArrow, Uparrow, UpArrowDownArrow, updownarrow, UpDownArrow, Updownarrow, UpEquilibrium, upharpoonleft, upharpoonright, uplus, UpperLeftArrow, UpperRightArrow, upsi, Upsi, upsih, Upsilon, upsilon, UpTeeArrow, UpTee, upuparrows, urcorn, urcorner, urcrop, Uring, uring, urtri, Uscr, uscr, utdot, Utilde, utilde, utri, utrif, uuarr, Uuml, uuml, uwangle, vangrt, varepsilon, varkappa, varnothing, varphi, varpi, varpropto, varr, vArr, varrho, varsigma, varsubsetneq, varsubsetneqq, varsupsetneq, varsupsetneqq, vartheta, vartriangleleft, vartriangleright, vBar, Vbar, vBarv, Vcy, vcy, vdash, vDash, Vdash, VDash, Vdashl, veebar, vee, Vee, veeeq, vellip, verbar, Verbar, vert, Vert, VerticalBar, VerticalLine, VerticalSeparator, VerticalTilde, VeryThinSpace, Vfr, vfr, vltri, vnsub, vnsup, Vopf, vopf, vprop, vrtri, Vscr, vscr, vsubnE, vsubne, vsupnE, vsupne, Vvdash, vzigzag, Wcirc, wcirc, wedbar, wedge, Wedge, wedgeq, weierp, Wfr, wfr, Wopf, wopf, wp, wr, wreath, Wscr, wscr, xcap, xcirc, xcup, xdtri, Xfr, xfr, xharr, xhArr, Xi, xi, xlarr, xlArr, xmap, xnis, xodot, Xopf, xopf, xoplus, xotime, xrarr, xrArr, Xscr, xscr, xsqcup, xuplus, xutri, xvee, xwedge, Yacute, yacute, YAcy, yacy, Ycirc, ycirc, Ycy, ycy, yen, Yfr, yfr, YIcy, yicy, Yopf, yopf, Yscr, yscr, YUcy, yucy, yuml, Yuml, Zacute, zacute, Zcaron, zcaron, Zcy, zcy, Zdot, zdot, zeetrf, ZeroWidthSpace, Zeta, zeta, zfr, Zfr, ZHcy, zhcy, zigrarr, zopf, Zopf, Zscr, zscr, zwj, zwnj, default */ -/***/ (function(module) { - -eval("module.exports = JSON.parse(\"{\\\"Aacute\\\":\\\"Á\\\",\\\"aacute\\\":\\\"á\\\",\\\"Abreve\\\":\\\"Ă\\\",\\\"abreve\\\":\\\"ă\\\",\\\"ac\\\":\\\"∾\\\",\\\"acd\\\":\\\"∿\\\",\\\"acE\\\":\\\"∾̳\\\",\\\"Acirc\\\":\\\"Â\\\",\\\"acirc\\\":\\\"â\\\",\\\"acute\\\":\\\"´\\\",\\\"Acy\\\":\\\"А\\\",\\\"acy\\\":\\\"а\\\",\\\"AElig\\\":\\\"Æ\\\",\\\"aelig\\\":\\\"æ\\\",\\\"af\\\":\\\"⁡\\\",\\\"Afr\\\":\\\"𝔄\\\",\\\"afr\\\":\\\"𝔞\\\",\\\"Agrave\\\":\\\"À\\\",\\\"agrave\\\":\\\"à\\\",\\\"alefsym\\\":\\\"ℵ\\\",\\\"aleph\\\":\\\"ℵ\\\",\\\"Alpha\\\":\\\"Α\\\",\\\"alpha\\\":\\\"α\\\",\\\"Amacr\\\":\\\"Ā\\\",\\\"amacr\\\":\\\"ā\\\",\\\"amalg\\\":\\\"⨿\\\",\\\"amp\\\":\\\"&\\\",\\\"AMP\\\":\\\"&\\\",\\\"andand\\\":\\\"⩕\\\",\\\"And\\\":\\\"⩓\\\",\\\"and\\\":\\\"∧\\\",\\\"andd\\\":\\\"⩜\\\",\\\"andslope\\\":\\\"⩘\\\",\\\"andv\\\":\\\"⩚\\\",\\\"ang\\\":\\\"∠\\\",\\\"ange\\\":\\\"⦤\\\",\\\"angle\\\":\\\"∠\\\",\\\"angmsdaa\\\":\\\"⦨\\\",\\\"angmsdab\\\":\\\"⦩\\\",\\\"angmsdac\\\":\\\"⦪\\\",\\\"angmsdad\\\":\\\"⦫\\\",\\\"angmsdae\\\":\\\"⦬\\\",\\\"angmsdaf\\\":\\\"⦭\\\",\\\"angmsdag\\\":\\\"⦮\\\",\\\"angmsdah\\\":\\\"⦯\\\",\\\"angmsd\\\":\\\"∡\\\",\\\"angrt\\\":\\\"∟\\\",\\\"angrtvb\\\":\\\"⊾\\\",\\\"angrtvbd\\\":\\\"⦝\\\",\\\"angsph\\\":\\\"∢\\\",\\\"angst\\\":\\\"Å\\\",\\\"angzarr\\\":\\\"⍼\\\",\\\"Aogon\\\":\\\"Ą\\\",\\\"aogon\\\":\\\"ą\\\",\\\"Aopf\\\":\\\"𝔸\\\",\\\"aopf\\\":\\\"𝕒\\\",\\\"apacir\\\":\\\"⩯\\\",\\\"ap\\\":\\\"≈\\\",\\\"apE\\\":\\\"⩰\\\",\\\"ape\\\":\\\"≊\\\",\\\"apid\\\":\\\"≋\\\",\\\"apos\\\":\\\"'\\\",\\\"ApplyFunction\\\":\\\"⁡\\\",\\\"approx\\\":\\\"≈\\\",\\\"approxeq\\\":\\\"≊\\\",\\\"Aring\\\":\\\"Å\\\",\\\"aring\\\":\\\"å\\\",\\\"Ascr\\\":\\\"𝒜\\\",\\\"ascr\\\":\\\"𝒶\\\",\\\"Assign\\\":\\\"≔\\\",\\\"ast\\\":\\\"*\\\",\\\"asymp\\\":\\\"≈\\\",\\\"asympeq\\\":\\\"≍\\\",\\\"Atilde\\\":\\\"Ã\\\",\\\"atilde\\\":\\\"ã\\\",\\\"Auml\\\":\\\"Ä\\\",\\\"auml\\\":\\\"ä\\\",\\\"awconint\\\":\\\"∳\\\",\\\"awint\\\":\\\"⨑\\\",\\\"backcong\\\":\\\"≌\\\",\\\"backepsilon\\\":\\\"϶\\\",\\\"backprime\\\":\\\"‵\\\",\\\"backsim\\\":\\\"∽\\\",\\\"backsimeq\\\":\\\"⋍\\\",\\\"Backslash\\\":\\\"∖\\\",\\\"Barv\\\":\\\"⫧\\\",\\\"barvee\\\":\\\"⊽\\\",\\\"barwed\\\":\\\"⌅\\\",\\\"Barwed\\\":\\\"⌆\\\",\\\"barwedge\\\":\\\"⌅\\\",\\\"bbrk\\\":\\\"⎵\\\",\\\"bbrktbrk\\\":\\\"⎶\\\",\\\"bcong\\\":\\\"≌\\\",\\\"Bcy\\\":\\\"Б\\\",\\\"bcy\\\":\\\"б\\\",\\\"bdquo\\\":\\\"„\\\",\\\"becaus\\\":\\\"∵\\\",\\\"because\\\":\\\"∵\\\",\\\"Because\\\":\\\"∵\\\",\\\"bemptyv\\\":\\\"⦰\\\",\\\"bepsi\\\":\\\"϶\\\",\\\"bernou\\\":\\\"ℬ\\\",\\\"Bernoullis\\\":\\\"ℬ\\\",\\\"Beta\\\":\\\"Β\\\",\\\"beta\\\":\\\"β\\\",\\\"beth\\\":\\\"ℶ\\\",\\\"between\\\":\\\"≬\\\",\\\"Bfr\\\":\\\"𝔅\\\",\\\"bfr\\\":\\\"𝔟\\\",\\\"bigcap\\\":\\\"⋂\\\",\\\"bigcirc\\\":\\\"◯\\\",\\\"bigcup\\\":\\\"⋃\\\",\\\"bigodot\\\":\\\"⨀\\\",\\\"bigoplus\\\":\\\"⨁\\\",\\\"bigotimes\\\":\\\"⨂\\\",\\\"bigsqcup\\\":\\\"⨆\\\",\\\"bigstar\\\":\\\"★\\\",\\\"bigtriangledown\\\":\\\"▽\\\",\\\"bigtriangleup\\\":\\\"△\\\",\\\"biguplus\\\":\\\"⨄\\\",\\\"bigvee\\\":\\\"⋁\\\",\\\"bigwedge\\\":\\\"⋀\\\",\\\"bkarow\\\":\\\"⤍\\\",\\\"blacklozenge\\\":\\\"⧫\\\",\\\"blacksquare\\\":\\\"▪\\\",\\\"blacktriangle\\\":\\\"▴\\\",\\\"blacktriangledown\\\":\\\"▾\\\",\\\"blacktriangleleft\\\":\\\"◂\\\",\\\"blacktriangleright\\\":\\\"▸\\\",\\\"blank\\\":\\\"␣\\\",\\\"blk12\\\":\\\"▒\\\",\\\"blk14\\\":\\\"░\\\",\\\"blk34\\\":\\\"▓\\\",\\\"block\\\":\\\"█\\\",\\\"bne\\\":\\\"=⃥\\\",\\\"bnequiv\\\":\\\"≡⃥\\\",\\\"bNot\\\":\\\"⫭\\\",\\\"bnot\\\":\\\"⌐\\\",\\\"Bopf\\\":\\\"𝔹\\\",\\\"bopf\\\":\\\"𝕓\\\",\\\"bot\\\":\\\"⊥\\\",\\\"bottom\\\":\\\"⊥\\\",\\\"bowtie\\\":\\\"⋈\\\",\\\"boxbox\\\":\\\"⧉\\\",\\\"boxdl\\\":\\\"┐\\\",\\\"boxdL\\\":\\\"╕\\\",\\\"boxDl\\\":\\\"╖\\\",\\\"boxDL\\\":\\\"╗\\\",\\\"boxdr\\\":\\\"┌\\\",\\\"boxdR\\\":\\\"╒\\\",\\\"boxDr\\\":\\\"╓\\\",\\\"boxDR\\\":\\\"╔\\\",\\\"boxh\\\":\\\"─\\\",\\\"boxH\\\":\\\"═\\\",\\\"boxhd\\\":\\\"┬\\\",\\\"boxHd\\\":\\\"╤\\\",\\\"boxhD\\\":\\\"╥\\\",\\\"boxHD\\\":\\\"╦\\\",\\\"boxhu\\\":\\\"┴\\\",\\\"boxHu\\\":\\\"╧\\\",\\\"boxhU\\\":\\\"╨\\\",\\\"boxHU\\\":\\\"╩\\\",\\\"boxminus\\\":\\\"⊟\\\",\\\"boxplus\\\":\\\"⊞\\\",\\\"boxtimes\\\":\\\"⊠\\\",\\\"boxul\\\":\\\"┘\\\",\\\"boxuL\\\":\\\"╛\\\",\\\"boxUl\\\":\\\"╜\\\",\\\"boxUL\\\":\\\"╝\\\",\\\"boxur\\\":\\\"└\\\",\\\"boxuR\\\":\\\"╘\\\",\\\"boxUr\\\":\\\"╙\\\",\\\"boxUR\\\":\\\"╚\\\",\\\"boxv\\\":\\\"│\\\",\\\"boxV\\\":\\\"║\\\",\\\"boxvh\\\":\\\"┼\\\",\\\"boxvH\\\":\\\"╪\\\",\\\"boxVh\\\":\\\"╫\\\",\\\"boxVH\\\":\\\"╬\\\",\\\"boxvl\\\":\\\"┤\\\",\\\"boxvL\\\":\\\"╡\\\",\\\"boxVl\\\":\\\"╢\\\",\\\"boxVL\\\":\\\"╣\\\",\\\"boxvr\\\":\\\"├\\\",\\\"boxvR\\\":\\\"╞\\\",\\\"boxVr\\\":\\\"╟\\\",\\\"boxVR\\\":\\\"╠\\\",\\\"bprime\\\":\\\"‵\\\",\\\"breve\\\":\\\"˘\\\",\\\"Breve\\\":\\\"˘\\\",\\\"brvbar\\\":\\\"¦\\\",\\\"bscr\\\":\\\"𝒷\\\",\\\"Bscr\\\":\\\"ℬ\\\",\\\"bsemi\\\":\\\"⁏\\\",\\\"bsim\\\":\\\"∽\\\",\\\"bsime\\\":\\\"⋍\\\",\\\"bsolb\\\":\\\"⧅\\\",\\\"bsol\\\":\\\"\\\\\\\\\\\",\\\"bsolhsub\\\":\\\"⟈\\\",\\\"bull\\\":\\\"•\\\",\\\"bullet\\\":\\\"•\\\",\\\"bump\\\":\\\"≎\\\",\\\"bumpE\\\":\\\"⪮\\\",\\\"bumpe\\\":\\\"≏\\\",\\\"Bumpeq\\\":\\\"≎\\\",\\\"bumpeq\\\":\\\"≏\\\",\\\"Cacute\\\":\\\"Ć\\\",\\\"cacute\\\":\\\"ć\\\",\\\"capand\\\":\\\"⩄\\\",\\\"capbrcup\\\":\\\"⩉\\\",\\\"capcap\\\":\\\"⩋\\\",\\\"cap\\\":\\\"∩\\\",\\\"Cap\\\":\\\"⋒\\\",\\\"capcup\\\":\\\"⩇\\\",\\\"capdot\\\":\\\"⩀\\\",\\\"CapitalDifferentialD\\\":\\\"ⅅ\\\",\\\"caps\\\":\\\"∩︀\\\",\\\"caret\\\":\\\"⁁\\\",\\\"caron\\\":\\\"ˇ\\\",\\\"Cayleys\\\":\\\"ℭ\\\",\\\"ccaps\\\":\\\"⩍\\\",\\\"Ccaron\\\":\\\"Č\\\",\\\"ccaron\\\":\\\"č\\\",\\\"Ccedil\\\":\\\"Ç\\\",\\\"ccedil\\\":\\\"ç\\\",\\\"Ccirc\\\":\\\"Ĉ\\\",\\\"ccirc\\\":\\\"ĉ\\\",\\\"Cconint\\\":\\\"∰\\\",\\\"ccups\\\":\\\"⩌\\\",\\\"ccupssm\\\":\\\"⩐\\\",\\\"Cdot\\\":\\\"Ċ\\\",\\\"cdot\\\":\\\"ċ\\\",\\\"cedil\\\":\\\"¸\\\",\\\"Cedilla\\\":\\\"¸\\\",\\\"cemptyv\\\":\\\"⦲\\\",\\\"cent\\\":\\\"¢\\\",\\\"centerdot\\\":\\\"·\\\",\\\"CenterDot\\\":\\\"·\\\",\\\"cfr\\\":\\\"𝔠\\\",\\\"Cfr\\\":\\\"ℭ\\\",\\\"CHcy\\\":\\\"Ч\\\",\\\"chcy\\\":\\\"ч\\\",\\\"check\\\":\\\"✓\\\",\\\"checkmark\\\":\\\"✓\\\",\\\"Chi\\\":\\\"Χ\\\",\\\"chi\\\":\\\"χ\\\",\\\"circ\\\":\\\"ˆ\\\",\\\"circeq\\\":\\\"≗\\\",\\\"circlearrowleft\\\":\\\"↺\\\",\\\"circlearrowright\\\":\\\"↻\\\",\\\"circledast\\\":\\\"⊛\\\",\\\"circledcirc\\\":\\\"⊚\\\",\\\"circleddash\\\":\\\"⊝\\\",\\\"CircleDot\\\":\\\"⊙\\\",\\\"circledR\\\":\\\"®\\\",\\\"circledS\\\":\\\"Ⓢ\\\",\\\"CircleMinus\\\":\\\"⊖\\\",\\\"CirclePlus\\\":\\\"⊕\\\",\\\"CircleTimes\\\":\\\"⊗\\\",\\\"cir\\\":\\\"○\\\",\\\"cirE\\\":\\\"⧃\\\",\\\"cire\\\":\\\"≗\\\",\\\"cirfnint\\\":\\\"⨐\\\",\\\"cirmid\\\":\\\"⫯\\\",\\\"cirscir\\\":\\\"⧂\\\",\\\"ClockwiseContourIntegral\\\":\\\"∲\\\",\\\"CloseCurlyDoubleQuote\\\":\\\"”\\\",\\\"CloseCurlyQuote\\\":\\\"’\\\",\\\"clubs\\\":\\\"♣\\\",\\\"clubsuit\\\":\\\"♣\\\",\\\"colon\\\":\\\":\\\",\\\"Colon\\\":\\\"∷\\\",\\\"Colone\\\":\\\"⩴\\\",\\\"colone\\\":\\\"≔\\\",\\\"coloneq\\\":\\\"≔\\\",\\\"comma\\\":\\\",\\\",\\\"commat\\\":\\\"@\\\",\\\"comp\\\":\\\"∁\\\",\\\"compfn\\\":\\\"∘\\\",\\\"complement\\\":\\\"∁\\\",\\\"complexes\\\":\\\"ℂ\\\",\\\"cong\\\":\\\"≅\\\",\\\"congdot\\\":\\\"⩭\\\",\\\"Congruent\\\":\\\"≡\\\",\\\"conint\\\":\\\"∮\\\",\\\"Conint\\\":\\\"∯\\\",\\\"ContourIntegral\\\":\\\"∮\\\",\\\"copf\\\":\\\"𝕔\\\",\\\"Copf\\\":\\\"ℂ\\\",\\\"coprod\\\":\\\"∐\\\",\\\"Coproduct\\\":\\\"∐\\\",\\\"copy\\\":\\\"©\\\",\\\"COPY\\\":\\\"©\\\",\\\"copysr\\\":\\\"℗\\\",\\\"CounterClockwiseContourIntegral\\\":\\\"∳\\\",\\\"crarr\\\":\\\"↵\\\",\\\"cross\\\":\\\"✗\\\",\\\"Cross\\\":\\\"⨯\\\",\\\"Cscr\\\":\\\"𝒞\\\",\\\"cscr\\\":\\\"𝒸\\\",\\\"csub\\\":\\\"⫏\\\",\\\"csube\\\":\\\"⫑\\\",\\\"csup\\\":\\\"⫐\\\",\\\"csupe\\\":\\\"⫒\\\",\\\"ctdot\\\":\\\"⋯\\\",\\\"cudarrl\\\":\\\"⤸\\\",\\\"cudarrr\\\":\\\"⤵\\\",\\\"cuepr\\\":\\\"⋞\\\",\\\"cuesc\\\":\\\"⋟\\\",\\\"cularr\\\":\\\"↶\\\",\\\"cularrp\\\":\\\"⤽\\\",\\\"cupbrcap\\\":\\\"⩈\\\",\\\"cupcap\\\":\\\"⩆\\\",\\\"CupCap\\\":\\\"≍\\\",\\\"cup\\\":\\\"∪\\\",\\\"Cup\\\":\\\"⋓\\\",\\\"cupcup\\\":\\\"⩊\\\",\\\"cupdot\\\":\\\"⊍\\\",\\\"cupor\\\":\\\"⩅\\\",\\\"cups\\\":\\\"∪︀\\\",\\\"curarr\\\":\\\"↷\\\",\\\"curarrm\\\":\\\"⤼\\\",\\\"curlyeqprec\\\":\\\"⋞\\\",\\\"curlyeqsucc\\\":\\\"⋟\\\",\\\"curlyvee\\\":\\\"⋎\\\",\\\"curlywedge\\\":\\\"⋏\\\",\\\"curren\\\":\\\"¤\\\",\\\"curvearrowleft\\\":\\\"↶\\\",\\\"curvearrowright\\\":\\\"↷\\\",\\\"cuvee\\\":\\\"⋎\\\",\\\"cuwed\\\":\\\"⋏\\\",\\\"cwconint\\\":\\\"∲\\\",\\\"cwint\\\":\\\"∱\\\",\\\"cylcty\\\":\\\"⌭\\\",\\\"dagger\\\":\\\"†\\\",\\\"Dagger\\\":\\\"‡\\\",\\\"daleth\\\":\\\"ℸ\\\",\\\"darr\\\":\\\"↓\\\",\\\"Darr\\\":\\\"↡\\\",\\\"dArr\\\":\\\"⇓\\\",\\\"dash\\\":\\\"‐\\\",\\\"Dashv\\\":\\\"⫤\\\",\\\"dashv\\\":\\\"⊣\\\",\\\"dbkarow\\\":\\\"⤏\\\",\\\"dblac\\\":\\\"˝\\\",\\\"Dcaron\\\":\\\"Ď\\\",\\\"dcaron\\\":\\\"ď\\\",\\\"Dcy\\\":\\\"Д\\\",\\\"dcy\\\":\\\"д\\\",\\\"ddagger\\\":\\\"‡\\\",\\\"ddarr\\\":\\\"⇊\\\",\\\"DD\\\":\\\"ⅅ\\\",\\\"dd\\\":\\\"ⅆ\\\",\\\"DDotrahd\\\":\\\"⤑\\\",\\\"ddotseq\\\":\\\"⩷\\\",\\\"deg\\\":\\\"°\\\",\\\"Del\\\":\\\"∇\\\",\\\"Delta\\\":\\\"Δ\\\",\\\"delta\\\":\\\"δ\\\",\\\"demptyv\\\":\\\"⦱\\\",\\\"dfisht\\\":\\\"⥿\\\",\\\"Dfr\\\":\\\"𝔇\\\",\\\"dfr\\\":\\\"𝔡\\\",\\\"dHar\\\":\\\"⥥\\\",\\\"dharl\\\":\\\"⇃\\\",\\\"dharr\\\":\\\"⇂\\\",\\\"DiacriticalAcute\\\":\\\"´\\\",\\\"DiacriticalDot\\\":\\\"˙\\\",\\\"DiacriticalDoubleAcute\\\":\\\"˝\\\",\\\"DiacriticalGrave\\\":\\\"`\\\",\\\"DiacriticalTilde\\\":\\\"˜\\\",\\\"diam\\\":\\\"⋄\\\",\\\"diamond\\\":\\\"⋄\\\",\\\"Diamond\\\":\\\"⋄\\\",\\\"diamondsuit\\\":\\\"♦\\\",\\\"diams\\\":\\\"♦\\\",\\\"die\\\":\\\"¨\\\",\\\"DifferentialD\\\":\\\"ⅆ\\\",\\\"digamma\\\":\\\"ϝ\\\",\\\"disin\\\":\\\"⋲\\\",\\\"div\\\":\\\"÷\\\",\\\"divide\\\":\\\"÷\\\",\\\"divideontimes\\\":\\\"⋇\\\",\\\"divonx\\\":\\\"⋇\\\",\\\"DJcy\\\":\\\"Ђ\\\",\\\"djcy\\\":\\\"ђ\\\",\\\"dlcorn\\\":\\\"⌞\\\",\\\"dlcrop\\\":\\\"⌍\\\",\\\"dollar\\\":\\\"$\\\",\\\"Dopf\\\":\\\"𝔻\\\",\\\"dopf\\\":\\\"𝕕\\\",\\\"Dot\\\":\\\"¨\\\",\\\"dot\\\":\\\"˙\\\",\\\"DotDot\\\":\\\"⃜\\\",\\\"doteq\\\":\\\"≐\\\",\\\"doteqdot\\\":\\\"≑\\\",\\\"DotEqual\\\":\\\"≐\\\",\\\"dotminus\\\":\\\"∸\\\",\\\"dotplus\\\":\\\"∔\\\",\\\"dotsquare\\\":\\\"⊡\\\",\\\"doublebarwedge\\\":\\\"⌆\\\",\\\"DoubleContourIntegral\\\":\\\"∯\\\",\\\"DoubleDot\\\":\\\"¨\\\",\\\"DoubleDownArrow\\\":\\\"⇓\\\",\\\"DoubleLeftArrow\\\":\\\"⇐\\\",\\\"DoubleLeftRightArrow\\\":\\\"⇔\\\",\\\"DoubleLeftTee\\\":\\\"⫤\\\",\\\"DoubleLongLeftArrow\\\":\\\"⟸\\\",\\\"DoubleLongLeftRightArrow\\\":\\\"⟺\\\",\\\"DoubleLongRightArrow\\\":\\\"⟹\\\",\\\"DoubleRightArrow\\\":\\\"⇒\\\",\\\"DoubleRightTee\\\":\\\"⊨\\\",\\\"DoubleUpArrow\\\":\\\"⇑\\\",\\\"DoubleUpDownArrow\\\":\\\"⇕\\\",\\\"DoubleVerticalBar\\\":\\\"∥\\\",\\\"DownArrowBar\\\":\\\"⤓\\\",\\\"downarrow\\\":\\\"↓\\\",\\\"DownArrow\\\":\\\"↓\\\",\\\"Downarrow\\\":\\\"⇓\\\",\\\"DownArrowUpArrow\\\":\\\"⇵\\\",\\\"DownBreve\\\":\\\"̑\\\",\\\"downdownarrows\\\":\\\"⇊\\\",\\\"downharpoonleft\\\":\\\"⇃\\\",\\\"downharpoonright\\\":\\\"⇂\\\",\\\"DownLeftRightVector\\\":\\\"⥐\\\",\\\"DownLeftTeeVector\\\":\\\"⥞\\\",\\\"DownLeftVectorBar\\\":\\\"⥖\\\",\\\"DownLeftVector\\\":\\\"↽\\\",\\\"DownRightTeeVector\\\":\\\"⥟\\\",\\\"DownRightVectorBar\\\":\\\"⥗\\\",\\\"DownRightVector\\\":\\\"⇁\\\",\\\"DownTeeArrow\\\":\\\"↧\\\",\\\"DownTee\\\":\\\"⊤\\\",\\\"drbkarow\\\":\\\"⤐\\\",\\\"drcorn\\\":\\\"⌟\\\",\\\"drcrop\\\":\\\"⌌\\\",\\\"Dscr\\\":\\\"𝒟\\\",\\\"dscr\\\":\\\"𝒹\\\",\\\"DScy\\\":\\\"Ѕ\\\",\\\"dscy\\\":\\\"ѕ\\\",\\\"dsol\\\":\\\"⧶\\\",\\\"Dstrok\\\":\\\"Đ\\\",\\\"dstrok\\\":\\\"đ\\\",\\\"dtdot\\\":\\\"⋱\\\",\\\"dtri\\\":\\\"▿\\\",\\\"dtrif\\\":\\\"▾\\\",\\\"duarr\\\":\\\"⇵\\\",\\\"duhar\\\":\\\"⥯\\\",\\\"dwangle\\\":\\\"⦦\\\",\\\"DZcy\\\":\\\"Џ\\\",\\\"dzcy\\\":\\\"џ\\\",\\\"dzigrarr\\\":\\\"⟿\\\",\\\"Eacute\\\":\\\"É\\\",\\\"eacute\\\":\\\"é\\\",\\\"easter\\\":\\\"⩮\\\",\\\"Ecaron\\\":\\\"Ě\\\",\\\"ecaron\\\":\\\"ě\\\",\\\"Ecirc\\\":\\\"Ê\\\",\\\"ecirc\\\":\\\"ê\\\",\\\"ecir\\\":\\\"≖\\\",\\\"ecolon\\\":\\\"≕\\\",\\\"Ecy\\\":\\\"Э\\\",\\\"ecy\\\":\\\"э\\\",\\\"eDDot\\\":\\\"⩷\\\",\\\"Edot\\\":\\\"Ė\\\",\\\"edot\\\":\\\"ė\\\",\\\"eDot\\\":\\\"≑\\\",\\\"ee\\\":\\\"ⅇ\\\",\\\"efDot\\\":\\\"≒\\\",\\\"Efr\\\":\\\"𝔈\\\",\\\"efr\\\":\\\"𝔢\\\",\\\"eg\\\":\\\"⪚\\\",\\\"Egrave\\\":\\\"È\\\",\\\"egrave\\\":\\\"è\\\",\\\"egs\\\":\\\"⪖\\\",\\\"egsdot\\\":\\\"⪘\\\",\\\"el\\\":\\\"⪙\\\",\\\"Element\\\":\\\"∈\\\",\\\"elinters\\\":\\\"⏧\\\",\\\"ell\\\":\\\"ℓ\\\",\\\"els\\\":\\\"⪕\\\",\\\"elsdot\\\":\\\"⪗\\\",\\\"Emacr\\\":\\\"Ē\\\",\\\"emacr\\\":\\\"ē\\\",\\\"empty\\\":\\\"∅\\\",\\\"emptyset\\\":\\\"∅\\\",\\\"EmptySmallSquare\\\":\\\"◻\\\",\\\"emptyv\\\":\\\"∅\\\",\\\"EmptyVerySmallSquare\\\":\\\"▫\\\",\\\"emsp13\\\":\\\" \\\",\\\"emsp14\\\":\\\" \\\",\\\"emsp\\\":\\\" \\\",\\\"ENG\\\":\\\"Ŋ\\\",\\\"eng\\\":\\\"ŋ\\\",\\\"ensp\\\":\\\" \\\",\\\"Eogon\\\":\\\"Ę\\\",\\\"eogon\\\":\\\"ę\\\",\\\"Eopf\\\":\\\"𝔼\\\",\\\"eopf\\\":\\\"𝕖\\\",\\\"epar\\\":\\\"⋕\\\",\\\"eparsl\\\":\\\"⧣\\\",\\\"eplus\\\":\\\"⩱\\\",\\\"epsi\\\":\\\"ε\\\",\\\"Epsilon\\\":\\\"Ε\\\",\\\"epsilon\\\":\\\"ε\\\",\\\"epsiv\\\":\\\"ϵ\\\",\\\"eqcirc\\\":\\\"≖\\\",\\\"eqcolon\\\":\\\"≕\\\",\\\"eqsim\\\":\\\"≂\\\",\\\"eqslantgtr\\\":\\\"⪖\\\",\\\"eqslantless\\\":\\\"⪕\\\",\\\"Equal\\\":\\\"⩵\\\",\\\"equals\\\":\\\"=\\\",\\\"EqualTilde\\\":\\\"≂\\\",\\\"equest\\\":\\\"≟\\\",\\\"Equilibrium\\\":\\\"⇌\\\",\\\"equiv\\\":\\\"≡\\\",\\\"equivDD\\\":\\\"⩸\\\",\\\"eqvparsl\\\":\\\"⧥\\\",\\\"erarr\\\":\\\"⥱\\\",\\\"erDot\\\":\\\"≓\\\",\\\"escr\\\":\\\"ℯ\\\",\\\"Escr\\\":\\\"ℰ\\\",\\\"esdot\\\":\\\"≐\\\",\\\"Esim\\\":\\\"⩳\\\",\\\"esim\\\":\\\"≂\\\",\\\"Eta\\\":\\\"Η\\\",\\\"eta\\\":\\\"η\\\",\\\"ETH\\\":\\\"Ð\\\",\\\"eth\\\":\\\"ð\\\",\\\"Euml\\\":\\\"Ë\\\",\\\"euml\\\":\\\"ë\\\",\\\"euro\\\":\\\"€\\\",\\\"excl\\\":\\\"!\\\",\\\"exist\\\":\\\"∃\\\",\\\"Exists\\\":\\\"∃\\\",\\\"expectation\\\":\\\"ℰ\\\",\\\"exponentiale\\\":\\\"ⅇ\\\",\\\"ExponentialE\\\":\\\"ⅇ\\\",\\\"fallingdotseq\\\":\\\"≒\\\",\\\"Fcy\\\":\\\"Ф\\\",\\\"fcy\\\":\\\"ф\\\",\\\"female\\\":\\\"♀\\\",\\\"ffilig\\\":\\\"ffi\\\",\\\"fflig\\\":\\\"ff\\\",\\\"ffllig\\\":\\\"ffl\\\",\\\"Ffr\\\":\\\"𝔉\\\",\\\"ffr\\\":\\\"𝔣\\\",\\\"filig\\\":\\\"fi\\\",\\\"FilledSmallSquare\\\":\\\"◼\\\",\\\"FilledVerySmallSquare\\\":\\\"▪\\\",\\\"fjlig\\\":\\\"fj\\\",\\\"flat\\\":\\\"♭\\\",\\\"fllig\\\":\\\"fl\\\",\\\"fltns\\\":\\\"▱\\\",\\\"fnof\\\":\\\"ƒ\\\",\\\"Fopf\\\":\\\"𝔽\\\",\\\"fopf\\\":\\\"𝕗\\\",\\\"forall\\\":\\\"∀\\\",\\\"ForAll\\\":\\\"∀\\\",\\\"fork\\\":\\\"⋔\\\",\\\"forkv\\\":\\\"⫙\\\",\\\"Fouriertrf\\\":\\\"ℱ\\\",\\\"fpartint\\\":\\\"⨍\\\",\\\"frac12\\\":\\\"½\\\",\\\"frac13\\\":\\\"⅓\\\",\\\"frac14\\\":\\\"¼\\\",\\\"frac15\\\":\\\"⅕\\\",\\\"frac16\\\":\\\"⅙\\\",\\\"frac18\\\":\\\"⅛\\\",\\\"frac23\\\":\\\"⅔\\\",\\\"frac25\\\":\\\"⅖\\\",\\\"frac34\\\":\\\"¾\\\",\\\"frac35\\\":\\\"⅗\\\",\\\"frac38\\\":\\\"⅜\\\",\\\"frac45\\\":\\\"⅘\\\",\\\"frac56\\\":\\\"⅚\\\",\\\"frac58\\\":\\\"⅝\\\",\\\"frac78\\\":\\\"⅞\\\",\\\"frasl\\\":\\\"⁄\\\",\\\"frown\\\":\\\"⌢\\\",\\\"fscr\\\":\\\"𝒻\\\",\\\"Fscr\\\":\\\"ℱ\\\",\\\"gacute\\\":\\\"ǵ\\\",\\\"Gamma\\\":\\\"Γ\\\",\\\"gamma\\\":\\\"γ\\\",\\\"Gammad\\\":\\\"Ϝ\\\",\\\"gammad\\\":\\\"ϝ\\\",\\\"gap\\\":\\\"⪆\\\",\\\"Gbreve\\\":\\\"Ğ\\\",\\\"gbreve\\\":\\\"ğ\\\",\\\"Gcedil\\\":\\\"Ģ\\\",\\\"Gcirc\\\":\\\"Ĝ\\\",\\\"gcirc\\\":\\\"ĝ\\\",\\\"Gcy\\\":\\\"Г\\\",\\\"gcy\\\":\\\"г\\\",\\\"Gdot\\\":\\\"Ġ\\\",\\\"gdot\\\":\\\"ġ\\\",\\\"ge\\\":\\\"≥\\\",\\\"gE\\\":\\\"≧\\\",\\\"gEl\\\":\\\"⪌\\\",\\\"gel\\\":\\\"⋛\\\",\\\"geq\\\":\\\"≥\\\",\\\"geqq\\\":\\\"≧\\\",\\\"geqslant\\\":\\\"⩾\\\",\\\"gescc\\\":\\\"⪩\\\",\\\"ges\\\":\\\"⩾\\\",\\\"gesdot\\\":\\\"⪀\\\",\\\"gesdoto\\\":\\\"⪂\\\",\\\"gesdotol\\\":\\\"⪄\\\",\\\"gesl\\\":\\\"⋛︀\\\",\\\"gesles\\\":\\\"⪔\\\",\\\"Gfr\\\":\\\"𝔊\\\",\\\"gfr\\\":\\\"𝔤\\\",\\\"gg\\\":\\\"≫\\\",\\\"Gg\\\":\\\"⋙\\\",\\\"ggg\\\":\\\"⋙\\\",\\\"gimel\\\":\\\"ℷ\\\",\\\"GJcy\\\":\\\"Ѓ\\\",\\\"gjcy\\\":\\\"ѓ\\\",\\\"gla\\\":\\\"⪥\\\",\\\"gl\\\":\\\"≷\\\",\\\"glE\\\":\\\"⪒\\\",\\\"glj\\\":\\\"⪤\\\",\\\"gnap\\\":\\\"⪊\\\",\\\"gnapprox\\\":\\\"⪊\\\",\\\"gne\\\":\\\"⪈\\\",\\\"gnE\\\":\\\"≩\\\",\\\"gneq\\\":\\\"⪈\\\",\\\"gneqq\\\":\\\"≩\\\",\\\"gnsim\\\":\\\"⋧\\\",\\\"Gopf\\\":\\\"𝔾\\\",\\\"gopf\\\":\\\"𝕘\\\",\\\"grave\\\":\\\"`\\\",\\\"GreaterEqual\\\":\\\"≥\\\",\\\"GreaterEqualLess\\\":\\\"⋛\\\",\\\"GreaterFullEqual\\\":\\\"≧\\\",\\\"GreaterGreater\\\":\\\"⪢\\\",\\\"GreaterLess\\\":\\\"≷\\\",\\\"GreaterSlantEqual\\\":\\\"⩾\\\",\\\"GreaterTilde\\\":\\\"≳\\\",\\\"Gscr\\\":\\\"𝒢\\\",\\\"gscr\\\":\\\"ℊ\\\",\\\"gsim\\\":\\\"≳\\\",\\\"gsime\\\":\\\"⪎\\\",\\\"gsiml\\\":\\\"⪐\\\",\\\"gtcc\\\":\\\"⪧\\\",\\\"gtcir\\\":\\\"⩺\\\",\\\"gt\\\":\\\">\\\",\\\"GT\\\":\\\">\\\",\\\"Gt\\\":\\\"≫\\\",\\\"gtdot\\\":\\\"⋗\\\",\\\"gtlPar\\\":\\\"⦕\\\",\\\"gtquest\\\":\\\"⩼\\\",\\\"gtrapprox\\\":\\\"⪆\\\",\\\"gtrarr\\\":\\\"⥸\\\",\\\"gtrdot\\\":\\\"⋗\\\",\\\"gtreqless\\\":\\\"⋛\\\",\\\"gtreqqless\\\":\\\"⪌\\\",\\\"gtrless\\\":\\\"≷\\\",\\\"gtrsim\\\":\\\"≳\\\",\\\"gvertneqq\\\":\\\"≩︀\\\",\\\"gvnE\\\":\\\"≩︀\\\",\\\"Hacek\\\":\\\"ˇ\\\",\\\"hairsp\\\":\\\" \\\",\\\"half\\\":\\\"½\\\",\\\"hamilt\\\":\\\"ℋ\\\",\\\"HARDcy\\\":\\\"Ъ\\\",\\\"hardcy\\\":\\\"ъ\\\",\\\"harrcir\\\":\\\"⥈\\\",\\\"harr\\\":\\\"↔\\\",\\\"hArr\\\":\\\"⇔\\\",\\\"harrw\\\":\\\"↭\\\",\\\"Hat\\\":\\\"^\\\",\\\"hbar\\\":\\\"ℏ\\\",\\\"Hcirc\\\":\\\"Ĥ\\\",\\\"hcirc\\\":\\\"ĥ\\\",\\\"hearts\\\":\\\"♥\\\",\\\"heartsuit\\\":\\\"♥\\\",\\\"hellip\\\":\\\"…\\\",\\\"hercon\\\":\\\"⊹\\\",\\\"hfr\\\":\\\"𝔥\\\",\\\"Hfr\\\":\\\"ℌ\\\",\\\"HilbertSpace\\\":\\\"ℋ\\\",\\\"hksearow\\\":\\\"⤥\\\",\\\"hkswarow\\\":\\\"⤦\\\",\\\"hoarr\\\":\\\"⇿\\\",\\\"homtht\\\":\\\"∻\\\",\\\"hookleftarrow\\\":\\\"↩\\\",\\\"hookrightarrow\\\":\\\"↪\\\",\\\"hopf\\\":\\\"𝕙\\\",\\\"Hopf\\\":\\\"ℍ\\\",\\\"horbar\\\":\\\"―\\\",\\\"HorizontalLine\\\":\\\"─\\\",\\\"hscr\\\":\\\"𝒽\\\",\\\"Hscr\\\":\\\"ℋ\\\",\\\"hslash\\\":\\\"ℏ\\\",\\\"Hstrok\\\":\\\"Ħ\\\",\\\"hstrok\\\":\\\"ħ\\\",\\\"HumpDownHump\\\":\\\"≎\\\",\\\"HumpEqual\\\":\\\"≏\\\",\\\"hybull\\\":\\\"⁃\\\",\\\"hyphen\\\":\\\"‐\\\",\\\"Iacute\\\":\\\"Í\\\",\\\"iacute\\\":\\\"í\\\",\\\"ic\\\":\\\"⁣\\\",\\\"Icirc\\\":\\\"Î\\\",\\\"icirc\\\":\\\"î\\\",\\\"Icy\\\":\\\"И\\\",\\\"icy\\\":\\\"и\\\",\\\"Idot\\\":\\\"İ\\\",\\\"IEcy\\\":\\\"Е\\\",\\\"iecy\\\":\\\"е\\\",\\\"iexcl\\\":\\\"¡\\\",\\\"iff\\\":\\\"⇔\\\",\\\"ifr\\\":\\\"𝔦\\\",\\\"Ifr\\\":\\\"ℑ\\\",\\\"Igrave\\\":\\\"Ì\\\",\\\"igrave\\\":\\\"ì\\\",\\\"ii\\\":\\\"ⅈ\\\",\\\"iiiint\\\":\\\"⨌\\\",\\\"iiint\\\":\\\"∭\\\",\\\"iinfin\\\":\\\"⧜\\\",\\\"iiota\\\":\\\"℩\\\",\\\"IJlig\\\":\\\"IJ\\\",\\\"ijlig\\\":\\\"ij\\\",\\\"Imacr\\\":\\\"Ī\\\",\\\"imacr\\\":\\\"ī\\\",\\\"image\\\":\\\"ℑ\\\",\\\"ImaginaryI\\\":\\\"ⅈ\\\",\\\"imagline\\\":\\\"ℐ\\\",\\\"imagpart\\\":\\\"ℑ\\\",\\\"imath\\\":\\\"ı\\\",\\\"Im\\\":\\\"ℑ\\\",\\\"imof\\\":\\\"⊷\\\",\\\"imped\\\":\\\"Ƶ\\\",\\\"Implies\\\":\\\"⇒\\\",\\\"incare\\\":\\\"℅\\\",\\\"in\\\":\\\"∈\\\",\\\"infin\\\":\\\"∞\\\",\\\"infintie\\\":\\\"⧝\\\",\\\"inodot\\\":\\\"ı\\\",\\\"intcal\\\":\\\"⊺\\\",\\\"int\\\":\\\"∫\\\",\\\"Int\\\":\\\"∬\\\",\\\"integers\\\":\\\"ℤ\\\",\\\"Integral\\\":\\\"∫\\\",\\\"intercal\\\":\\\"⊺\\\",\\\"Intersection\\\":\\\"⋂\\\",\\\"intlarhk\\\":\\\"⨗\\\",\\\"intprod\\\":\\\"⨼\\\",\\\"InvisibleComma\\\":\\\"⁣\\\",\\\"InvisibleTimes\\\":\\\"⁢\\\",\\\"IOcy\\\":\\\"Ё\\\",\\\"iocy\\\":\\\"ё\\\",\\\"Iogon\\\":\\\"Į\\\",\\\"iogon\\\":\\\"į\\\",\\\"Iopf\\\":\\\"𝕀\\\",\\\"iopf\\\":\\\"𝕚\\\",\\\"Iota\\\":\\\"Ι\\\",\\\"iota\\\":\\\"ι\\\",\\\"iprod\\\":\\\"⨼\\\",\\\"iquest\\\":\\\"¿\\\",\\\"iscr\\\":\\\"𝒾\\\",\\\"Iscr\\\":\\\"ℐ\\\",\\\"isin\\\":\\\"∈\\\",\\\"isindot\\\":\\\"⋵\\\",\\\"isinE\\\":\\\"⋹\\\",\\\"isins\\\":\\\"⋴\\\",\\\"isinsv\\\":\\\"⋳\\\",\\\"isinv\\\":\\\"∈\\\",\\\"it\\\":\\\"⁢\\\",\\\"Itilde\\\":\\\"Ĩ\\\",\\\"itilde\\\":\\\"ĩ\\\",\\\"Iukcy\\\":\\\"І\\\",\\\"iukcy\\\":\\\"і\\\",\\\"Iuml\\\":\\\"Ï\\\",\\\"iuml\\\":\\\"ï\\\",\\\"Jcirc\\\":\\\"Ĵ\\\",\\\"jcirc\\\":\\\"ĵ\\\",\\\"Jcy\\\":\\\"Й\\\",\\\"jcy\\\":\\\"й\\\",\\\"Jfr\\\":\\\"𝔍\\\",\\\"jfr\\\":\\\"𝔧\\\",\\\"jmath\\\":\\\"ȷ\\\",\\\"Jopf\\\":\\\"𝕁\\\",\\\"jopf\\\":\\\"𝕛\\\",\\\"Jscr\\\":\\\"𝒥\\\",\\\"jscr\\\":\\\"𝒿\\\",\\\"Jsercy\\\":\\\"Ј\\\",\\\"jsercy\\\":\\\"ј\\\",\\\"Jukcy\\\":\\\"Є\\\",\\\"jukcy\\\":\\\"є\\\",\\\"Kappa\\\":\\\"Κ\\\",\\\"kappa\\\":\\\"κ\\\",\\\"kappav\\\":\\\"ϰ\\\",\\\"Kcedil\\\":\\\"Ķ\\\",\\\"kcedil\\\":\\\"ķ\\\",\\\"Kcy\\\":\\\"К\\\",\\\"kcy\\\":\\\"к\\\",\\\"Kfr\\\":\\\"𝔎\\\",\\\"kfr\\\":\\\"𝔨\\\",\\\"kgreen\\\":\\\"ĸ\\\",\\\"KHcy\\\":\\\"Х\\\",\\\"khcy\\\":\\\"х\\\",\\\"KJcy\\\":\\\"Ќ\\\",\\\"kjcy\\\":\\\"ќ\\\",\\\"Kopf\\\":\\\"𝕂\\\",\\\"kopf\\\":\\\"𝕜\\\",\\\"Kscr\\\":\\\"𝒦\\\",\\\"kscr\\\":\\\"𝓀\\\",\\\"lAarr\\\":\\\"⇚\\\",\\\"Lacute\\\":\\\"Ĺ\\\",\\\"lacute\\\":\\\"ĺ\\\",\\\"laemptyv\\\":\\\"⦴\\\",\\\"lagran\\\":\\\"ℒ\\\",\\\"Lambda\\\":\\\"Λ\\\",\\\"lambda\\\":\\\"λ\\\",\\\"lang\\\":\\\"⟨\\\",\\\"Lang\\\":\\\"⟪\\\",\\\"langd\\\":\\\"⦑\\\",\\\"langle\\\":\\\"⟨\\\",\\\"lap\\\":\\\"⪅\\\",\\\"Laplacetrf\\\":\\\"ℒ\\\",\\\"laquo\\\":\\\"«\\\",\\\"larrb\\\":\\\"⇤\\\",\\\"larrbfs\\\":\\\"⤟\\\",\\\"larr\\\":\\\"←\\\",\\\"Larr\\\":\\\"↞\\\",\\\"lArr\\\":\\\"⇐\\\",\\\"larrfs\\\":\\\"⤝\\\",\\\"larrhk\\\":\\\"↩\\\",\\\"larrlp\\\":\\\"↫\\\",\\\"larrpl\\\":\\\"⤹\\\",\\\"larrsim\\\":\\\"⥳\\\",\\\"larrtl\\\":\\\"↢\\\",\\\"latail\\\":\\\"⤙\\\",\\\"lAtail\\\":\\\"⤛\\\",\\\"lat\\\":\\\"⪫\\\",\\\"late\\\":\\\"⪭\\\",\\\"lates\\\":\\\"⪭︀\\\",\\\"lbarr\\\":\\\"⤌\\\",\\\"lBarr\\\":\\\"⤎\\\",\\\"lbbrk\\\":\\\"❲\\\",\\\"lbrace\\\":\\\"{\\\",\\\"lbrack\\\":\\\"[\\\",\\\"lbrke\\\":\\\"⦋\\\",\\\"lbrksld\\\":\\\"⦏\\\",\\\"lbrkslu\\\":\\\"⦍\\\",\\\"Lcaron\\\":\\\"Ľ\\\",\\\"lcaron\\\":\\\"ľ\\\",\\\"Lcedil\\\":\\\"Ļ\\\",\\\"lcedil\\\":\\\"ļ\\\",\\\"lceil\\\":\\\"⌈\\\",\\\"lcub\\\":\\\"{\\\",\\\"Lcy\\\":\\\"Л\\\",\\\"lcy\\\":\\\"л\\\",\\\"ldca\\\":\\\"⤶\\\",\\\"ldquo\\\":\\\"“\\\",\\\"ldquor\\\":\\\"„\\\",\\\"ldrdhar\\\":\\\"⥧\\\",\\\"ldrushar\\\":\\\"⥋\\\",\\\"ldsh\\\":\\\"↲\\\",\\\"le\\\":\\\"≤\\\",\\\"lE\\\":\\\"≦\\\",\\\"LeftAngleBracket\\\":\\\"⟨\\\",\\\"LeftArrowBar\\\":\\\"⇤\\\",\\\"leftarrow\\\":\\\"←\\\",\\\"LeftArrow\\\":\\\"←\\\",\\\"Leftarrow\\\":\\\"⇐\\\",\\\"LeftArrowRightArrow\\\":\\\"⇆\\\",\\\"leftarrowtail\\\":\\\"↢\\\",\\\"LeftCeiling\\\":\\\"⌈\\\",\\\"LeftDoubleBracket\\\":\\\"⟦\\\",\\\"LeftDownTeeVector\\\":\\\"⥡\\\",\\\"LeftDownVectorBar\\\":\\\"⥙\\\",\\\"LeftDownVector\\\":\\\"⇃\\\",\\\"LeftFloor\\\":\\\"⌊\\\",\\\"leftharpoondown\\\":\\\"↽\\\",\\\"leftharpoonup\\\":\\\"↼\\\",\\\"leftleftarrows\\\":\\\"⇇\\\",\\\"leftrightarrow\\\":\\\"↔\\\",\\\"LeftRightArrow\\\":\\\"↔\\\",\\\"Leftrightarrow\\\":\\\"⇔\\\",\\\"leftrightarrows\\\":\\\"⇆\\\",\\\"leftrightharpoons\\\":\\\"⇋\\\",\\\"leftrightsquigarrow\\\":\\\"↭\\\",\\\"LeftRightVector\\\":\\\"⥎\\\",\\\"LeftTeeArrow\\\":\\\"↤\\\",\\\"LeftTee\\\":\\\"⊣\\\",\\\"LeftTeeVector\\\":\\\"⥚\\\",\\\"leftthreetimes\\\":\\\"⋋\\\",\\\"LeftTriangleBar\\\":\\\"⧏\\\",\\\"LeftTriangle\\\":\\\"⊲\\\",\\\"LeftTriangleEqual\\\":\\\"⊴\\\",\\\"LeftUpDownVector\\\":\\\"⥑\\\",\\\"LeftUpTeeVector\\\":\\\"⥠\\\",\\\"LeftUpVectorBar\\\":\\\"⥘\\\",\\\"LeftUpVector\\\":\\\"↿\\\",\\\"LeftVectorBar\\\":\\\"⥒\\\",\\\"LeftVector\\\":\\\"↼\\\",\\\"lEg\\\":\\\"⪋\\\",\\\"leg\\\":\\\"⋚\\\",\\\"leq\\\":\\\"≤\\\",\\\"leqq\\\":\\\"≦\\\",\\\"leqslant\\\":\\\"⩽\\\",\\\"lescc\\\":\\\"⪨\\\",\\\"les\\\":\\\"⩽\\\",\\\"lesdot\\\":\\\"⩿\\\",\\\"lesdoto\\\":\\\"⪁\\\",\\\"lesdotor\\\":\\\"⪃\\\",\\\"lesg\\\":\\\"⋚︀\\\",\\\"lesges\\\":\\\"⪓\\\",\\\"lessapprox\\\":\\\"⪅\\\",\\\"lessdot\\\":\\\"⋖\\\",\\\"lesseqgtr\\\":\\\"⋚\\\",\\\"lesseqqgtr\\\":\\\"⪋\\\",\\\"LessEqualGreater\\\":\\\"⋚\\\",\\\"LessFullEqual\\\":\\\"≦\\\",\\\"LessGreater\\\":\\\"≶\\\",\\\"lessgtr\\\":\\\"≶\\\",\\\"LessLess\\\":\\\"⪡\\\",\\\"lesssim\\\":\\\"≲\\\",\\\"LessSlantEqual\\\":\\\"⩽\\\",\\\"LessTilde\\\":\\\"≲\\\",\\\"lfisht\\\":\\\"⥼\\\",\\\"lfloor\\\":\\\"⌊\\\",\\\"Lfr\\\":\\\"𝔏\\\",\\\"lfr\\\":\\\"𝔩\\\",\\\"lg\\\":\\\"≶\\\",\\\"lgE\\\":\\\"⪑\\\",\\\"lHar\\\":\\\"⥢\\\",\\\"lhard\\\":\\\"↽\\\",\\\"lharu\\\":\\\"↼\\\",\\\"lharul\\\":\\\"⥪\\\",\\\"lhblk\\\":\\\"▄\\\",\\\"LJcy\\\":\\\"Љ\\\",\\\"ljcy\\\":\\\"љ\\\",\\\"llarr\\\":\\\"⇇\\\",\\\"ll\\\":\\\"≪\\\",\\\"Ll\\\":\\\"⋘\\\",\\\"llcorner\\\":\\\"⌞\\\",\\\"Lleftarrow\\\":\\\"⇚\\\",\\\"llhard\\\":\\\"⥫\\\",\\\"lltri\\\":\\\"◺\\\",\\\"Lmidot\\\":\\\"Ŀ\\\",\\\"lmidot\\\":\\\"ŀ\\\",\\\"lmoustache\\\":\\\"⎰\\\",\\\"lmoust\\\":\\\"⎰\\\",\\\"lnap\\\":\\\"⪉\\\",\\\"lnapprox\\\":\\\"⪉\\\",\\\"lne\\\":\\\"⪇\\\",\\\"lnE\\\":\\\"≨\\\",\\\"lneq\\\":\\\"⪇\\\",\\\"lneqq\\\":\\\"≨\\\",\\\"lnsim\\\":\\\"⋦\\\",\\\"loang\\\":\\\"⟬\\\",\\\"loarr\\\":\\\"⇽\\\",\\\"lobrk\\\":\\\"⟦\\\",\\\"longleftarrow\\\":\\\"⟵\\\",\\\"LongLeftArrow\\\":\\\"⟵\\\",\\\"Longleftarrow\\\":\\\"⟸\\\",\\\"longleftrightarrow\\\":\\\"⟷\\\",\\\"LongLeftRightArrow\\\":\\\"⟷\\\",\\\"Longleftrightarrow\\\":\\\"⟺\\\",\\\"longmapsto\\\":\\\"⟼\\\",\\\"longrightarrow\\\":\\\"⟶\\\",\\\"LongRightArrow\\\":\\\"⟶\\\",\\\"Longrightarrow\\\":\\\"⟹\\\",\\\"looparrowleft\\\":\\\"↫\\\",\\\"looparrowright\\\":\\\"↬\\\",\\\"lopar\\\":\\\"⦅\\\",\\\"Lopf\\\":\\\"𝕃\\\",\\\"lopf\\\":\\\"𝕝\\\",\\\"loplus\\\":\\\"⨭\\\",\\\"lotimes\\\":\\\"⨴\\\",\\\"lowast\\\":\\\"∗\\\",\\\"lowbar\\\":\\\"_\\\",\\\"LowerLeftArrow\\\":\\\"↙\\\",\\\"LowerRightArrow\\\":\\\"↘\\\",\\\"loz\\\":\\\"◊\\\",\\\"lozenge\\\":\\\"◊\\\",\\\"lozf\\\":\\\"⧫\\\",\\\"lpar\\\":\\\"(\\\",\\\"lparlt\\\":\\\"⦓\\\",\\\"lrarr\\\":\\\"⇆\\\",\\\"lrcorner\\\":\\\"⌟\\\",\\\"lrhar\\\":\\\"⇋\\\",\\\"lrhard\\\":\\\"⥭\\\",\\\"lrm\\\":\\\"‎\\\",\\\"lrtri\\\":\\\"⊿\\\",\\\"lsaquo\\\":\\\"‹\\\",\\\"lscr\\\":\\\"𝓁\\\",\\\"Lscr\\\":\\\"ℒ\\\",\\\"lsh\\\":\\\"↰\\\",\\\"Lsh\\\":\\\"↰\\\",\\\"lsim\\\":\\\"≲\\\",\\\"lsime\\\":\\\"⪍\\\",\\\"lsimg\\\":\\\"⪏\\\",\\\"lsqb\\\":\\\"[\\\",\\\"lsquo\\\":\\\"‘\\\",\\\"lsquor\\\":\\\"‚\\\",\\\"Lstrok\\\":\\\"Ł\\\",\\\"lstrok\\\":\\\"ł\\\",\\\"ltcc\\\":\\\"⪦\\\",\\\"ltcir\\\":\\\"⩹\\\",\\\"lt\\\":\\\"<\\\",\\\"LT\\\":\\\"<\\\",\\\"Lt\\\":\\\"≪\\\",\\\"ltdot\\\":\\\"⋖\\\",\\\"lthree\\\":\\\"⋋\\\",\\\"ltimes\\\":\\\"⋉\\\",\\\"ltlarr\\\":\\\"⥶\\\",\\\"ltquest\\\":\\\"⩻\\\",\\\"ltri\\\":\\\"◃\\\",\\\"ltrie\\\":\\\"⊴\\\",\\\"ltrif\\\":\\\"◂\\\",\\\"ltrPar\\\":\\\"⦖\\\",\\\"lurdshar\\\":\\\"⥊\\\",\\\"luruhar\\\":\\\"⥦\\\",\\\"lvertneqq\\\":\\\"≨︀\\\",\\\"lvnE\\\":\\\"≨︀\\\",\\\"macr\\\":\\\"¯\\\",\\\"male\\\":\\\"♂\\\",\\\"malt\\\":\\\"✠\\\",\\\"maltese\\\":\\\"✠\\\",\\\"Map\\\":\\\"⤅\\\",\\\"map\\\":\\\"↦\\\",\\\"mapsto\\\":\\\"↦\\\",\\\"mapstodown\\\":\\\"↧\\\",\\\"mapstoleft\\\":\\\"↤\\\",\\\"mapstoup\\\":\\\"↥\\\",\\\"marker\\\":\\\"▮\\\",\\\"mcomma\\\":\\\"⨩\\\",\\\"Mcy\\\":\\\"М\\\",\\\"mcy\\\":\\\"м\\\",\\\"mdash\\\":\\\"—\\\",\\\"mDDot\\\":\\\"∺\\\",\\\"measuredangle\\\":\\\"∡\\\",\\\"MediumSpace\\\":\\\" \\\",\\\"Mellintrf\\\":\\\"ℳ\\\",\\\"Mfr\\\":\\\"𝔐\\\",\\\"mfr\\\":\\\"𝔪\\\",\\\"mho\\\":\\\"℧\\\",\\\"micro\\\":\\\"µ\\\",\\\"midast\\\":\\\"*\\\",\\\"midcir\\\":\\\"⫰\\\",\\\"mid\\\":\\\"∣\\\",\\\"middot\\\":\\\"·\\\",\\\"minusb\\\":\\\"⊟\\\",\\\"minus\\\":\\\"−\\\",\\\"minusd\\\":\\\"∸\\\",\\\"minusdu\\\":\\\"⨪\\\",\\\"MinusPlus\\\":\\\"∓\\\",\\\"mlcp\\\":\\\"⫛\\\",\\\"mldr\\\":\\\"…\\\",\\\"mnplus\\\":\\\"∓\\\",\\\"models\\\":\\\"⊧\\\",\\\"Mopf\\\":\\\"𝕄\\\",\\\"mopf\\\":\\\"𝕞\\\",\\\"mp\\\":\\\"∓\\\",\\\"mscr\\\":\\\"𝓂\\\",\\\"Mscr\\\":\\\"ℳ\\\",\\\"mstpos\\\":\\\"∾\\\",\\\"Mu\\\":\\\"Μ\\\",\\\"mu\\\":\\\"μ\\\",\\\"multimap\\\":\\\"⊸\\\",\\\"mumap\\\":\\\"⊸\\\",\\\"nabla\\\":\\\"∇\\\",\\\"Nacute\\\":\\\"Ń\\\",\\\"nacute\\\":\\\"ń\\\",\\\"nang\\\":\\\"∠⃒\\\",\\\"nap\\\":\\\"≉\\\",\\\"napE\\\":\\\"⩰̸\\\",\\\"napid\\\":\\\"≋̸\\\",\\\"napos\\\":\\\"ʼn\\\",\\\"napprox\\\":\\\"≉\\\",\\\"natural\\\":\\\"♮\\\",\\\"naturals\\\":\\\"ℕ\\\",\\\"natur\\\":\\\"♮\\\",\\\"nbsp\\\":\\\" \\\",\\\"nbump\\\":\\\"≎̸\\\",\\\"nbumpe\\\":\\\"≏̸\\\",\\\"ncap\\\":\\\"⩃\\\",\\\"Ncaron\\\":\\\"Ň\\\",\\\"ncaron\\\":\\\"ň\\\",\\\"Ncedil\\\":\\\"Ņ\\\",\\\"ncedil\\\":\\\"ņ\\\",\\\"ncong\\\":\\\"≇\\\",\\\"ncongdot\\\":\\\"⩭̸\\\",\\\"ncup\\\":\\\"⩂\\\",\\\"Ncy\\\":\\\"Н\\\",\\\"ncy\\\":\\\"н\\\",\\\"ndash\\\":\\\"–\\\",\\\"nearhk\\\":\\\"⤤\\\",\\\"nearr\\\":\\\"↗\\\",\\\"neArr\\\":\\\"⇗\\\",\\\"nearrow\\\":\\\"↗\\\",\\\"ne\\\":\\\"≠\\\",\\\"nedot\\\":\\\"≐̸\\\",\\\"NegativeMediumSpace\\\":\\\"​\\\",\\\"NegativeThickSpace\\\":\\\"​\\\",\\\"NegativeThinSpace\\\":\\\"​\\\",\\\"NegativeVeryThinSpace\\\":\\\"​\\\",\\\"nequiv\\\":\\\"≢\\\",\\\"nesear\\\":\\\"⤨\\\",\\\"nesim\\\":\\\"≂̸\\\",\\\"NestedGreaterGreater\\\":\\\"≫\\\",\\\"NestedLessLess\\\":\\\"≪\\\",\\\"NewLine\\\":\\\"\\\\n\\\",\\\"nexist\\\":\\\"∄\\\",\\\"nexists\\\":\\\"∄\\\",\\\"Nfr\\\":\\\"𝔑\\\",\\\"nfr\\\":\\\"𝔫\\\",\\\"ngE\\\":\\\"≧̸\\\",\\\"nge\\\":\\\"≱\\\",\\\"ngeq\\\":\\\"≱\\\",\\\"ngeqq\\\":\\\"≧̸\\\",\\\"ngeqslant\\\":\\\"⩾̸\\\",\\\"nges\\\":\\\"⩾̸\\\",\\\"nGg\\\":\\\"⋙̸\\\",\\\"ngsim\\\":\\\"≵\\\",\\\"nGt\\\":\\\"≫⃒\\\",\\\"ngt\\\":\\\"≯\\\",\\\"ngtr\\\":\\\"≯\\\",\\\"nGtv\\\":\\\"≫̸\\\",\\\"nharr\\\":\\\"↮\\\",\\\"nhArr\\\":\\\"⇎\\\",\\\"nhpar\\\":\\\"⫲\\\",\\\"ni\\\":\\\"∋\\\",\\\"nis\\\":\\\"⋼\\\",\\\"nisd\\\":\\\"⋺\\\",\\\"niv\\\":\\\"∋\\\",\\\"NJcy\\\":\\\"Њ\\\",\\\"njcy\\\":\\\"њ\\\",\\\"nlarr\\\":\\\"↚\\\",\\\"nlArr\\\":\\\"⇍\\\",\\\"nldr\\\":\\\"‥\\\",\\\"nlE\\\":\\\"≦̸\\\",\\\"nle\\\":\\\"≰\\\",\\\"nleftarrow\\\":\\\"↚\\\",\\\"nLeftarrow\\\":\\\"⇍\\\",\\\"nleftrightarrow\\\":\\\"↮\\\",\\\"nLeftrightarrow\\\":\\\"⇎\\\",\\\"nleq\\\":\\\"≰\\\",\\\"nleqq\\\":\\\"≦̸\\\",\\\"nleqslant\\\":\\\"⩽̸\\\",\\\"nles\\\":\\\"⩽̸\\\",\\\"nless\\\":\\\"≮\\\",\\\"nLl\\\":\\\"⋘̸\\\",\\\"nlsim\\\":\\\"≴\\\",\\\"nLt\\\":\\\"≪⃒\\\",\\\"nlt\\\":\\\"≮\\\",\\\"nltri\\\":\\\"⋪\\\",\\\"nltrie\\\":\\\"⋬\\\",\\\"nLtv\\\":\\\"≪̸\\\",\\\"nmid\\\":\\\"∤\\\",\\\"NoBreak\\\":\\\"⁠\\\",\\\"NonBreakingSpace\\\":\\\" \\\",\\\"nopf\\\":\\\"𝕟\\\",\\\"Nopf\\\":\\\"ℕ\\\",\\\"Not\\\":\\\"⫬\\\",\\\"not\\\":\\\"¬\\\",\\\"NotCongruent\\\":\\\"≢\\\",\\\"NotCupCap\\\":\\\"≭\\\",\\\"NotDoubleVerticalBar\\\":\\\"∦\\\",\\\"NotElement\\\":\\\"∉\\\",\\\"NotEqual\\\":\\\"≠\\\",\\\"NotEqualTilde\\\":\\\"≂̸\\\",\\\"NotExists\\\":\\\"∄\\\",\\\"NotGreater\\\":\\\"≯\\\",\\\"NotGreaterEqual\\\":\\\"≱\\\",\\\"NotGreaterFullEqual\\\":\\\"≧̸\\\",\\\"NotGreaterGreater\\\":\\\"≫̸\\\",\\\"NotGreaterLess\\\":\\\"≹\\\",\\\"NotGreaterSlantEqual\\\":\\\"⩾̸\\\",\\\"NotGreaterTilde\\\":\\\"≵\\\",\\\"NotHumpDownHump\\\":\\\"≎̸\\\",\\\"NotHumpEqual\\\":\\\"≏̸\\\",\\\"notin\\\":\\\"∉\\\",\\\"notindot\\\":\\\"⋵̸\\\",\\\"notinE\\\":\\\"⋹̸\\\",\\\"notinva\\\":\\\"∉\\\",\\\"notinvb\\\":\\\"⋷\\\",\\\"notinvc\\\":\\\"⋶\\\",\\\"NotLeftTriangleBar\\\":\\\"⧏̸\\\",\\\"NotLeftTriangle\\\":\\\"⋪\\\",\\\"NotLeftTriangleEqual\\\":\\\"⋬\\\",\\\"NotLess\\\":\\\"≮\\\",\\\"NotLessEqual\\\":\\\"≰\\\",\\\"NotLessGreater\\\":\\\"≸\\\",\\\"NotLessLess\\\":\\\"≪̸\\\",\\\"NotLessSlantEqual\\\":\\\"⩽̸\\\",\\\"NotLessTilde\\\":\\\"≴\\\",\\\"NotNestedGreaterGreater\\\":\\\"⪢̸\\\",\\\"NotNestedLessLess\\\":\\\"⪡̸\\\",\\\"notni\\\":\\\"∌\\\",\\\"notniva\\\":\\\"∌\\\",\\\"notnivb\\\":\\\"⋾\\\",\\\"notnivc\\\":\\\"⋽\\\",\\\"NotPrecedes\\\":\\\"⊀\\\",\\\"NotPrecedesEqual\\\":\\\"⪯̸\\\",\\\"NotPrecedesSlantEqual\\\":\\\"⋠\\\",\\\"NotReverseElement\\\":\\\"∌\\\",\\\"NotRightTriangleBar\\\":\\\"⧐̸\\\",\\\"NotRightTriangle\\\":\\\"⋫\\\",\\\"NotRightTriangleEqual\\\":\\\"⋭\\\",\\\"NotSquareSubset\\\":\\\"⊏̸\\\",\\\"NotSquareSubsetEqual\\\":\\\"⋢\\\",\\\"NotSquareSuperset\\\":\\\"⊐̸\\\",\\\"NotSquareSupersetEqual\\\":\\\"⋣\\\",\\\"NotSubset\\\":\\\"⊂⃒\\\",\\\"NotSubsetEqual\\\":\\\"⊈\\\",\\\"NotSucceeds\\\":\\\"⊁\\\",\\\"NotSucceedsEqual\\\":\\\"⪰̸\\\",\\\"NotSucceedsSlantEqual\\\":\\\"⋡\\\",\\\"NotSucceedsTilde\\\":\\\"≿̸\\\",\\\"NotSuperset\\\":\\\"⊃⃒\\\",\\\"NotSupersetEqual\\\":\\\"⊉\\\",\\\"NotTilde\\\":\\\"≁\\\",\\\"NotTildeEqual\\\":\\\"≄\\\",\\\"NotTildeFullEqual\\\":\\\"≇\\\",\\\"NotTildeTilde\\\":\\\"≉\\\",\\\"NotVerticalBar\\\":\\\"∤\\\",\\\"nparallel\\\":\\\"∦\\\",\\\"npar\\\":\\\"∦\\\",\\\"nparsl\\\":\\\"⫽⃥\\\",\\\"npart\\\":\\\"∂̸\\\",\\\"npolint\\\":\\\"⨔\\\",\\\"npr\\\":\\\"⊀\\\",\\\"nprcue\\\":\\\"⋠\\\",\\\"nprec\\\":\\\"⊀\\\",\\\"npreceq\\\":\\\"⪯̸\\\",\\\"npre\\\":\\\"⪯̸\\\",\\\"nrarrc\\\":\\\"⤳̸\\\",\\\"nrarr\\\":\\\"↛\\\",\\\"nrArr\\\":\\\"⇏\\\",\\\"nrarrw\\\":\\\"↝̸\\\",\\\"nrightarrow\\\":\\\"↛\\\",\\\"nRightarrow\\\":\\\"⇏\\\",\\\"nrtri\\\":\\\"⋫\\\",\\\"nrtrie\\\":\\\"⋭\\\",\\\"nsc\\\":\\\"⊁\\\",\\\"nsccue\\\":\\\"⋡\\\",\\\"nsce\\\":\\\"⪰̸\\\",\\\"Nscr\\\":\\\"𝒩\\\",\\\"nscr\\\":\\\"𝓃\\\",\\\"nshortmid\\\":\\\"∤\\\",\\\"nshortparallel\\\":\\\"∦\\\",\\\"nsim\\\":\\\"≁\\\",\\\"nsime\\\":\\\"≄\\\",\\\"nsimeq\\\":\\\"≄\\\",\\\"nsmid\\\":\\\"∤\\\",\\\"nspar\\\":\\\"∦\\\",\\\"nsqsube\\\":\\\"⋢\\\",\\\"nsqsupe\\\":\\\"⋣\\\",\\\"nsub\\\":\\\"⊄\\\",\\\"nsubE\\\":\\\"⫅̸\\\",\\\"nsube\\\":\\\"⊈\\\",\\\"nsubset\\\":\\\"⊂⃒\\\",\\\"nsubseteq\\\":\\\"⊈\\\",\\\"nsubseteqq\\\":\\\"⫅̸\\\",\\\"nsucc\\\":\\\"⊁\\\",\\\"nsucceq\\\":\\\"⪰̸\\\",\\\"nsup\\\":\\\"⊅\\\",\\\"nsupE\\\":\\\"⫆̸\\\",\\\"nsupe\\\":\\\"⊉\\\",\\\"nsupset\\\":\\\"⊃⃒\\\",\\\"nsupseteq\\\":\\\"⊉\\\",\\\"nsupseteqq\\\":\\\"⫆̸\\\",\\\"ntgl\\\":\\\"≹\\\",\\\"Ntilde\\\":\\\"Ñ\\\",\\\"ntilde\\\":\\\"ñ\\\",\\\"ntlg\\\":\\\"≸\\\",\\\"ntriangleleft\\\":\\\"⋪\\\",\\\"ntrianglelefteq\\\":\\\"⋬\\\",\\\"ntriangleright\\\":\\\"⋫\\\",\\\"ntrianglerighteq\\\":\\\"⋭\\\",\\\"Nu\\\":\\\"Ν\\\",\\\"nu\\\":\\\"ν\\\",\\\"num\\\":\\\"#\\\",\\\"numero\\\":\\\"№\\\",\\\"numsp\\\":\\\" \\\",\\\"nvap\\\":\\\"≍⃒\\\",\\\"nvdash\\\":\\\"⊬\\\",\\\"nvDash\\\":\\\"⊭\\\",\\\"nVdash\\\":\\\"⊮\\\",\\\"nVDash\\\":\\\"⊯\\\",\\\"nvge\\\":\\\"≥⃒\\\",\\\"nvgt\\\":\\\">⃒\\\",\\\"nvHarr\\\":\\\"⤄\\\",\\\"nvinfin\\\":\\\"⧞\\\",\\\"nvlArr\\\":\\\"⤂\\\",\\\"nvle\\\":\\\"≤⃒\\\",\\\"nvlt\\\":\\\"<⃒\\\",\\\"nvltrie\\\":\\\"⊴⃒\\\",\\\"nvrArr\\\":\\\"⤃\\\",\\\"nvrtrie\\\":\\\"⊵⃒\\\",\\\"nvsim\\\":\\\"∼⃒\\\",\\\"nwarhk\\\":\\\"⤣\\\",\\\"nwarr\\\":\\\"↖\\\",\\\"nwArr\\\":\\\"⇖\\\",\\\"nwarrow\\\":\\\"↖\\\",\\\"nwnear\\\":\\\"⤧\\\",\\\"Oacute\\\":\\\"Ó\\\",\\\"oacute\\\":\\\"ó\\\",\\\"oast\\\":\\\"⊛\\\",\\\"Ocirc\\\":\\\"Ô\\\",\\\"ocirc\\\":\\\"ô\\\",\\\"ocir\\\":\\\"⊚\\\",\\\"Ocy\\\":\\\"О\\\",\\\"ocy\\\":\\\"о\\\",\\\"odash\\\":\\\"⊝\\\",\\\"Odblac\\\":\\\"Ő\\\",\\\"odblac\\\":\\\"ő\\\",\\\"odiv\\\":\\\"⨸\\\",\\\"odot\\\":\\\"⊙\\\",\\\"odsold\\\":\\\"⦼\\\",\\\"OElig\\\":\\\"Œ\\\",\\\"oelig\\\":\\\"œ\\\",\\\"ofcir\\\":\\\"⦿\\\",\\\"Ofr\\\":\\\"𝔒\\\",\\\"ofr\\\":\\\"𝔬\\\",\\\"ogon\\\":\\\"˛\\\",\\\"Ograve\\\":\\\"Ò\\\",\\\"ograve\\\":\\\"ò\\\",\\\"ogt\\\":\\\"⧁\\\",\\\"ohbar\\\":\\\"⦵\\\",\\\"ohm\\\":\\\"Ω\\\",\\\"oint\\\":\\\"∮\\\",\\\"olarr\\\":\\\"↺\\\",\\\"olcir\\\":\\\"⦾\\\",\\\"olcross\\\":\\\"⦻\\\",\\\"oline\\\":\\\"‾\\\",\\\"olt\\\":\\\"⧀\\\",\\\"Omacr\\\":\\\"Ō\\\",\\\"omacr\\\":\\\"ō\\\",\\\"Omega\\\":\\\"Ω\\\",\\\"omega\\\":\\\"ω\\\",\\\"Omicron\\\":\\\"Ο\\\",\\\"omicron\\\":\\\"ο\\\",\\\"omid\\\":\\\"⦶\\\",\\\"ominus\\\":\\\"⊖\\\",\\\"Oopf\\\":\\\"𝕆\\\",\\\"oopf\\\":\\\"𝕠\\\",\\\"opar\\\":\\\"⦷\\\",\\\"OpenCurlyDoubleQuote\\\":\\\"“\\\",\\\"OpenCurlyQuote\\\":\\\"‘\\\",\\\"operp\\\":\\\"⦹\\\",\\\"oplus\\\":\\\"⊕\\\",\\\"orarr\\\":\\\"↻\\\",\\\"Or\\\":\\\"⩔\\\",\\\"or\\\":\\\"∨\\\",\\\"ord\\\":\\\"⩝\\\",\\\"order\\\":\\\"ℴ\\\",\\\"orderof\\\":\\\"ℴ\\\",\\\"ordf\\\":\\\"ª\\\",\\\"ordm\\\":\\\"º\\\",\\\"origof\\\":\\\"⊶\\\",\\\"oror\\\":\\\"⩖\\\",\\\"orslope\\\":\\\"⩗\\\",\\\"orv\\\":\\\"⩛\\\",\\\"oS\\\":\\\"Ⓢ\\\",\\\"Oscr\\\":\\\"𝒪\\\",\\\"oscr\\\":\\\"ℴ\\\",\\\"Oslash\\\":\\\"Ø\\\",\\\"oslash\\\":\\\"ø\\\",\\\"osol\\\":\\\"⊘\\\",\\\"Otilde\\\":\\\"Õ\\\",\\\"otilde\\\":\\\"õ\\\",\\\"otimesas\\\":\\\"⨶\\\",\\\"Otimes\\\":\\\"⨷\\\",\\\"otimes\\\":\\\"⊗\\\",\\\"Ouml\\\":\\\"Ö\\\",\\\"ouml\\\":\\\"ö\\\",\\\"ovbar\\\":\\\"⌽\\\",\\\"OverBar\\\":\\\"‾\\\",\\\"OverBrace\\\":\\\"⏞\\\",\\\"OverBracket\\\":\\\"⎴\\\",\\\"OverParenthesis\\\":\\\"⏜\\\",\\\"para\\\":\\\"¶\\\",\\\"parallel\\\":\\\"∥\\\",\\\"par\\\":\\\"∥\\\",\\\"parsim\\\":\\\"⫳\\\",\\\"parsl\\\":\\\"⫽\\\",\\\"part\\\":\\\"∂\\\",\\\"PartialD\\\":\\\"∂\\\",\\\"Pcy\\\":\\\"П\\\",\\\"pcy\\\":\\\"п\\\",\\\"percnt\\\":\\\"%\\\",\\\"period\\\":\\\".\\\",\\\"permil\\\":\\\"‰\\\",\\\"perp\\\":\\\"⊥\\\",\\\"pertenk\\\":\\\"‱\\\",\\\"Pfr\\\":\\\"𝔓\\\",\\\"pfr\\\":\\\"𝔭\\\",\\\"Phi\\\":\\\"Φ\\\",\\\"phi\\\":\\\"φ\\\",\\\"phiv\\\":\\\"ϕ\\\",\\\"phmmat\\\":\\\"ℳ\\\",\\\"phone\\\":\\\"☎\\\",\\\"Pi\\\":\\\"Π\\\",\\\"pi\\\":\\\"π\\\",\\\"pitchfork\\\":\\\"⋔\\\",\\\"piv\\\":\\\"ϖ\\\",\\\"planck\\\":\\\"ℏ\\\",\\\"planckh\\\":\\\"ℎ\\\",\\\"plankv\\\":\\\"ℏ\\\",\\\"plusacir\\\":\\\"⨣\\\",\\\"plusb\\\":\\\"⊞\\\",\\\"pluscir\\\":\\\"⨢\\\",\\\"plus\\\":\\\"+\\\",\\\"plusdo\\\":\\\"∔\\\",\\\"plusdu\\\":\\\"⨥\\\",\\\"pluse\\\":\\\"⩲\\\",\\\"PlusMinus\\\":\\\"±\\\",\\\"plusmn\\\":\\\"±\\\",\\\"plussim\\\":\\\"⨦\\\",\\\"plustwo\\\":\\\"⨧\\\",\\\"pm\\\":\\\"±\\\",\\\"Poincareplane\\\":\\\"ℌ\\\",\\\"pointint\\\":\\\"⨕\\\",\\\"popf\\\":\\\"𝕡\\\",\\\"Popf\\\":\\\"ℙ\\\",\\\"pound\\\":\\\"£\\\",\\\"prap\\\":\\\"⪷\\\",\\\"Pr\\\":\\\"⪻\\\",\\\"pr\\\":\\\"≺\\\",\\\"prcue\\\":\\\"≼\\\",\\\"precapprox\\\":\\\"⪷\\\",\\\"prec\\\":\\\"≺\\\",\\\"preccurlyeq\\\":\\\"≼\\\",\\\"Precedes\\\":\\\"≺\\\",\\\"PrecedesEqual\\\":\\\"⪯\\\",\\\"PrecedesSlantEqual\\\":\\\"≼\\\",\\\"PrecedesTilde\\\":\\\"≾\\\",\\\"preceq\\\":\\\"⪯\\\",\\\"precnapprox\\\":\\\"⪹\\\",\\\"precneqq\\\":\\\"⪵\\\",\\\"precnsim\\\":\\\"⋨\\\",\\\"pre\\\":\\\"⪯\\\",\\\"prE\\\":\\\"⪳\\\",\\\"precsim\\\":\\\"≾\\\",\\\"prime\\\":\\\"′\\\",\\\"Prime\\\":\\\"″\\\",\\\"primes\\\":\\\"ℙ\\\",\\\"prnap\\\":\\\"⪹\\\",\\\"prnE\\\":\\\"⪵\\\",\\\"prnsim\\\":\\\"⋨\\\",\\\"prod\\\":\\\"∏\\\",\\\"Product\\\":\\\"∏\\\",\\\"profalar\\\":\\\"⌮\\\",\\\"profline\\\":\\\"⌒\\\",\\\"profsurf\\\":\\\"⌓\\\",\\\"prop\\\":\\\"∝\\\",\\\"Proportional\\\":\\\"∝\\\",\\\"Proportion\\\":\\\"∷\\\",\\\"propto\\\":\\\"∝\\\",\\\"prsim\\\":\\\"≾\\\",\\\"prurel\\\":\\\"⊰\\\",\\\"Pscr\\\":\\\"𝒫\\\",\\\"pscr\\\":\\\"𝓅\\\",\\\"Psi\\\":\\\"Ψ\\\",\\\"psi\\\":\\\"ψ\\\",\\\"puncsp\\\":\\\" \\\",\\\"Qfr\\\":\\\"𝔔\\\",\\\"qfr\\\":\\\"𝔮\\\",\\\"qint\\\":\\\"⨌\\\",\\\"qopf\\\":\\\"𝕢\\\",\\\"Qopf\\\":\\\"ℚ\\\",\\\"qprime\\\":\\\"⁗\\\",\\\"Qscr\\\":\\\"𝒬\\\",\\\"qscr\\\":\\\"𝓆\\\",\\\"quaternions\\\":\\\"ℍ\\\",\\\"quatint\\\":\\\"⨖\\\",\\\"quest\\\":\\\"?\\\",\\\"questeq\\\":\\\"≟\\\",\\\"quot\\\":\\\"\\\\\\\"\\\",\\\"QUOT\\\":\\\"\\\\\\\"\\\",\\\"rAarr\\\":\\\"⇛\\\",\\\"race\\\":\\\"∽̱\\\",\\\"Racute\\\":\\\"Ŕ\\\",\\\"racute\\\":\\\"ŕ\\\",\\\"radic\\\":\\\"√\\\",\\\"raemptyv\\\":\\\"⦳\\\",\\\"rang\\\":\\\"⟩\\\",\\\"Rang\\\":\\\"⟫\\\",\\\"rangd\\\":\\\"⦒\\\",\\\"range\\\":\\\"⦥\\\",\\\"rangle\\\":\\\"⟩\\\",\\\"raquo\\\":\\\"»\\\",\\\"rarrap\\\":\\\"⥵\\\",\\\"rarrb\\\":\\\"⇥\\\",\\\"rarrbfs\\\":\\\"⤠\\\",\\\"rarrc\\\":\\\"⤳\\\",\\\"rarr\\\":\\\"→\\\",\\\"Rarr\\\":\\\"↠\\\",\\\"rArr\\\":\\\"⇒\\\",\\\"rarrfs\\\":\\\"⤞\\\",\\\"rarrhk\\\":\\\"↪\\\",\\\"rarrlp\\\":\\\"↬\\\",\\\"rarrpl\\\":\\\"⥅\\\",\\\"rarrsim\\\":\\\"⥴\\\",\\\"Rarrtl\\\":\\\"⤖\\\",\\\"rarrtl\\\":\\\"↣\\\",\\\"rarrw\\\":\\\"↝\\\",\\\"ratail\\\":\\\"⤚\\\",\\\"rAtail\\\":\\\"⤜\\\",\\\"ratio\\\":\\\"∶\\\",\\\"rationals\\\":\\\"ℚ\\\",\\\"rbarr\\\":\\\"⤍\\\",\\\"rBarr\\\":\\\"⤏\\\",\\\"RBarr\\\":\\\"⤐\\\",\\\"rbbrk\\\":\\\"❳\\\",\\\"rbrace\\\":\\\"}\\\",\\\"rbrack\\\":\\\"]\\\",\\\"rbrke\\\":\\\"⦌\\\",\\\"rbrksld\\\":\\\"⦎\\\",\\\"rbrkslu\\\":\\\"⦐\\\",\\\"Rcaron\\\":\\\"Ř\\\",\\\"rcaron\\\":\\\"ř\\\",\\\"Rcedil\\\":\\\"Ŗ\\\",\\\"rcedil\\\":\\\"ŗ\\\",\\\"rceil\\\":\\\"⌉\\\",\\\"rcub\\\":\\\"}\\\",\\\"Rcy\\\":\\\"Р\\\",\\\"rcy\\\":\\\"р\\\",\\\"rdca\\\":\\\"⤷\\\",\\\"rdldhar\\\":\\\"⥩\\\",\\\"rdquo\\\":\\\"”\\\",\\\"rdquor\\\":\\\"”\\\",\\\"rdsh\\\":\\\"↳\\\",\\\"real\\\":\\\"ℜ\\\",\\\"realine\\\":\\\"ℛ\\\",\\\"realpart\\\":\\\"ℜ\\\",\\\"reals\\\":\\\"ℝ\\\",\\\"Re\\\":\\\"ℜ\\\",\\\"rect\\\":\\\"▭\\\",\\\"reg\\\":\\\"®\\\",\\\"REG\\\":\\\"®\\\",\\\"ReverseElement\\\":\\\"∋\\\",\\\"ReverseEquilibrium\\\":\\\"⇋\\\",\\\"ReverseUpEquilibrium\\\":\\\"⥯\\\",\\\"rfisht\\\":\\\"⥽\\\",\\\"rfloor\\\":\\\"⌋\\\",\\\"rfr\\\":\\\"𝔯\\\",\\\"Rfr\\\":\\\"ℜ\\\",\\\"rHar\\\":\\\"⥤\\\",\\\"rhard\\\":\\\"⇁\\\",\\\"rharu\\\":\\\"⇀\\\",\\\"rharul\\\":\\\"⥬\\\",\\\"Rho\\\":\\\"Ρ\\\",\\\"rho\\\":\\\"ρ\\\",\\\"rhov\\\":\\\"ϱ\\\",\\\"RightAngleBracket\\\":\\\"⟩\\\",\\\"RightArrowBar\\\":\\\"⇥\\\",\\\"rightarrow\\\":\\\"→\\\",\\\"RightArrow\\\":\\\"→\\\",\\\"Rightarrow\\\":\\\"⇒\\\",\\\"RightArrowLeftArrow\\\":\\\"⇄\\\",\\\"rightarrowtail\\\":\\\"↣\\\",\\\"RightCeiling\\\":\\\"⌉\\\",\\\"RightDoubleBracket\\\":\\\"⟧\\\",\\\"RightDownTeeVector\\\":\\\"⥝\\\",\\\"RightDownVectorBar\\\":\\\"⥕\\\",\\\"RightDownVector\\\":\\\"⇂\\\",\\\"RightFloor\\\":\\\"⌋\\\",\\\"rightharpoondown\\\":\\\"⇁\\\",\\\"rightharpoonup\\\":\\\"⇀\\\",\\\"rightleftarrows\\\":\\\"⇄\\\",\\\"rightleftharpoons\\\":\\\"⇌\\\",\\\"rightrightarrows\\\":\\\"⇉\\\",\\\"rightsquigarrow\\\":\\\"↝\\\",\\\"RightTeeArrow\\\":\\\"↦\\\",\\\"RightTee\\\":\\\"⊢\\\",\\\"RightTeeVector\\\":\\\"⥛\\\",\\\"rightthreetimes\\\":\\\"⋌\\\",\\\"RightTriangleBar\\\":\\\"⧐\\\",\\\"RightTriangle\\\":\\\"⊳\\\",\\\"RightTriangleEqual\\\":\\\"⊵\\\",\\\"RightUpDownVector\\\":\\\"⥏\\\",\\\"RightUpTeeVector\\\":\\\"⥜\\\",\\\"RightUpVectorBar\\\":\\\"⥔\\\",\\\"RightUpVector\\\":\\\"↾\\\",\\\"RightVectorBar\\\":\\\"⥓\\\",\\\"RightVector\\\":\\\"⇀\\\",\\\"ring\\\":\\\"˚\\\",\\\"risingdotseq\\\":\\\"≓\\\",\\\"rlarr\\\":\\\"⇄\\\",\\\"rlhar\\\":\\\"⇌\\\",\\\"rlm\\\":\\\"‏\\\",\\\"rmoustache\\\":\\\"⎱\\\",\\\"rmoust\\\":\\\"⎱\\\",\\\"rnmid\\\":\\\"⫮\\\",\\\"roang\\\":\\\"⟭\\\",\\\"roarr\\\":\\\"⇾\\\",\\\"robrk\\\":\\\"⟧\\\",\\\"ropar\\\":\\\"⦆\\\",\\\"ropf\\\":\\\"𝕣\\\",\\\"Ropf\\\":\\\"ℝ\\\",\\\"roplus\\\":\\\"⨮\\\",\\\"rotimes\\\":\\\"⨵\\\",\\\"RoundImplies\\\":\\\"⥰\\\",\\\"rpar\\\":\\\")\\\",\\\"rpargt\\\":\\\"⦔\\\",\\\"rppolint\\\":\\\"⨒\\\",\\\"rrarr\\\":\\\"⇉\\\",\\\"Rrightarrow\\\":\\\"⇛\\\",\\\"rsaquo\\\":\\\"›\\\",\\\"rscr\\\":\\\"𝓇\\\",\\\"Rscr\\\":\\\"ℛ\\\",\\\"rsh\\\":\\\"↱\\\",\\\"Rsh\\\":\\\"↱\\\",\\\"rsqb\\\":\\\"]\\\",\\\"rsquo\\\":\\\"’\\\",\\\"rsquor\\\":\\\"’\\\",\\\"rthree\\\":\\\"⋌\\\",\\\"rtimes\\\":\\\"⋊\\\",\\\"rtri\\\":\\\"▹\\\",\\\"rtrie\\\":\\\"⊵\\\",\\\"rtrif\\\":\\\"▸\\\",\\\"rtriltri\\\":\\\"⧎\\\",\\\"RuleDelayed\\\":\\\"⧴\\\",\\\"ruluhar\\\":\\\"⥨\\\",\\\"rx\\\":\\\"℞\\\",\\\"Sacute\\\":\\\"Ś\\\",\\\"sacute\\\":\\\"ś\\\",\\\"sbquo\\\":\\\"‚\\\",\\\"scap\\\":\\\"⪸\\\",\\\"Scaron\\\":\\\"Š\\\",\\\"scaron\\\":\\\"š\\\",\\\"Sc\\\":\\\"⪼\\\",\\\"sc\\\":\\\"≻\\\",\\\"sccue\\\":\\\"≽\\\",\\\"sce\\\":\\\"⪰\\\",\\\"scE\\\":\\\"⪴\\\",\\\"Scedil\\\":\\\"Ş\\\",\\\"scedil\\\":\\\"ş\\\",\\\"Scirc\\\":\\\"Ŝ\\\",\\\"scirc\\\":\\\"ŝ\\\",\\\"scnap\\\":\\\"⪺\\\",\\\"scnE\\\":\\\"⪶\\\",\\\"scnsim\\\":\\\"⋩\\\",\\\"scpolint\\\":\\\"⨓\\\",\\\"scsim\\\":\\\"≿\\\",\\\"Scy\\\":\\\"С\\\",\\\"scy\\\":\\\"с\\\",\\\"sdotb\\\":\\\"⊡\\\",\\\"sdot\\\":\\\"⋅\\\",\\\"sdote\\\":\\\"⩦\\\",\\\"searhk\\\":\\\"⤥\\\",\\\"searr\\\":\\\"↘\\\",\\\"seArr\\\":\\\"⇘\\\",\\\"searrow\\\":\\\"↘\\\",\\\"sect\\\":\\\"§\\\",\\\"semi\\\":\\\";\\\",\\\"seswar\\\":\\\"⤩\\\",\\\"setminus\\\":\\\"∖\\\",\\\"setmn\\\":\\\"∖\\\",\\\"sext\\\":\\\"✶\\\",\\\"Sfr\\\":\\\"𝔖\\\",\\\"sfr\\\":\\\"𝔰\\\",\\\"sfrown\\\":\\\"⌢\\\",\\\"sharp\\\":\\\"♯\\\",\\\"SHCHcy\\\":\\\"Щ\\\",\\\"shchcy\\\":\\\"щ\\\",\\\"SHcy\\\":\\\"Ш\\\",\\\"shcy\\\":\\\"ш\\\",\\\"ShortDownArrow\\\":\\\"↓\\\",\\\"ShortLeftArrow\\\":\\\"←\\\",\\\"shortmid\\\":\\\"∣\\\",\\\"shortparallel\\\":\\\"∥\\\",\\\"ShortRightArrow\\\":\\\"→\\\",\\\"ShortUpArrow\\\":\\\"↑\\\",\\\"shy\\\":\\\"­\\\",\\\"Sigma\\\":\\\"Σ\\\",\\\"sigma\\\":\\\"σ\\\",\\\"sigmaf\\\":\\\"ς\\\",\\\"sigmav\\\":\\\"ς\\\",\\\"sim\\\":\\\"∼\\\",\\\"simdot\\\":\\\"⩪\\\",\\\"sime\\\":\\\"≃\\\",\\\"simeq\\\":\\\"≃\\\",\\\"simg\\\":\\\"⪞\\\",\\\"simgE\\\":\\\"⪠\\\",\\\"siml\\\":\\\"⪝\\\",\\\"simlE\\\":\\\"⪟\\\",\\\"simne\\\":\\\"≆\\\",\\\"simplus\\\":\\\"⨤\\\",\\\"simrarr\\\":\\\"⥲\\\",\\\"slarr\\\":\\\"←\\\",\\\"SmallCircle\\\":\\\"∘\\\",\\\"smallsetminus\\\":\\\"∖\\\",\\\"smashp\\\":\\\"⨳\\\",\\\"smeparsl\\\":\\\"⧤\\\",\\\"smid\\\":\\\"∣\\\",\\\"smile\\\":\\\"⌣\\\",\\\"smt\\\":\\\"⪪\\\",\\\"smte\\\":\\\"⪬\\\",\\\"smtes\\\":\\\"⪬︀\\\",\\\"SOFTcy\\\":\\\"Ь\\\",\\\"softcy\\\":\\\"ь\\\",\\\"solbar\\\":\\\"⌿\\\",\\\"solb\\\":\\\"⧄\\\",\\\"sol\\\":\\\"/\\\",\\\"Sopf\\\":\\\"𝕊\\\",\\\"sopf\\\":\\\"𝕤\\\",\\\"spades\\\":\\\"♠\\\",\\\"spadesuit\\\":\\\"♠\\\",\\\"spar\\\":\\\"∥\\\",\\\"sqcap\\\":\\\"⊓\\\",\\\"sqcaps\\\":\\\"⊓︀\\\",\\\"sqcup\\\":\\\"⊔\\\",\\\"sqcups\\\":\\\"⊔︀\\\",\\\"Sqrt\\\":\\\"√\\\",\\\"sqsub\\\":\\\"⊏\\\",\\\"sqsube\\\":\\\"⊑\\\",\\\"sqsubset\\\":\\\"⊏\\\",\\\"sqsubseteq\\\":\\\"⊑\\\",\\\"sqsup\\\":\\\"⊐\\\",\\\"sqsupe\\\":\\\"⊒\\\",\\\"sqsupset\\\":\\\"⊐\\\",\\\"sqsupseteq\\\":\\\"⊒\\\",\\\"square\\\":\\\"□\\\",\\\"Square\\\":\\\"□\\\",\\\"SquareIntersection\\\":\\\"⊓\\\",\\\"SquareSubset\\\":\\\"⊏\\\",\\\"SquareSubsetEqual\\\":\\\"⊑\\\",\\\"SquareSuperset\\\":\\\"⊐\\\",\\\"SquareSupersetEqual\\\":\\\"⊒\\\",\\\"SquareUnion\\\":\\\"⊔\\\",\\\"squarf\\\":\\\"▪\\\",\\\"squ\\\":\\\"□\\\",\\\"squf\\\":\\\"▪\\\",\\\"srarr\\\":\\\"→\\\",\\\"Sscr\\\":\\\"𝒮\\\",\\\"sscr\\\":\\\"𝓈\\\",\\\"ssetmn\\\":\\\"∖\\\",\\\"ssmile\\\":\\\"⌣\\\",\\\"sstarf\\\":\\\"⋆\\\",\\\"Star\\\":\\\"⋆\\\",\\\"star\\\":\\\"☆\\\",\\\"starf\\\":\\\"★\\\",\\\"straightepsilon\\\":\\\"ϵ\\\",\\\"straightphi\\\":\\\"ϕ\\\",\\\"strns\\\":\\\"¯\\\",\\\"sub\\\":\\\"⊂\\\",\\\"Sub\\\":\\\"⋐\\\",\\\"subdot\\\":\\\"⪽\\\",\\\"subE\\\":\\\"⫅\\\",\\\"sube\\\":\\\"⊆\\\",\\\"subedot\\\":\\\"⫃\\\",\\\"submult\\\":\\\"⫁\\\",\\\"subnE\\\":\\\"⫋\\\",\\\"subne\\\":\\\"⊊\\\",\\\"subplus\\\":\\\"⪿\\\",\\\"subrarr\\\":\\\"⥹\\\",\\\"subset\\\":\\\"⊂\\\",\\\"Subset\\\":\\\"⋐\\\",\\\"subseteq\\\":\\\"⊆\\\",\\\"subseteqq\\\":\\\"⫅\\\",\\\"SubsetEqual\\\":\\\"⊆\\\",\\\"subsetneq\\\":\\\"⊊\\\",\\\"subsetneqq\\\":\\\"⫋\\\",\\\"subsim\\\":\\\"⫇\\\",\\\"subsub\\\":\\\"⫕\\\",\\\"subsup\\\":\\\"⫓\\\",\\\"succapprox\\\":\\\"⪸\\\",\\\"succ\\\":\\\"≻\\\",\\\"succcurlyeq\\\":\\\"≽\\\",\\\"Succeeds\\\":\\\"≻\\\",\\\"SucceedsEqual\\\":\\\"⪰\\\",\\\"SucceedsSlantEqual\\\":\\\"≽\\\",\\\"SucceedsTilde\\\":\\\"≿\\\",\\\"succeq\\\":\\\"⪰\\\",\\\"succnapprox\\\":\\\"⪺\\\",\\\"succneqq\\\":\\\"⪶\\\",\\\"succnsim\\\":\\\"⋩\\\",\\\"succsim\\\":\\\"≿\\\",\\\"SuchThat\\\":\\\"∋\\\",\\\"sum\\\":\\\"∑\\\",\\\"Sum\\\":\\\"∑\\\",\\\"sung\\\":\\\"♪\\\",\\\"sup1\\\":\\\"¹\\\",\\\"sup2\\\":\\\"²\\\",\\\"sup3\\\":\\\"³\\\",\\\"sup\\\":\\\"⊃\\\",\\\"Sup\\\":\\\"⋑\\\",\\\"supdot\\\":\\\"⪾\\\",\\\"supdsub\\\":\\\"⫘\\\",\\\"supE\\\":\\\"⫆\\\",\\\"supe\\\":\\\"⊇\\\",\\\"supedot\\\":\\\"⫄\\\",\\\"Superset\\\":\\\"⊃\\\",\\\"SupersetEqual\\\":\\\"⊇\\\",\\\"suphsol\\\":\\\"⟉\\\",\\\"suphsub\\\":\\\"⫗\\\",\\\"suplarr\\\":\\\"⥻\\\",\\\"supmult\\\":\\\"⫂\\\",\\\"supnE\\\":\\\"⫌\\\",\\\"supne\\\":\\\"⊋\\\",\\\"supplus\\\":\\\"⫀\\\",\\\"supset\\\":\\\"⊃\\\",\\\"Supset\\\":\\\"⋑\\\",\\\"supseteq\\\":\\\"⊇\\\",\\\"supseteqq\\\":\\\"⫆\\\",\\\"supsetneq\\\":\\\"⊋\\\",\\\"supsetneqq\\\":\\\"⫌\\\",\\\"supsim\\\":\\\"⫈\\\",\\\"supsub\\\":\\\"⫔\\\",\\\"supsup\\\":\\\"⫖\\\",\\\"swarhk\\\":\\\"⤦\\\",\\\"swarr\\\":\\\"↙\\\",\\\"swArr\\\":\\\"⇙\\\",\\\"swarrow\\\":\\\"↙\\\",\\\"swnwar\\\":\\\"⤪\\\",\\\"szlig\\\":\\\"ß\\\",\\\"Tab\\\":\\\"\\\\t\\\",\\\"target\\\":\\\"⌖\\\",\\\"Tau\\\":\\\"Τ\\\",\\\"tau\\\":\\\"τ\\\",\\\"tbrk\\\":\\\"⎴\\\",\\\"Tcaron\\\":\\\"Ť\\\",\\\"tcaron\\\":\\\"ť\\\",\\\"Tcedil\\\":\\\"Ţ\\\",\\\"tcedil\\\":\\\"ţ\\\",\\\"Tcy\\\":\\\"Т\\\",\\\"tcy\\\":\\\"т\\\",\\\"tdot\\\":\\\"⃛\\\",\\\"telrec\\\":\\\"⌕\\\",\\\"Tfr\\\":\\\"𝔗\\\",\\\"tfr\\\":\\\"𝔱\\\",\\\"there4\\\":\\\"∴\\\",\\\"therefore\\\":\\\"∴\\\",\\\"Therefore\\\":\\\"∴\\\",\\\"Theta\\\":\\\"Θ\\\",\\\"theta\\\":\\\"θ\\\",\\\"thetasym\\\":\\\"ϑ\\\",\\\"thetav\\\":\\\"ϑ\\\",\\\"thickapprox\\\":\\\"≈\\\",\\\"thicksim\\\":\\\"∼\\\",\\\"ThickSpace\\\":\\\"  \\\",\\\"ThinSpace\\\":\\\" \\\",\\\"thinsp\\\":\\\" \\\",\\\"thkap\\\":\\\"≈\\\",\\\"thksim\\\":\\\"∼\\\",\\\"THORN\\\":\\\"Þ\\\",\\\"thorn\\\":\\\"þ\\\",\\\"tilde\\\":\\\"˜\\\",\\\"Tilde\\\":\\\"∼\\\",\\\"TildeEqual\\\":\\\"≃\\\",\\\"TildeFullEqual\\\":\\\"≅\\\",\\\"TildeTilde\\\":\\\"≈\\\",\\\"timesbar\\\":\\\"⨱\\\",\\\"timesb\\\":\\\"⊠\\\",\\\"times\\\":\\\"×\\\",\\\"timesd\\\":\\\"⨰\\\",\\\"tint\\\":\\\"∭\\\",\\\"toea\\\":\\\"⤨\\\",\\\"topbot\\\":\\\"⌶\\\",\\\"topcir\\\":\\\"⫱\\\",\\\"top\\\":\\\"⊤\\\",\\\"Topf\\\":\\\"𝕋\\\",\\\"topf\\\":\\\"𝕥\\\",\\\"topfork\\\":\\\"⫚\\\",\\\"tosa\\\":\\\"⤩\\\",\\\"tprime\\\":\\\"‴\\\",\\\"trade\\\":\\\"™\\\",\\\"TRADE\\\":\\\"™\\\",\\\"triangle\\\":\\\"▵\\\",\\\"triangledown\\\":\\\"▿\\\",\\\"triangleleft\\\":\\\"◃\\\",\\\"trianglelefteq\\\":\\\"⊴\\\",\\\"triangleq\\\":\\\"≜\\\",\\\"triangleright\\\":\\\"▹\\\",\\\"trianglerighteq\\\":\\\"⊵\\\",\\\"tridot\\\":\\\"◬\\\",\\\"trie\\\":\\\"≜\\\",\\\"triminus\\\":\\\"⨺\\\",\\\"TripleDot\\\":\\\"⃛\\\",\\\"triplus\\\":\\\"⨹\\\",\\\"trisb\\\":\\\"⧍\\\",\\\"tritime\\\":\\\"⨻\\\",\\\"trpezium\\\":\\\"⏢\\\",\\\"Tscr\\\":\\\"𝒯\\\",\\\"tscr\\\":\\\"𝓉\\\",\\\"TScy\\\":\\\"Ц\\\",\\\"tscy\\\":\\\"ц\\\",\\\"TSHcy\\\":\\\"Ћ\\\",\\\"tshcy\\\":\\\"ћ\\\",\\\"Tstrok\\\":\\\"Ŧ\\\",\\\"tstrok\\\":\\\"ŧ\\\",\\\"twixt\\\":\\\"≬\\\",\\\"twoheadleftarrow\\\":\\\"↞\\\",\\\"twoheadrightarrow\\\":\\\"↠\\\",\\\"Uacute\\\":\\\"Ú\\\",\\\"uacute\\\":\\\"ú\\\",\\\"uarr\\\":\\\"↑\\\",\\\"Uarr\\\":\\\"↟\\\",\\\"uArr\\\":\\\"⇑\\\",\\\"Uarrocir\\\":\\\"⥉\\\",\\\"Ubrcy\\\":\\\"Ў\\\",\\\"ubrcy\\\":\\\"ў\\\",\\\"Ubreve\\\":\\\"Ŭ\\\",\\\"ubreve\\\":\\\"ŭ\\\",\\\"Ucirc\\\":\\\"Û\\\",\\\"ucirc\\\":\\\"û\\\",\\\"Ucy\\\":\\\"У\\\",\\\"ucy\\\":\\\"у\\\",\\\"udarr\\\":\\\"⇅\\\",\\\"Udblac\\\":\\\"Ű\\\",\\\"udblac\\\":\\\"ű\\\",\\\"udhar\\\":\\\"⥮\\\",\\\"ufisht\\\":\\\"⥾\\\",\\\"Ufr\\\":\\\"𝔘\\\",\\\"ufr\\\":\\\"𝔲\\\",\\\"Ugrave\\\":\\\"Ù\\\",\\\"ugrave\\\":\\\"ù\\\",\\\"uHar\\\":\\\"⥣\\\",\\\"uharl\\\":\\\"↿\\\",\\\"uharr\\\":\\\"↾\\\",\\\"uhblk\\\":\\\"▀\\\",\\\"ulcorn\\\":\\\"⌜\\\",\\\"ulcorner\\\":\\\"⌜\\\",\\\"ulcrop\\\":\\\"⌏\\\",\\\"ultri\\\":\\\"◸\\\",\\\"Umacr\\\":\\\"Ū\\\",\\\"umacr\\\":\\\"ū\\\",\\\"uml\\\":\\\"¨\\\",\\\"UnderBar\\\":\\\"_\\\",\\\"UnderBrace\\\":\\\"⏟\\\",\\\"UnderBracket\\\":\\\"⎵\\\",\\\"UnderParenthesis\\\":\\\"⏝\\\",\\\"Union\\\":\\\"⋃\\\",\\\"UnionPlus\\\":\\\"⊎\\\",\\\"Uogon\\\":\\\"Ų\\\",\\\"uogon\\\":\\\"ų\\\",\\\"Uopf\\\":\\\"𝕌\\\",\\\"uopf\\\":\\\"𝕦\\\",\\\"UpArrowBar\\\":\\\"⤒\\\",\\\"uparrow\\\":\\\"↑\\\",\\\"UpArrow\\\":\\\"↑\\\",\\\"Uparrow\\\":\\\"⇑\\\",\\\"UpArrowDownArrow\\\":\\\"⇅\\\",\\\"updownarrow\\\":\\\"↕\\\",\\\"UpDownArrow\\\":\\\"↕\\\",\\\"Updownarrow\\\":\\\"⇕\\\",\\\"UpEquilibrium\\\":\\\"⥮\\\",\\\"upharpoonleft\\\":\\\"↿\\\",\\\"upharpoonright\\\":\\\"↾\\\",\\\"uplus\\\":\\\"⊎\\\",\\\"UpperLeftArrow\\\":\\\"↖\\\",\\\"UpperRightArrow\\\":\\\"↗\\\",\\\"upsi\\\":\\\"υ\\\",\\\"Upsi\\\":\\\"ϒ\\\",\\\"upsih\\\":\\\"ϒ\\\",\\\"Upsilon\\\":\\\"Υ\\\",\\\"upsilon\\\":\\\"υ\\\",\\\"UpTeeArrow\\\":\\\"↥\\\",\\\"UpTee\\\":\\\"⊥\\\",\\\"upuparrows\\\":\\\"⇈\\\",\\\"urcorn\\\":\\\"⌝\\\",\\\"urcorner\\\":\\\"⌝\\\",\\\"urcrop\\\":\\\"⌎\\\",\\\"Uring\\\":\\\"Ů\\\",\\\"uring\\\":\\\"ů\\\",\\\"urtri\\\":\\\"◹\\\",\\\"Uscr\\\":\\\"𝒰\\\",\\\"uscr\\\":\\\"𝓊\\\",\\\"utdot\\\":\\\"⋰\\\",\\\"Utilde\\\":\\\"Ũ\\\",\\\"utilde\\\":\\\"ũ\\\",\\\"utri\\\":\\\"▵\\\",\\\"utrif\\\":\\\"▴\\\",\\\"uuarr\\\":\\\"⇈\\\",\\\"Uuml\\\":\\\"Ü\\\",\\\"uuml\\\":\\\"ü\\\",\\\"uwangle\\\":\\\"⦧\\\",\\\"vangrt\\\":\\\"⦜\\\",\\\"varepsilon\\\":\\\"ϵ\\\",\\\"varkappa\\\":\\\"ϰ\\\",\\\"varnothing\\\":\\\"∅\\\",\\\"varphi\\\":\\\"ϕ\\\",\\\"varpi\\\":\\\"ϖ\\\",\\\"varpropto\\\":\\\"∝\\\",\\\"varr\\\":\\\"↕\\\",\\\"vArr\\\":\\\"⇕\\\",\\\"varrho\\\":\\\"ϱ\\\",\\\"varsigma\\\":\\\"ς\\\",\\\"varsubsetneq\\\":\\\"⊊︀\\\",\\\"varsubsetneqq\\\":\\\"⫋︀\\\",\\\"varsupsetneq\\\":\\\"⊋︀\\\",\\\"varsupsetneqq\\\":\\\"⫌︀\\\",\\\"vartheta\\\":\\\"ϑ\\\",\\\"vartriangleleft\\\":\\\"⊲\\\",\\\"vartriangleright\\\":\\\"⊳\\\",\\\"vBar\\\":\\\"⫨\\\",\\\"Vbar\\\":\\\"⫫\\\",\\\"vBarv\\\":\\\"⫩\\\",\\\"Vcy\\\":\\\"В\\\",\\\"vcy\\\":\\\"в\\\",\\\"vdash\\\":\\\"⊢\\\",\\\"vDash\\\":\\\"⊨\\\",\\\"Vdash\\\":\\\"⊩\\\",\\\"VDash\\\":\\\"⊫\\\",\\\"Vdashl\\\":\\\"⫦\\\",\\\"veebar\\\":\\\"⊻\\\",\\\"vee\\\":\\\"∨\\\",\\\"Vee\\\":\\\"⋁\\\",\\\"veeeq\\\":\\\"≚\\\",\\\"vellip\\\":\\\"⋮\\\",\\\"verbar\\\":\\\"|\\\",\\\"Verbar\\\":\\\"‖\\\",\\\"vert\\\":\\\"|\\\",\\\"Vert\\\":\\\"‖\\\",\\\"VerticalBar\\\":\\\"∣\\\",\\\"VerticalLine\\\":\\\"|\\\",\\\"VerticalSeparator\\\":\\\"❘\\\",\\\"VerticalTilde\\\":\\\"≀\\\",\\\"VeryThinSpace\\\":\\\" \\\",\\\"Vfr\\\":\\\"𝔙\\\",\\\"vfr\\\":\\\"𝔳\\\",\\\"vltri\\\":\\\"⊲\\\",\\\"vnsub\\\":\\\"⊂⃒\\\",\\\"vnsup\\\":\\\"⊃⃒\\\",\\\"Vopf\\\":\\\"𝕍\\\",\\\"vopf\\\":\\\"𝕧\\\",\\\"vprop\\\":\\\"∝\\\",\\\"vrtri\\\":\\\"⊳\\\",\\\"Vscr\\\":\\\"𝒱\\\",\\\"vscr\\\":\\\"𝓋\\\",\\\"vsubnE\\\":\\\"⫋︀\\\",\\\"vsubne\\\":\\\"⊊︀\\\",\\\"vsupnE\\\":\\\"⫌︀\\\",\\\"vsupne\\\":\\\"⊋︀\\\",\\\"Vvdash\\\":\\\"⊪\\\",\\\"vzigzag\\\":\\\"⦚\\\",\\\"Wcirc\\\":\\\"Ŵ\\\",\\\"wcirc\\\":\\\"ŵ\\\",\\\"wedbar\\\":\\\"⩟\\\",\\\"wedge\\\":\\\"∧\\\",\\\"Wedge\\\":\\\"⋀\\\",\\\"wedgeq\\\":\\\"≙\\\",\\\"weierp\\\":\\\"℘\\\",\\\"Wfr\\\":\\\"𝔚\\\",\\\"wfr\\\":\\\"𝔴\\\",\\\"Wopf\\\":\\\"𝕎\\\",\\\"wopf\\\":\\\"𝕨\\\",\\\"wp\\\":\\\"℘\\\",\\\"wr\\\":\\\"≀\\\",\\\"wreath\\\":\\\"≀\\\",\\\"Wscr\\\":\\\"𝒲\\\",\\\"wscr\\\":\\\"𝓌\\\",\\\"xcap\\\":\\\"⋂\\\",\\\"xcirc\\\":\\\"◯\\\",\\\"xcup\\\":\\\"⋃\\\",\\\"xdtri\\\":\\\"▽\\\",\\\"Xfr\\\":\\\"𝔛\\\",\\\"xfr\\\":\\\"𝔵\\\",\\\"xharr\\\":\\\"⟷\\\",\\\"xhArr\\\":\\\"⟺\\\",\\\"Xi\\\":\\\"Ξ\\\",\\\"xi\\\":\\\"ξ\\\",\\\"xlarr\\\":\\\"⟵\\\",\\\"xlArr\\\":\\\"⟸\\\",\\\"xmap\\\":\\\"⟼\\\",\\\"xnis\\\":\\\"⋻\\\",\\\"xodot\\\":\\\"⨀\\\",\\\"Xopf\\\":\\\"𝕏\\\",\\\"xopf\\\":\\\"𝕩\\\",\\\"xoplus\\\":\\\"⨁\\\",\\\"xotime\\\":\\\"⨂\\\",\\\"xrarr\\\":\\\"⟶\\\",\\\"xrArr\\\":\\\"⟹\\\",\\\"Xscr\\\":\\\"𝒳\\\",\\\"xscr\\\":\\\"𝓍\\\",\\\"xsqcup\\\":\\\"⨆\\\",\\\"xuplus\\\":\\\"⨄\\\",\\\"xutri\\\":\\\"△\\\",\\\"xvee\\\":\\\"⋁\\\",\\\"xwedge\\\":\\\"⋀\\\",\\\"Yacute\\\":\\\"Ý\\\",\\\"yacute\\\":\\\"ý\\\",\\\"YAcy\\\":\\\"Я\\\",\\\"yacy\\\":\\\"я\\\",\\\"Ycirc\\\":\\\"Ŷ\\\",\\\"ycirc\\\":\\\"ŷ\\\",\\\"Ycy\\\":\\\"Ы\\\",\\\"ycy\\\":\\\"ы\\\",\\\"yen\\\":\\\"¥\\\",\\\"Yfr\\\":\\\"𝔜\\\",\\\"yfr\\\":\\\"𝔶\\\",\\\"YIcy\\\":\\\"Ї\\\",\\\"yicy\\\":\\\"ї\\\",\\\"Yopf\\\":\\\"𝕐\\\",\\\"yopf\\\":\\\"𝕪\\\",\\\"Yscr\\\":\\\"𝒴\\\",\\\"yscr\\\":\\\"𝓎\\\",\\\"YUcy\\\":\\\"Ю\\\",\\\"yucy\\\":\\\"ю\\\",\\\"yuml\\\":\\\"ÿ\\\",\\\"Yuml\\\":\\\"Ÿ\\\",\\\"Zacute\\\":\\\"Ź\\\",\\\"zacute\\\":\\\"ź\\\",\\\"Zcaron\\\":\\\"Ž\\\",\\\"zcaron\\\":\\\"ž\\\",\\\"Zcy\\\":\\\"З\\\",\\\"zcy\\\":\\\"з\\\",\\\"Zdot\\\":\\\"Ż\\\",\\\"zdot\\\":\\\"ż\\\",\\\"zeetrf\\\":\\\"ℨ\\\",\\\"ZeroWidthSpace\\\":\\\"​\\\",\\\"Zeta\\\":\\\"Ζ\\\",\\\"zeta\\\":\\\"ζ\\\",\\\"zfr\\\":\\\"𝔷\\\",\\\"Zfr\\\":\\\"ℨ\\\",\\\"ZHcy\\\":\\\"Ж\\\",\\\"zhcy\\\":\\\"ж\\\",\\\"zigrarr\\\":\\\"⇝\\\",\\\"zopf\\\":\\\"𝕫\\\",\\\"Zopf\\\":\\\"ℤ\\\",\\\"Zscr\\\":\\\"𝒵\\\",\\\"zscr\\\":\\\"𝓏\\\",\\\"zwj\\\":\\\"‍\\\",\\\"zwnj\\\":\\\"‌\\\"}\");\n\n//# sourceURL=webpack:///./node_modules/entities/lib/maps/entities.json?"); - -/***/ }), - -/***/ "./node_modules/linkify-it/index.js": -/*!******************************************!*\ - !*** ./node_modules/linkify-it/index.js ***! - \******************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\n\n////////////////////////////////////////////////////////////////////////////////\n// Helpers\n\n// Merge objects\n//\nfunction assign(obj /*from1, from2, from3, ...*/) {\n var sources = Array.prototype.slice.call(arguments, 1);\n\n sources.forEach(function (source) {\n if (!source) { return; }\n\n Object.keys(source).forEach(function (key) {\n obj[key] = source[key];\n });\n });\n\n return obj;\n}\n\nfunction _class(obj) { return Object.prototype.toString.call(obj); }\nfunction isString(obj) { return _class(obj) === '[object String]'; }\nfunction isObject(obj) { return _class(obj) === '[object Object]'; }\nfunction isRegExp(obj) { return _class(obj) === '[object RegExp]'; }\nfunction isFunction(obj) { return _class(obj) === '[object Function]'; }\n\n\nfunction escapeRE(str) { return str.replace(/[.?*+^$[\\]\\\\(){}|-]/g, '\\\\$&'); }\n\n////////////////////////////////////////////////////////////////////////////////\n\n\nvar defaultOptions = {\n fuzzyLink: true,\n fuzzyEmail: true,\n fuzzyIP: false\n};\n\n\nfunction isOptionsObj(obj) {\n return Object.keys(obj || {}).reduce(function (acc, k) {\n return acc || defaultOptions.hasOwnProperty(k);\n }, false);\n}\n\n\nvar defaultSchemas = {\n 'http:': {\n validate: function (text, pos, self) {\n var tail = text.slice(pos);\n\n if (!self.re.http) {\n // compile lazily, because \"host\"-containing variables can change on tlds update.\n self.re.http = new RegExp(\n '^\\\\/\\\\/' + self.re.src_auth + self.re.src_host_port_strict + self.re.src_path, 'i'\n );\n }\n if (self.re.http.test(tail)) {\n return tail.match(self.re.http)[0].length;\n }\n return 0;\n }\n },\n 'https:': 'http:',\n 'ftp:': 'http:',\n '//': {\n validate: function (text, pos, self) {\n var tail = text.slice(pos);\n\n if (!self.re.no_http) {\n // compile lazily, because \"host\"-containing variables can change on tlds update.\n self.re.no_http = new RegExp(\n '^' +\n self.re.src_auth +\n // Don't allow single-level domains, because of false positives like '//test'\n // with code comments\n '(?:localhost|(?:(?:' + self.re.src_domain + ')\\\\.)+' + self.re.src_domain_root + ')' +\n self.re.src_port +\n self.re.src_host_terminator +\n self.re.src_path,\n\n 'i'\n );\n }\n\n if (self.re.no_http.test(tail)) {\n // should not be `://` & `///`, that protects from errors in protocol name\n if (pos >= 3 && text[pos - 3] === ':') { return 0; }\n if (pos >= 3 && text[pos - 3] === '/') { return 0; }\n return tail.match(self.re.no_http)[0].length;\n }\n return 0;\n }\n },\n 'mailto:': {\n validate: function (text, pos, self) {\n var tail = text.slice(pos);\n\n if (!self.re.mailto) {\n self.re.mailto = new RegExp(\n '^' + self.re.src_email_name + '@' + self.re.src_host_strict, 'i'\n );\n }\n if (self.re.mailto.test(tail)) {\n return tail.match(self.re.mailto)[0].length;\n }\n return 0;\n }\n }\n};\n\n/*eslint-disable max-len*/\n\n// RE pattern for 2-character tlds (autogenerated by ./support/tlds_2char_gen.js)\nvar tlds_2ch_src_re = 'a[cdefgilmnoqrstuwxz]|b[abdefghijmnorstvwyz]|c[acdfghiklmnoruvwxyz]|d[ejkmoz]|e[cegrstu]|f[ijkmor]|g[abdefghilmnpqrstuwy]|h[kmnrtu]|i[delmnoqrst]|j[emop]|k[eghimnprwyz]|l[abcikrstuvy]|m[acdeghklmnopqrstuvwxyz]|n[acefgilopruz]|om|p[aefghklmnrstwy]|qa|r[eosuw]|s[abcdeghijklmnortuvxyz]|t[cdfghjklmnortvwz]|u[agksyz]|v[aceginu]|w[fs]|y[et]|z[amw]';\n\n// DON'T try to make PRs with changes. Extend TLDs with LinkifyIt.tlds() instead\nvar tlds_default = 'biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф'.split('|');\n\n/*eslint-enable max-len*/\n\n////////////////////////////////////////////////////////////////////////////////\n\nfunction resetScanCache(self) {\n self.__index__ = -1;\n self.__text_cache__ = '';\n}\n\nfunction createValidator(re) {\n return function (text, pos) {\n var tail = text.slice(pos);\n\n if (re.test(tail)) {\n return tail.match(re)[0].length;\n }\n return 0;\n };\n}\n\nfunction createNormalizer() {\n return function (match, self) {\n self.normalize(match);\n };\n}\n\n// Schemas compiler. Build regexps.\n//\nfunction compile(self) {\n\n // Load & clone RE patterns.\n var re = self.re = __webpack_require__(/*! ./lib/re */ \"./node_modules/linkify-it/lib/re.js\")(self.__opts__);\n\n // Define dynamic patterns\n var tlds = self.__tlds__.slice();\n\n self.onCompile();\n\n if (!self.__tlds_replaced__) {\n tlds.push(tlds_2ch_src_re);\n }\n tlds.push(re.src_xn);\n\n re.src_tlds = tlds.join('|');\n\n function untpl(tpl) { return tpl.replace('%TLDS%', re.src_tlds); }\n\n re.email_fuzzy = RegExp(untpl(re.tpl_email_fuzzy), 'i');\n re.link_fuzzy = RegExp(untpl(re.tpl_link_fuzzy), 'i');\n re.link_no_ip_fuzzy = RegExp(untpl(re.tpl_link_no_ip_fuzzy), 'i');\n re.host_fuzzy_test = RegExp(untpl(re.tpl_host_fuzzy_test), 'i');\n\n //\n // Compile each schema\n //\n\n var aliases = [];\n\n self.__compiled__ = {}; // Reset compiled data\n\n function schemaError(name, val) {\n throw new Error('(LinkifyIt) Invalid schema \"' + name + '\": ' + val);\n }\n\n Object.keys(self.__schemas__).forEach(function (name) {\n var val = self.__schemas__[name];\n\n // skip disabled methods\n if (val === null) { return; }\n\n var compiled = { validate: null, link: null };\n\n self.__compiled__[name] = compiled;\n\n if (isObject(val)) {\n if (isRegExp(val.validate)) {\n compiled.validate = createValidator(val.validate);\n } else if (isFunction(val.validate)) {\n compiled.validate = val.validate;\n } else {\n schemaError(name, val);\n }\n\n if (isFunction(val.normalize)) {\n compiled.normalize = val.normalize;\n } else if (!val.normalize) {\n compiled.normalize = createNormalizer();\n } else {\n schemaError(name, val);\n }\n\n return;\n }\n\n if (isString(val)) {\n aliases.push(name);\n return;\n }\n\n schemaError(name, val);\n });\n\n //\n // Compile postponed aliases\n //\n\n aliases.forEach(function (alias) {\n if (!self.__compiled__[self.__schemas__[alias]]) {\n // Silently fail on missed schemas to avoid errons on disable.\n // schemaError(alias, self.__schemas__[alias]);\n return;\n }\n\n self.__compiled__[alias].validate =\n self.__compiled__[self.__schemas__[alias]].validate;\n self.__compiled__[alias].normalize =\n self.__compiled__[self.__schemas__[alias]].normalize;\n });\n\n //\n // Fake record for guessed links\n //\n self.__compiled__[''] = { validate: null, normalize: createNormalizer() };\n\n //\n // Build schema condition\n //\n var slist = Object.keys(self.__compiled__)\n .filter(function (name) {\n // Filter disabled & fake schemas\n return name.length > 0 && self.__compiled__[name];\n })\n .map(escapeRE)\n .join('|');\n // (?!_) cause 1.5x slowdown\n self.re.schema_test = RegExp('(^|(?!_)(?:[><\\uff5c]|' + re.src_ZPCc + '))(' + slist + ')', 'i');\n self.re.schema_search = RegExp('(^|(?!_)(?:[><\\uff5c]|' + re.src_ZPCc + '))(' + slist + ')', 'ig');\n\n self.re.pretest = RegExp(\n '(' + self.re.schema_test.source + ')|(' + self.re.host_fuzzy_test.source + ')|@',\n 'i'\n );\n\n //\n // Cleanup\n //\n\n resetScanCache(self);\n}\n\n/**\n * class Match\n *\n * Match result. Single element of array, returned by [[LinkifyIt#match]]\n **/\nfunction Match(self, shift) {\n var start = self.__index__,\n end = self.__last_index__,\n text = self.__text_cache__.slice(start, end);\n\n /**\n * Match#schema -> String\n *\n * Prefix (protocol) for matched string.\n **/\n this.schema = self.__schema__.toLowerCase();\n /**\n * Match#index -> Number\n *\n * First position of matched string.\n **/\n this.index = start + shift;\n /**\n * Match#lastIndex -> Number\n *\n * Next position after matched string.\n **/\n this.lastIndex = end + shift;\n /**\n * Match#raw -> String\n *\n * Matched string.\n **/\n this.raw = text;\n /**\n * Match#text -> String\n *\n * Notmalized text of matched string.\n **/\n this.text = text;\n /**\n * Match#url -> String\n *\n * Normalized url of matched string.\n **/\n this.url = text;\n}\n\nfunction createMatch(self, shift) {\n var match = new Match(self, shift);\n\n self.__compiled__[match.schema].normalize(match, self);\n\n return match;\n}\n\n\n/**\n * class LinkifyIt\n **/\n\n/**\n * new LinkifyIt(schemas, options)\n * - schemas (Object): Optional. Additional schemas to validate (prefix/validator)\n * - options (Object): { fuzzyLink|fuzzyEmail|fuzzyIP: true|false }\n *\n * Creates new linkifier instance with optional additional schemas.\n * Can be called without `new` keyword for convenience.\n *\n * By default understands:\n *\n * - `http(s)://...` , `ftp://...`, `mailto:...` & `//...` links\n * - \"fuzzy\" links and emails (example.com, foo@bar.com).\n *\n * `schemas` is an object, where each key/value describes protocol/rule:\n *\n * - __key__ - link prefix (usually, protocol name with `:` at the end, `skype:`\n * for example). `linkify-it` makes shure that prefix is not preceeded with\n * alphanumeric char and symbols. Only whitespaces and punctuation allowed.\n * - __value__ - rule to check tail after link prefix\n * - _String_ - just alias to existing rule\n * - _Object_\n * - _validate_ - validator function (should return matched length on success),\n * or `RegExp`.\n * - _normalize_ - optional function to normalize text & url of matched result\n * (for example, for @twitter mentions).\n *\n * `options`:\n *\n * - __fuzzyLink__ - recognige URL-s without `http(s):` prefix. Default `true`.\n * - __fuzzyIP__ - allow IPs in fuzzy links above. Can conflict with some texts\n * like version numbers. Default `false`.\n * - __fuzzyEmail__ - recognize emails without `mailto:` prefix.\n *\n **/\nfunction LinkifyIt(schemas, options) {\n if (!(this instanceof LinkifyIt)) {\n return new LinkifyIt(schemas, options);\n }\n\n if (!options) {\n if (isOptionsObj(schemas)) {\n options = schemas;\n schemas = {};\n }\n }\n\n this.__opts__ = assign({}, defaultOptions, options);\n\n // Cache last tested result. Used to skip repeating steps on next `match` call.\n this.__index__ = -1;\n this.__last_index__ = -1; // Next scan position\n this.__schema__ = '';\n this.__text_cache__ = '';\n\n this.__schemas__ = assign({}, defaultSchemas, schemas);\n this.__compiled__ = {};\n\n this.__tlds__ = tlds_default;\n this.__tlds_replaced__ = false;\n\n this.re = {};\n\n compile(this);\n}\n\n\n/** chainable\n * LinkifyIt#add(schema, definition)\n * - schema (String): rule name (fixed pattern prefix)\n * - definition (String|RegExp|Object): schema definition\n *\n * Add new rule definition. See constructor description for details.\n **/\nLinkifyIt.prototype.add = function add(schema, definition) {\n this.__schemas__[schema] = definition;\n compile(this);\n return this;\n};\n\n\n/** chainable\n * LinkifyIt#set(options)\n * - options (Object): { fuzzyLink|fuzzyEmail|fuzzyIP: true|false }\n *\n * Set recognition options for links without schema.\n **/\nLinkifyIt.prototype.set = function set(options) {\n this.__opts__ = assign(this.__opts__, options);\n return this;\n};\n\n\n/**\n * LinkifyIt#test(text) -> Boolean\n *\n * Searches linkifiable pattern and returns `true` on success or `false` on fail.\n **/\nLinkifyIt.prototype.test = function test(text) {\n // Reset scan cache\n this.__text_cache__ = text;\n this.__index__ = -1;\n\n if (!text.length) { return false; }\n\n var m, ml, me, len, shift, next, re, tld_pos, at_pos;\n\n // try to scan for link with schema - that's the most simple rule\n if (this.re.schema_test.test(text)) {\n re = this.re.schema_search;\n re.lastIndex = 0;\n while ((m = re.exec(text)) !== null) {\n len = this.testSchemaAt(text, m[2], re.lastIndex);\n if (len) {\n this.__schema__ = m[2];\n this.__index__ = m.index + m[1].length;\n this.__last_index__ = m.index + m[0].length + len;\n break;\n }\n }\n }\n\n if (this.__opts__.fuzzyLink && this.__compiled__['http:']) {\n // guess schemaless links\n tld_pos = text.search(this.re.host_fuzzy_test);\n if (tld_pos >= 0) {\n // if tld is located after found link - no need to check fuzzy pattern\n if (this.__index__ < 0 || tld_pos < this.__index__) {\n if ((ml = text.match(this.__opts__.fuzzyIP ? this.re.link_fuzzy : this.re.link_no_ip_fuzzy)) !== null) {\n\n shift = ml.index + ml[1].length;\n\n if (this.__index__ < 0 || shift < this.__index__) {\n this.__schema__ = '';\n this.__index__ = shift;\n this.__last_index__ = ml.index + ml[0].length;\n }\n }\n }\n }\n }\n\n if (this.__opts__.fuzzyEmail && this.__compiled__['mailto:']) {\n // guess schemaless emails\n at_pos = text.indexOf('@');\n if (at_pos >= 0) {\n // We can't skip this check, because this cases are possible:\n // 192.168.1.1@gmail.com, my.in@example.com\n if ((me = text.match(this.re.email_fuzzy)) !== null) {\n\n shift = me.index + me[1].length;\n next = me.index + me[0].length;\n\n if (this.__index__ < 0 || shift < this.__index__ ||\n (shift === this.__index__ && next > this.__last_index__)) {\n this.__schema__ = 'mailto:';\n this.__index__ = shift;\n this.__last_index__ = next;\n }\n }\n }\n }\n\n return this.__index__ >= 0;\n};\n\n\n/**\n * LinkifyIt#pretest(text) -> Boolean\n *\n * Very quick check, that can give false positives. Returns true if link MAY BE\n * can exists. Can be used for speed optimization, when you need to check that\n * link NOT exists.\n **/\nLinkifyIt.prototype.pretest = function pretest(text) {\n return this.re.pretest.test(text);\n};\n\n\n/**\n * LinkifyIt#testSchemaAt(text, name, position) -> Number\n * - text (String): text to scan\n * - name (String): rule (schema) name\n * - position (Number): text offset to check from\n *\n * Similar to [[LinkifyIt#test]] but checks only specific protocol tail exactly\n * at given position. Returns length of found pattern (0 on fail).\n **/\nLinkifyIt.prototype.testSchemaAt = function testSchemaAt(text, schema, pos) {\n // If not supported schema check requested - terminate\n if (!this.__compiled__[schema.toLowerCase()]) {\n return 0;\n }\n return this.__compiled__[schema.toLowerCase()].validate(text, pos, this);\n};\n\n\n/**\n * LinkifyIt#match(text) -> Array|null\n *\n * Returns array of found link descriptions or `null` on fail. We strongly\n * recommend to use [[LinkifyIt#test]] first, for best speed.\n *\n * ##### Result match description\n *\n * - __schema__ - link schema, can be empty for fuzzy links, or `//` for\n * protocol-neutral links.\n * - __index__ - offset of matched text\n * - __lastIndex__ - index of next char after mathch end\n * - __raw__ - matched text\n * - __text__ - normalized text\n * - __url__ - link, generated from matched text\n **/\nLinkifyIt.prototype.match = function match(text) {\n var shift = 0, result = [];\n\n // Try to take previous element from cache, if .test() called before\n if (this.__index__ >= 0 && this.__text_cache__ === text) {\n result.push(createMatch(this, shift));\n shift = this.__last_index__;\n }\n\n // Cut head if cache was used\n var tail = shift ? text.slice(shift) : text;\n\n // Scan string until end reached\n while (this.test(tail)) {\n result.push(createMatch(this, shift));\n\n tail = tail.slice(this.__last_index__);\n shift += this.__last_index__;\n }\n\n if (result.length) {\n return result;\n }\n\n return null;\n};\n\n\n/** chainable\n * LinkifyIt#tlds(list [, keepOld]) -> this\n * - list (Array): list of tlds\n * - keepOld (Boolean): merge with current list if `true` (`false` by default)\n *\n * Load (or merge) new tlds list. Those are user for fuzzy links (without prefix)\n * to avoid false positives. By default this algorythm used:\n *\n * - hostname with any 2-letter root zones are ok.\n * - biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф\n * are ok.\n * - encoded (`xn--...`) root zones are ok.\n *\n * If list is replaced, then exact match for 2-chars root zones will be checked.\n **/\nLinkifyIt.prototype.tlds = function tlds(list, keepOld) {\n list = Array.isArray(list) ? list : [ list ];\n\n if (!keepOld) {\n this.__tlds__ = list.slice();\n this.__tlds_replaced__ = true;\n compile(this);\n return this;\n }\n\n this.__tlds__ = this.__tlds__.concat(list)\n .sort()\n .filter(function (el, idx, arr) {\n return el !== arr[idx - 1];\n })\n .reverse();\n\n compile(this);\n return this;\n};\n\n/**\n * LinkifyIt#normalize(match)\n *\n * Default normalizer (if schema does not define it's own).\n **/\nLinkifyIt.prototype.normalize = function normalize(match) {\n\n // Do minimal possible changes by default. Need to collect feedback prior\n // to move forward https://github.com/markdown-it/linkify-it/issues/1\n\n if (!match.schema) { match.url = 'http://' + match.url; }\n\n if (match.schema === 'mailto:' && !/^mailto:/i.test(match.url)) {\n match.url = 'mailto:' + match.url;\n }\n};\n\n\n/**\n * LinkifyIt#onCompile()\n *\n * Override to modify basic RegExp-s.\n **/\nLinkifyIt.prototype.onCompile = function onCompile() {\n};\n\n\nmodule.exports = LinkifyIt;\n\n\n//# sourceURL=webpack:///./node_modules/linkify-it/index.js?"); - -/***/ }), - -/***/ "./node_modules/linkify-it/lib/re.js": -/*!*******************************************!*\ - !*** ./node_modules/linkify-it/lib/re.js ***! - \*******************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\n\nmodule.exports = function (opts) {\n var re = {};\n\n // Use direct extract instead of `regenerate` to reduse browserified size\n re.src_Any = __webpack_require__(/*! uc.micro/properties/Any/regex */ \"./node_modules/linkify-it/node_modules/uc.micro/properties/Any/regex.js\").source;\n re.src_Cc = __webpack_require__(/*! uc.micro/categories/Cc/regex */ \"./node_modules/linkify-it/node_modules/uc.micro/categories/Cc/regex.js\").source;\n re.src_Z = __webpack_require__(/*! uc.micro/categories/Z/regex */ \"./node_modules/linkify-it/node_modules/uc.micro/categories/Z/regex.js\").source;\n re.src_P = __webpack_require__(/*! uc.micro/categories/P/regex */ \"./node_modules/linkify-it/node_modules/uc.micro/categories/P/regex.js\").source;\n\n // \\p{\\Z\\P\\Cc\\CF} (white spaces + control + format + punctuation)\n re.src_ZPCc = [ re.src_Z, re.src_P, re.src_Cc ].join('|');\n\n // \\p{\\Z\\Cc} (white spaces + control)\n re.src_ZCc = [ re.src_Z, re.src_Cc ].join('|');\n\n // Experimental. List of chars, completely prohibited in links\n // because can separate it from other part of text\n var text_separators = '[><\\uff5c]';\n\n // All possible word characters (everything without punctuation, spaces & controls)\n // Defined via punctuation & spaces to save space\n // Should be something like \\p{\\L\\N\\S\\M} (\\w but without `_`)\n re.src_pseudo_letter = '(?:(?!' + text_separators + '|' + re.src_ZPCc + ')' + re.src_Any + ')';\n // The same as abothe but without [0-9]\n // var src_pseudo_letter_non_d = '(?:(?![0-9]|' + src_ZPCc + ')' + src_Any + ')';\n\n ////////////////////////////////////////////////////////////////////////////////\n\n re.src_ip4 =\n\n '(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)';\n\n // Prohibit any of \"@/[]()\" in user/pass to avoid wrong domain fetch.\n re.src_auth = '(?:(?:(?!' + re.src_ZCc + '|[@/\\\\[\\\\]()]).)+@)?';\n\n re.src_port =\n\n '(?::(?:6(?:[0-4]\\\\d{3}|5(?:[0-4]\\\\d{2}|5(?:[0-2]\\\\d|3[0-5])))|[1-5]?\\\\d{1,4}))?';\n\n re.src_host_terminator =\n\n '(?=$|' + text_separators + '|' + re.src_ZPCc + ')(?!-|_|:\\\\d|\\\\.-|\\\\.(?!$|' + re.src_ZPCc + '))';\n\n re.src_path =\n\n '(?:' +\n '[/?#]' +\n '(?:' +\n '(?!' + re.src_ZCc + '|' + text_separators + '|[()[\\\\]{}.,\"\\'?!\\\\-]).|' +\n '\\\\[(?:(?!' + re.src_ZCc + '|\\\\]).)*\\\\]|' +\n '\\\\((?:(?!' + re.src_ZCc + '|[)]).)*\\\\)|' +\n '\\\\{(?:(?!' + re.src_ZCc + '|[}]).)*\\\\}|' +\n '\\\\\"(?:(?!' + re.src_ZCc + '|[\"]).)+\\\\\"|' +\n \"\\\\'(?:(?!\" + re.src_ZCc + \"|[']).)+\\\\'|\" +\n \"\\\\'(?=\" + re.src_pseudo_letter + '|[-]).|' + // allow `I'm_king` if no pair found\n '\\\\.{2,}[a-zA-Z0-9%/&]|' + // google has many dots in \"google search\" links (#66, #81).\n // github has ... in commit range links,\n // Restrict to\n // - english\n // - percent-encoded\n // - parts of file path\n // - params separator\n // until more examples found.\n '\\\\.(?!' + re.src_ZCc + '|[.]).|' +\n (opts && opts['---'] ?\n '\\\\-(?!--(?:[^-]|$))(?:-*)|' // `---` => long dash, terminate\n :\n '\\\\-+|'\n ) +\n '\\\\,(?!' + re.src_ZCc + ').|' + // allow `,,,` in paths\n '\\\\!+(?!' + re.src_ZCc + '|[!]).|' + // allow `!!!` in paths, but not at the end\n '\\\\?(?!' + re.src_ZCc + '|[?]).' +\n ')+' +\n '|\\\\/' +\n ')?';\n\n // Allow anything in markdown spec, forbid quote (\") at the first position\n // because emails enclosed in quotes are far more common\n re.src_email_name =\n\n '[\\\\-;:&=\\\\+\\\\$,\\\\.a-zA-Z0-9_][\\\\-;:&=\\\\+\\\\$,\\\\\"\\\\.a-zA-Z0-9_]*';\n\n re.src_xn =\n\n 'xn--[a-z0-9\\\\-]{1,59}';\n\n // More to read about domain names\n // http://serverfault.com/questions/638260/\n\n re.src_domain_root =\n\n // Allow letters & digits (http://test1)\n '(?:' +\n re.src_xn +\n '|' +\n re.src_pseudo_letter + '{1,63}' +\n ')';\n\n re.src_domain =\n\n '(?:' +\n re.src_xn +\n '|' +\n '(?:' + re.src_pseudo_letter + ')' +\n '|' +\n '(?:' + re.src_pseudo_letter + '(?:-|' + re.src_pseudo_letter + '){0,61}' + re.src_pseudo_letter + ')' +\n ')';\n\n re.src_host =\n\n '(?:' +\n // Don't need IP check, because digits are already allowed in normal domain names\n // src_ip4 +\n // '|' +\n '(?:(?:(?:' + re.src_domain + ')\\\\.)*' + re.src_domain/*_root*/ + ')' +\n ')';\n\n re.tpl_host_fuzzy =\n\n '(?:' +\n re.src_ip4 +\n '|' +\n '(?:(?:(?:' + re.src_domain + ')\\\\.)+(?:%TLDS%))' +\n ')';\n\n re.tpl_host_no_ip_fuzzy =\n\n '(?:(?:(?:' + re.src_domain + ')\\\\.)+(?:%TLDS%))';\n\n re.src_host_strict =\n\n re.src_host + re.src_host_terminator;\n\n re.tpl_host_fuzzy_strict =\n\n re.tpl_host_fuzzy + re.src_host_terminator;\n\n re.src_host_port_strict =\n\n re.src_host + re.src_port + re.src_host_terminator;\n\n re.tpl_host_port_fuzzy_strict =\n\n re.tpl_host_fuzzy + re.src_port + re.src_host_terminator;\n\n re.tpl_host_port_no_ip_fuzzy_strict =\n\n re.tpl_host_no_ip_fuzzy + re.src_port + re.src_host_terminator;\n\n\n ////////////////////////////////////////////////////////////////////////////////\n // Main rules\n\n // Rude test fuzzy links by host, for quick deny\n re.tpl_host_fuzzy_test =\n\n 'localhost|www\\\\.|\\\\.\\\\d{1,3}\\\\.|(?:\\\\.(?:%TLDS%)(?:' + re.src_ZPCc + '|>|$))';\n\n re.tpl_email_fuzzy =\n\n '(^|' + text_separators + '|\"|\\\\(|' + re.src_ZCc + ')' +\n '(' + re.src_email_name + '@' + re.tpl_host_fuzzy_strict + ')';\n\n re.tpl_link_fuzzy =\n // Fuzzy link can't be prepended with .:/\\- and non punctuation.\n // but can start with > (markdown blockquote)\n '(^|(?![.:/\\\\-_@])(?:[$+<=>^`|\\uff5c]|' + re.src_ZPCc + '))' +\n '((?![$+<=>^`|\\uff5c])' + re.tpl_host_port_fuzzy_strict + re.src_path + ')';\n\n re.tpl_link_no_ip_fuzzy =\n // Fuzzy link can't be prepended with .:/\\- and non punctuation.\n // but can start with > (markdown blockquote)\n '(^|(?![.:/\\\\-_@])(?:[$+<=>^`|\\uff5c]|' + re.src_ZPCc + '))' +\n '((?![$+<=>^`|\\uff5c])' + re.tpl_host_port_no_ip_fuzzy_strict + re.src_path + ')';\n\n return re;\n};\n\n\n//# sourceURL=webpack:///./node_modules/linkify-it/lib/re.js?"); - -/***/ }), - -/***/ "./node_modules/linkify-it/node_modules/uc.micro/categories/Cc/regex.js": -/*!******************************************************************************!*\ - !*** ./node_modules/linkify-it/node_modules/uc.micro/categories/Cc/regex.js ***! - \******************************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports) { - -eval("module.exports=/[\\0-\\x1F\\x7F-\\x9F]/\n\n//# sourceURL=webpack:///./node_modules/linkify-it/node_modules/uc.micro/categories/Cc/regex.js?"); - -/***/ }), - -/***/ "./node_modules/linkify-it/node_modules/uc.micro/categories/P/regex.js": -/*!*****************************************************************************!*\ - !*** ./node_modules/linkify-it/node_modules/uc.micro/categories/P/regex.js ***! - \*****************************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports) { - -eval("module.exports=/[!-#%-\\*,-/:;\\?@\\[-\\]_\\{\\}\\xA1\\xA7\\xAB\\xB6\\xB7\\xBB\\xBF\\u037E\\u0387\\u055A-\\u055F\\u0589\\u058A\\u05BE\\u05C0\\u05C3\\u05C6\\u05F3\\u05F4\\u0609\\u060A\\u060C\\u060D\\u061B\\u061E\\u061F\\u066A-\\u066D\\u06D4\\u0700-\\u070D\\u07F7-\\u07F9\\u0830-\\u083E\\u085E\\u0964\\u0965\\u0970\\u0AF0\\u0DF4\\u0E4F\\u0E5A\\u0E5B\\u0F04-\\u0F12\\u0F14\\u0F3A-\\u0F3D\\u0F85\\u0FD0-\\u0FD4\\u0FD9\\u0FDA\\u104A-\\u104F\\u10FB\\u1360-\\u1368\\u1400\\u166D\\u166E\\u169B\\u169C\\u16EB-\\u16ED\\u1735\\u1736\\u17D4-\\u17D6\\u17D8-\\u17DA\\u1800-\\u180A\\u1944\\u1945\\u1A1E\\u1A1F\\u1AA0-\\u1AA6\\u1AA8-\\u1AAD\\u1B5A-\\u1B60\\u1BFC-\\u1BFF\\u1C3B-\\u1C3F\\u1C7E\\u1C7F\\u1CC0-\\u1CC7\\u1CD3\\u2010-\\u2027\\u2030-\\u2043\\u2045-\\u2051\\u2053-\\u205E\\u207D\\u207E\\u208D\\u208E\\u2308-\\u230B\\u2329\\u232A\\u2768-\\u2775\\u27C5\\u27C6\\u27E6-\\u27EF\\u2983-\\u2998\\u29D8-\\u29DB\\u29FC\\u29FD\\u2CF9-\\u2CFC\\u2CFE\\u2CFF\\u2D70\\u2E00-\\u2E2E\\u2E30-\\u2E44\\u3001-\\u3003\\u3008-\\u3011\\u3014-\\u301F\\u3030\\u303D\\u30A0\\u30FB\\uA4FE\\uA4FF\\uA60D-\\uA60F\\uA673\\uA67E\\uA6F2-\\uA6F7\\uA874-\\uA877\\uA8CE\\uA8CF\\uA8F8-\\uA8FA\\uA8FC\\uA92E\\uA92F\\uA95F\\uA9C1-\\uA9CD\\uA9DE\\uA9DF\\uAA5C-\\uAA5F\\uAADE\\uAADF\\uAAF0\\uAAF1\\uABEB\\uFD3E\\uFD3F\\uFE10-\\uFE19\\uFE30-\\uFE52\\uFE54-\\uFE61\\uFE63\\uFE68\\uFE6A\\uFE6B\\uFF01-\\uFF03\\uFF05-\\uFF0A\\uFF0C-\\uFF0F\\uFF1A\\uFF1B\\uFF1F\\uFF20\\uFF3B-\\uFF3D\\uFF3F\\uFF5B\\uFF5D\\uFF5F-\\uFF65]|\\uD800[\\uDD00-\\uDD02\\uDF9F\\uDFD0]|\\uD801\\uDD6F|\\uD802[\\uDC57\\uDD1F\\uDD3F\\uDE50-\\uDE58\\uDE7F\\uDEF0-\\uDEF6\\uDF39-\\uDF3F\\uDF99-\\uDF9C]|\\uD804[\\uDC47-\\uDC4D\\uDCBB\\uDCBC\\uDCBE-\\uDCC1\\uDD40-\\uDD43\\uDD74\\uDD75\\uDDC5-\\uDDC9\\uDDCD\\uDDDB\\uDDDD-\\uDDDF\\uDE38-\\uDE3D\\uDEA9]|\\uD805[\\uDC4B-\\uDC4F\\uDC5B\\uDC5D\\uDCC6\\uDDC1-\\uDDD7\\uDE41-\\uDE43\\uDE60-\\uDE6C\\uDF3C-\\uDF3E]|\\uD807[\\uDC41-\\uDC45\\uDC70\\uDC71]|\\uD809[\\uDC70-\\uDC74]|\\uD81A[\\uDE6E\\uDE6F\\uDEF5\\uDF37-\\uDF3B\\uDF44]|\\uD82F\\uDC9F|\\uD836[\\uDE87-\\uDE8B]|\\uD83A[\\uDD5E\\uDD5F]/\n\n//# sourceURL=webpack:///./node_modules/linkify-it/node_modules/uc.micro/categories/P/regex.js?"); - -/***/ }), - -/***/ "./node_modules/linkify-it/node_modules/uc.micro/categories/Z/regex.js": -/*!*****************************************************************************!*\ - !*** ./node_modules/linkify-it/node_modules/uc.micro/categories/Z/regex.js ***! - \*****************************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports) { - -eval("module.exports=/[ \\xA0\\u1680\\u2000-\\u200A\\u202F\\u205F\\u3000]/\n\n//# sourceURL=webpack:///./node_modules/linkify-it/node_modules/uc.micro/categories/Z/regex.js?"); - -/***/ }), - -/***/ "./node_modules/linkify-it/node_modules/uc.micro/properties/Any/regex.js": -/*!*******************************************************************************!*\ - !*** ./node_modules/linkify-it/node_modules/uc.micro/properties/Any/regex.js ***! - \*******************************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports) { - -eval("module.exports=/[\\0-\\uD7FF\\uE000-\\uFFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]|[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?:[^\\uD800-\\uDBFF]|^)[\\uDC00-\\uDFFF]/\n\n//# sourceURL=webpack:///./node_modules/linkify-it/node_modules/uc.micro/properties/Any/regex.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/index.js": -/*!*******************************************!*\ - !*** ./node_modules/markdown-it/index.js ***! - \*******************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\n\nmodule.exports = __webpack_require__(/*! ./lib/ */ \"./node_modules/markdown-it/lib/index.js\");\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/index.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/common/entities.js": -/*!*********************************************************!*\ - !*** ./node_modules/markdown-it/lib/common/entities.js ***! - \*********************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// HTML5 entities map: { name -> utf16string }\n//\n\n\n/*eslint quotes:0*/\nmodule.exports = __webpack_require__(/*! entities/lib/maps/entities.json */ \"./node_modules/entities/lib/maps/entities.json\");\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/common/entities.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/common/html_blocks.js": -/*!************************************************************!*\ - !*** ./node_modules/markdown-it/lib/common/html_blocks.js ***! - \************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// List of valid html blocks names, accorting to commonmark spec\n// http://jgm.github.io/CommonMark/spec.html#html-blocks\n\n\n\n\nmodule.exports = [\n 'address',\n 'article',\n 'aside',\n 'base',\n 'basefont',\n 'blockquote',\n 'body',\n 'caption',\n 'center',\n 'col',\n 'colgroup',\n 'dd',\n 'details',\n 'dialog',\n 'dir',\n 'div',\n 'dl',\n 'dt',\n 'fieldset',\n 'figcaption',\n 'figure',\n 'footer',\n 'form',\n 'frame',\n 'frameset',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'head',\n 'header',\n 'hr',\n 'html',\n 'iframe',\n 'legend',\n 'li',\n 'link',\n 'main',\n 'menu',\n 'menuitem',\n 'nav',\n 'noframes',\n 'ol',\n 'optgroup',\n 'option',\n 'p',\n 'param',\n 'section',\n 'source',\n 'summary',\n 'table',\n 'tbody',\n 'td',\n 'tfoot',\n 'th',\n 'thead',\n 'title',\n 'tr',\n 'track',\n 'ul'\n];\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/common/html_blocks.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/common/html_re.js": -/*!********************************************************!*\ - !*** ./node_modules/markdown-it/lib/common/html_re.js ***! - \********************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Regexps to match html elements\n\n\n\nvar attr_name = '[a-zA-Z_:][a-zA-Z0-9:._-]*';\n\nvar unquoted = '[^\"\\'=<>`\\\\x00-\\\\x20]+';\nvar single_quoted = \"'[^']*'\";\nvar double_quoted = '\"[^\"]*\"';\n\nvar attr_value = '(?:' + unquoted + '|' + single_quoted + '|' + double_quoted + ')';\n\nvar attribute = '(?:\\\\s+' + attr_name + '(?:\\\\s*=\\\\s*' + attr_value + ')?)';\n\nvar open_tag = '<[A-Za-z][A-Za-z0-9\\\\-]*' + attribute + '*\\\\s*\\\\/?>';\n\nvar close_tag = '<\\\\/[A-Za-z][A-Za-z0-9\\\\-]*\\\\s*>';\nvar comment = '|';\nvar processing = '<[?][\\\\s\\\\S]*?[?]>';\nvar declaration = ']*>';\nvar cdata = '';\n\nvar HTML_TAG_RE = new RegExp('^(?:' + open_tag + '|' + close_tag + '|' + comment +\n '|' + processing + '|' + declaration + '|' + cdata + ')');\nvar HTML_OPEN_CLOSE_TAG_RE = new RegExp('^(?:' + open_tag + '|' + close_tag + ')');\n\nmodule.exports.HTML_TAG_RE = HTML_TAG_RE;\nmodule.exports.HTML_OPEN_CLOSE_TAG_RE = HTML_OPEN_CLOSE_TAG_RE;\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/common/html_re.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/common/utils.js": -/*!******************************************************!*\ - !*** ./node_modules/markdown-it/lib/common/utils.js ***! - \******************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Utilities\n//\n\n\n\nfunction _class(obj) { return Object.prototype.toString.call(obj); }\n\nfunction isString(obj) { return _class(obj) === '[object String]'; }\n\nvar _hasOwnProperty = Object.prototype.hasOwnProperty;\n\nfunction has(object, key) {\n return _hasOwnProperty.call(object, key);\n}\n\n// Merge objects\n//\nfunction assign(obj /*from1, from2, from3, ...*/) {\n var sources = Array.prototype.slice.call(arguments, 1);\n\n sources.forEach(function (source) {\n if (!source) { return; }\n\n if (typeof source !== 'object') {\n throw new TypeError(source + 'must be object');\n }\n\n Object.keys(source).forEach(function (key) {\n obj[key] = source[key];\n });\n });\n\n return obj;\n}\n\n// Remove element from array and put another array at those position.\n// Useful for some operations with tokens\nfunction arrayReplaceAt(src, pos, newElements) {\n return [].concat(src.slice(0, pos), newElements, src.slice(pos + 1));\n}\n\n////////////////////////////////////////////////////////////////////////////////\n\nfunction isValidEntityCode(c) {\n /*eslint no-bitwise:0*/\n // broken sequence\n if (c >= 0xD800 && c <= 0xDFFF) { return false; }\n // never used\n if (c >= 0xFDD0 && c <= 0xFDEF) { return false; }\n if ((c & 0xFFFF) === 0xFFFF || (c & 0xFFFF) === 0xFFFE) { return false; }\n // control codes\n if (c >= 0x00 && c <= 0x08) { return false; }\n if (c === 0x0B) { return false; }\n if (c >= 0x0E && c <= 0x1F) { return false; }\n if (c >= 0x7F && c <= 0x9F) { return false; }\n // out of range\n if (c > 0x10FFFF) { return false; }\n return true;\n}\n\nfunction fromCodePoint(c) {\n /*eslint no-bitwise:0*/\n if (c > 0xffff) {\n c -= 0x10000;\n var surrogate1 = 0xd800 + (c >> 10),\n surrogate2 = 0xdc00 + (c & 0x3ff);\n\n return String.fromCharCode(surrogate1, surrogate2);\n }\n return String.fromCharCode(c);\n}\n\n\nvar UNESCAPE_MD_RE = /\\\\([!\"#$%&'()*+,\\-.\\/:;<=>?@[\\\\\\]^_`{|}~])/g;\nvar ENTITY_RE = /&([a-z#][a-z0-9]{1,31});/gi;\nvar UNESCAPE_ALL_RE = new RegExp(UNESCAPE_MD_RE.source + '|' + ENTITY_RE.source, 'gi');\n\nvar DIGITAL_ENTITY_TEST_RE = /^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))/i;\n\nvar entities = __webpack_require__(/*! ./entities */ \"./node_modules/markdown-it/lib/common/entities.js\");\n\nfunction replaceEntityPattern(match, name) {\n var code = 0;\n\n if (has(entities, name)) {\n return entities[name];\n }\n\n if (name.charCodeAt(0) === 0x23/* # */ && DIGITAL_ENTITY_TEST_RE.test(name)) {\n code = name[1].toLowerCase() === 'x' ?\n parseInt(name.slice(2), 16) : parseInt(name.slice(1), 10);\n\n if (isValidEntityCode(code)) {\n return fromCodePoint(code);\n }\n }\n\n return match;\n}\n\n/*function replaceEntities(str) {\n if (str.indexOf('&') < 0) { return str; }\n\n return str.replace(ENTITY_RE, replaceEntityPattern);\n}*/\n\nfunction unescapeMd(str) {\n if (str.indexOf('\\\\') < 0) { return str; }\n return str.replace(UNESCAPE_MD_RE, '$1');\n}\n\nfunction unescapeAll(str) {\n if (str.indexOf('\\\\') < 0 && str.indexOf('&') < 0) { return str; }\n\n return str.replace(UNESCAPE_ALL_RE, function (match, escaped, entity) {\n if (escaped) { return escaped; }\n return replaceEntityPattern(match, entity);\n });\n}\n\n////////////////////////////////////////////////////////////////////////////////\n\nvar HTML_ESCAPE_TEST_RE = /[&<>\"]/;\nvar HTML_ESCAPE_REPLACE_RE = /[&<>\"]/g;\nvar HTML_REPLACEMENTS = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"'\n};\n\nfunction replaceUnsafeChar(ch) {\n return HTML_REPLACEMENTS[ch];\n}\n\nfunction escapeHtml(str) {\n if (HTML_ESCAPE_TEST_RE.test(str)) {\n return str.replace(HTML_ESCAPE_REPLACE_RE, replaceUnsafeChar);\n }\n return str;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n\nvar REGEXP_ESCAPE_RE = /[.?*+^$[\\]\\\\(){}|-]/g;\n\nfunction escapeRE(str) {\n return str.replace(REGEXP_ESCAPE_RE, '\\\\$&');\n}\n\n////////////////////////////////////////////////////////////////////////////////\n\nfunction isSpace(code) {\n switch (code) {\n case 0x09:\n case 0x20:\n return true;\n }\n return false;\n}\n\n// Zs (unicode class) || [\\t\\f\\v\\r\\n]\nfunction isWhiteSpace(code) {\n if (code >= 0x2000 && code <= 0x200A) { return true; }\n switch (code) {\n case 0x09: // \\t\n case 0x0A: // \\n\n case 0x0B: // \\v\n case 0x0C: // \\f\n case 0x0D: // \\r\n case 0x20:\n case 0xA0:\n case 0x1680:\n case 0x202F:\n case 0x205F:\n case 0x3000:\n return true;\n }\n return false;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n\n/*eslint-disable max-len*/\nvar UNICODE_PUNCT_RE = __webpack_require__(/*! uc.micro/categories/P/regex */ \"./node_modules/uc.micro/categories/P/regex.js\");\n\n// Currently without astral characters support.\nfunction isPunctChar(ch) {\n return UNICODE_PUNCT_RE.test(ch);\n}\n\n\n// Markdown ASCII punctuation characters.\n//\n// !, \", #, $, %, &, ', (, ), *, +, ,, -, ., /, :, ;, <, =, >, ?, @, [, \\, ], ^, _, `, {, |, }, or ~\n// http://spec.commonmark.org/0.15/#ascii-punctuation-character\n//\n// Don't confuse with unicode punctuation !!! It lacks some chars in ascii range.\n//\nfunction isMdAsciiPunct(ch) {\n switch (ch) {\n case 0x21/* ! */:\n case 0x22/* \" */:\n case 0x23/* # */:\n case 0x24/* $ */:\n case 0x25/* % */:\n case 0x26/* & */:\n case 0x27/* ' */:\n case 0x28/* ( */:\n case 0x29/* ) */:\n case 0x2A/* * */:\n case 0x2B/* + */:\n case 0x2C/* , */:\n case 0x2D/* - */:\n case 0x2E/* . */:\n case 0x2F/* / */:\n case 0x3A/* : */:\n case 0x3B/* ; */:\n case 0x3C/* < */:\n case 0x3D/* = */:\n case 0x3E/* > */:\n case 0x3F/* ? */:\n case 0x40/* @ */:\n case 0x5B/* [ */:\n case 0x5C/* \\ */:\n case 0x5D/* ] */:\n case 0x5E/* ^ */:\n case 0x5F/* _ */:\n case 0x60/* ` */:\n case 0x7B/* { */:\n case 0x7C/* | */:\n case 0x7D/* } */:\n case 0x7E/* ~ */:\n return true;\n default:\n return false;\n }\n}\n\n// Hepler to unify [reference labels].\n//\nfunction normalizeReference(str) {\n // Trim and collapse whitespace\n //\n str = str.trim().replace(/\\s+/g, ' ');\n\n // In node v10 'ẞ'.toLowerCase() === 'Ṿ', which is presumed to be a bug\n // fixed in v12 (couldn't find any details).\n //\n // So treat this one as a special case\n // (remove this when node v10 is no longer supported).\n //\n if ('ẞ'.toLowerCase() === 'Ṿ') {\n str = str.replace(/ẞ/g, 'ß');\n }\n\n // .toLowerCase().toUpperCase() should get rid of all differences\n // between letter variants.\n //\n // Simple .toLowerCase() doesn't normalize 125 code points correctly,\n // and .toUpperCase doesn't normalize 6 of them (list of exceptions:\n // İ, ϴ, ẞ, Ω, K, Å - those are already uppercased, but have differently\n // uppercased versions).\n //\n // Here's an example showing how it happens. Lets take greek letter omega:\n // uppercase U+0398 (Θ), U+03f4 (ϴ) and lowercase U+03b8 (θ), U+03d1 (ϑ)\n //\n // Unicode entries:\n // 0398;GREEK CAPITAL LETTER THETA;Lu;0;L;;;;;N;;;;03B8;\n // 03B8;GREEK SMALL LETTER THETA;Ll;0;L;;;;;N;;;0398;;0398\n // 03D1;GREEK THETA SYMBOL;Ll;0;L; 03B8;;;;N;GREEK SMALL LETTER SCRIPT THETA;;0398;;0398\n // 03F4;GREEK CAPITAL THETA SYMBOL;Lu;0;L; 0398;;;;N;;;;03B8;\n //\n // Case-insensitive comparison should treat all of them as equivalent.\n //\n // But .toLowerCase() doesn't change ϑ (it's already lowercase),\n // and .toUpperCase() doesn't change ϴ (already uppercase).\n //\n // Applying first lower then upper case normalizes any character:\n // '\\u0398\\u03f4\\u03b8\\u03d1'.toLowerCase().toUpperCase() === '\\u0398\\u0398\\u0398\\u0398'\n //\n // Note: this is equivalent to unicode case folding; unicode normalization\n // is a different step that is not required here.\n //\n // Final result should be uppercased, because it's later stored in an object\n // (this avoid a conflict with Object.prototype members,\n // most notably, `__proto__`)\n //\n return str.toLowerCase().toUpperCase();\n}\n\n////////////////////////////////////////////////////////////////////////////////\n\n// Re-export libraries commonly used in both markdown-it and its plugins,\n// so plugins won't have to depend on them explicitly, which reduces their\n// bundled size (e.g. a browser build).\n//\nexports.lib = {};\nexports.lib.mdurl = __webpack_require__(/*! mdurl */ \"./node_modules/mdurl/index.js\");\nexports.lib.ucmicro = __webpack_require__(/*! uc.micro */ \"./node_modules/uc.micro/index.js\");\n\nexports.assign = assign;\nexports.isString = isString;\nexports.has = has;\nexports.unescapeMd = unescapeMd;\nexports.unescapeAll = unescapeAll;\nexports.isValidEntityCode = isValidEntityCode;\nexports.fromCodePoint = fromCodePoint;\n// exports.replaceEntities = replaceEntities;\nexports.escapeHtml = escapeHtml;\nexports.arrayReplaceAt = arrayReplaceAt;\nexports.isSpace = isSpace;\nexports.isWhiteSpace = isWhiteSpace;\nexports.isMdAsciiPunct = isMdAsciiPunct;\nexports.isPunctChar = isPunctChar;\nexports.escapeRE = escapeRE;\nexports.normalizeReference = normalizeReference;\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/common/utils.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/helpers/index.js": -/*!*******************************************************!*\ - !*** ./node_modules/markdown-it/lib/helpers/index.js ***! - \*******************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Just a shortcut for bulk export\n\n\n\nexports.parseLinkLabel = __webpack_require__(/*! ./parse_link_label */ \"./node_modules/markdown-it/lib/helpers/parse_link_label.js\");\nexports.parseLinkDestination = __webpack_require__(/*! ./parse_link_destination */ \"./node_modules/markdown-it/lib/helpers/parse_link_destination.js\");\nexports.parseLinkTitle = __webpack_require__(/*! ./parse_link_title */ \"./node_modules/markdown-it/lib/helpers/parse_link_title.js\");\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/helpers/index.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/helpers/parse_link_destination.js": -/*!************************************************************************!*\ - !*** ./node_modules/markdown-it/lib/helpers/parse_link_destination.js ***! - \************************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Parse link destination\n//\n\n\n\nvar unescapeAll = __webpack_require__(/*! ../common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\").unescapeAll;\n\n\nmodule.exports = function parseLinkDestination(str, pos, max) {\n var code, level,\n lines = 0,\n start = pos,\n result = {\n ok: false,\n pos: 0,\n lines: 0,\n str: ''\n };\n\n if (str.charCodeAt(pos) === 0x3C /* < */) {\n pos++;\n while (pos < max) {\n code = str.charCodeAt(pos);\n if (code === 0x0A /* \\n */) { return result; }\n if (code === 0x3C /* < */) { return result; }\n if (code === 0x3E /* > */) {\n result.pos = pos + 1;\n result.str = unescapeAll(str.slice(start + 1, pos));\n result.ok = true;\n return result;\n }\n if (code === 0x5C /* \\ */ && pos + 1 < max) {\n pos += 2;\n continue;\n }\n\n pos++;\n }\n\n // no closing '>'\n return result;\n }\n\n // this should be ... } else { ... branch\n\n level = 0;\n while (pos < max) {\n code = str.charCodeAt(pos);\n\n if (code === 0x20) { break; }\n\n // ascii control characters\n if (code < 0x20 || code === 0x7F) { break; }\n\n if (code === 0x5C /* \\ */ && pos + 1 < max) {\n if (str.charCodeAt(pos + 1) === 0x20) { break; }\n pos += 2;\n continue;\n }\n\n if (code === 0x28 /* ( */) {\n level++;\n if (level > 32) { return result; }\n }\n\n if (code === 0x29 /* ) */) {\n if (level === 0) { break; }\n level--;\n }\n\n pos++;\n }\n\n if (start === pos) { return result; }\n if (level !== 0) { return result; }\n\n result.str = unescapeAll(str.slice(start, pos));\n result.lines = lines;\n result.pos = pos;\n result.ok = true;\n return result;\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/helpers/parse_link_destination.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/helpers/parse_link_label.js": -/*!******************************************************************!*\ - !*** ./node_modules/markdown-it/lib/helpers/parse_link_label.js ***! - \******************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Parse link label\n//\n// this function assumes that first character (\"[\") already matches;\n// returns the end of the label\n//\n\n\nmodule.exports = function parseLinkLabel(state, start, disableNested) {\n var level, found, marker, prevPos,\n labelEnd = -1,\n max = state.posMax,\n oldPos = state.pos;\n\n state.pos = start + 1;\n level = 1;\n\n while (state.pos < max) {\n marker = state.src.charCodeAt(state.pos);\n if (marker === 0x5D /* ] */) {\n level--;\n if (level === 0) {\n found = true;\n break;\n }\n }\n\n prevPos = state.pos;\n state.md.inline.skipToken(state);\n if (marker === 0x5B /* [ */) {\n if (prevPos === state.pos - 1) {\n // increase level if we find text `[`, which is not a part of any token\n level++;\n } else if (disableNested) {\n state.pos = oldPos;\n return -1;\n }\n }\n }\n\n if (found) {\n labelEnd = state.pos;\n }\n\n // restore old state\n state.pos = oldPos;\n\n return labelEnd;\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/helpers/parse_link_label.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/helpers/parse_link_title.js": -/*!******************************************************************!*\ - !*** ./node_modules/markdown-it/lib/helpers/parse_link_title.js ***! - \******************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Parse link title\n//\n\n\n\nvar unescapeAll = __webpack_require__(/*! ../common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\").unescapeAll;\n\n\nmodule.exports = function parseLinkTitle(str, pos, max) {\n var code,\n marker,\n lines = 0,\n start = pos,\n result = {\n ok: false,\n pos: 0,\n lines: 0,\n str: ''\n };\n\n if (pos >= max) { return result; }\n\n marker = str.charCodeAt(pos);\n\n if (marker !== 0x22 /* \" */ && marker !== 0x27 /* ' */ && marker !== 0x28 /* ( */) { return result; }\n\n pos++;\n\n // if opening marker is \"(\", switch it to closing marker \")\"\n if (marker === 0x28) { marker = 0x29; }\n\n while (pos < max) {\n code = str.charCodeAt(pos);\n if (code === marker) {\n result.pos = pos + 1;\n result.lines = lines;\n result.str = unescapeAll(str.slice(start + 1, pos));\n result.ok = true;\n return result;\n } else if (code === 0x28 /* ( */ && marker === 0x29 /* ) */) {\n return result;\n } else if (code === 0x0A) {\n lines++;\n } else if (code === 0x5C /* \\ */ && pos + 1 < max) {\n pos++;\n if (str.charCodeAt(pos) === 0x0A) {\n lines++;\n }\n }\n\n pos++;\n }\n\n return result;\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/helpers/parse_link_title.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/index.js": -/*!***********************************************!*\ - !*** ./node_modules/markdown-it/lib/index.js ***! - \***********************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Main parser class\n\n\n\n\nvar utils = __webpack_require__(/*! ./common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\");\nvar helpers = __webpack_require__(/*! ./helpers */ \"./node_modules/markdown-it/lib/helpers/index.js\");\nvar Renderer = __webpack_require__(/*! ./renderer */ \"./node_modules/markdown-it/lib/renderer.js\");\nvar ParserCore = __webpack_require__(/*! ./parser_core */ \"./node_modules/markdown-it/lib/parser_core.js\");\nvar ParserBlock = __webpack_require__(/*! ./parser_block */ \"./node_modules/markdown-it/lib/parser_block.js\");\nvar ParserInline = __webpack_require__(/*! ./parser_inline */ \"./node_modules/markdown-it/lib/parser_inline.js\");\nvar LinkifyIt = __webpack_require__(/*! linkify-it */ \"./node_modules/linkify-it/index.js\");\nvar mdurl = __webpack_require__(/*! mdurl */ \"./node_modules/mdurl/index.js\");\nvar punycode = __webpack_require__(/*! punycode */ \"../../node_modules/punycode/punycode.js\");\n\n\nvar config = {\n default: __webpack_require__(/*! ./presets/default */ \"./node_modules/markdown-it/lib/presets/default.js\"),\n zero: __webpack_require__(/*! ./presets/zero */ \"./node_modules/markdown-it/lib/presets/zero.js\"),\n commonmark: __webpack_require__(/*! ./presets/commonmark */ \"./node_modules/markdown-it/lib/presets/commonmark.js\")\n};\n\n////////////////////////////////////////////////////////////////////////////////\n//\n// This validator can prohibit more than really needed to prevent XSS. It's a\n// tradeoff to keep code simple and to be secure by default.\n//\n// If you need different setup - override validator method as you wish. Or\n// replace it with dummy function and use external sanitizer.\n//\n\nvar BAD_PROTO_RE = /^(vbscript|javascript|file|data):/;\nvar GOOD_DATA_RE = /^data:image\\/(gif|png|jpeg|webp);/;\n\nfunction validateLink(url) {\n // url should be normalized at this point, and existing entities are decoded\n var str = url.trim().toLowerCase();\n\n return BAD_PROTO_RE.test(str) ? (GOOD_DATA_RE.test(str) ? true : false) : true;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n\n\nvar RECODE_HOSTNAME_FOR = [ 'http:', 'https:', 'mailto:' ];\n\nfunction normalizeLink(url) {\n var parsed = mdurl.parse(url, true);\n\n if (parsed.hostname) {\n // Encode hostnames in urls like:\n // `http://host/`, `https://host/`, `mailto:user@host`, `//host/`\n //\n // We don't encode unknown schemas, because it's likely that we encode\n // something we shouldn't (e.g. `skype:name` treated as `skype:host`)\n //\n if (!parsed.protocol || RECODE_HOSTNAME_FOR.indexOf(parsed.protocol) >= 0) {\n try {\n parsed.hostname = punycode.toASCII(parsed.hostname);\n } catch (er) { /**/ }\n }\n }\n\n return mdurl.encode(mdurl.format(parsed));\n}\n\nfunction normalizeLinkText(url) {\n var parsed = mdurl.parse(url, true);\n\n if (parsed.hostname) {\n // Encode hostnames in urls like:\n // `http://host/`, `https://host/`, `mailto:user@host`, `//host/`\n //\n // We don't encode unknown schemas, because it's likely that we encode\n // something we shouldn't (e.g. `skype:name` treated as `skype:host`)\n //\n if (!parsed.protocol || RECODE_HOSTNAME_FOR.indexOf(parsed.protocol) >= 0) {\n try {\n parsed.hostname = punycode.toUnicode(parsed.hostname);\n } catch (er) { /**/ }\n }\n }\n\n // add '%' to exclude list because of https://github.com/markdown-it/markdown-it/issues/720\n return mdurl.decode(mdurl.format(parsed), mdurl.decode.defaultChars + '%');\n}\n\n\n/**\n * class MarkdownIt\n *\n * Main parser/renderer class.\n *\n * ##### Usage\n *\n * ```javascript\n * // node.js, \"classic\" way:\n * var MarkdownIt = require('markdown-it'),\n * md = new MarkdownIt();\n * var result = md.render('# markdown-it rulezz!');\n *\n * // node.js, the same, but with sugar:\n * var md = require('markdown-it')();\n * var result = md.render('# markdown-it rulezz!');\n *\n * // browser without AMD, added to \"window\" on script load\n * // Note, there are no dash.\n * var md = window.markdownit();\n * var result = md.render('# markdown-it rulezz!');\n * ```\n *\n * Single line rendering, without paragraph wrap:\n *\n * ```javascript\n * var md = require('markdown-it')();\n * var result = md.renderInline('__markdown-it__ rulezz!');\n * ```\n **/\n\n/**\n * new MarkdownIt([presetName, options])\n * - presetName (String): optional, `commonmark` / `zero`\n * - options (Object)\n *\n * Creates parser instanse with given config. Can be called without `new`.\n *\n * ##### presetName\n *\n * MarkdownIt provides named presets as a convenience to quickly\n * enable/disable active syntax rules and options for common use cases.\n *\n * - [\"commonmark\"](https://github.com/markdown-it/markdown-it/blob/master/lib/presets/commonmark.js) -\n * configures parser to strict [CommonMark](http://commonmark.org/) mode.\n * - [default](https://github.com/markdown-it/markdown-it/blob/master/lib/presets/default.js) -\n * similar to GFM, used when no preset name given. Enables all available rules,\n * but still without html, typographer & autolinker.\n * - [\"zero\"](https://github.com/markdown-it/markdown-it/blob/master/lib/presets/zero.js) -\n * all rules disabled. Useful to quickly setup your config via `.enable()`.\n * For example, when you need only `bold` and `italic` markup and nothing else.\n *\n * ##### options:\n *\n * - __html__ - `false`. Set `true` to enable HTML tags in source. Be careful!\n * That's not safe! You may need external sanitizer to protect output from XSS.\n * It's better to extend features via plugins, instead of enabling HTML.\n * - __xhtmlOut__ - `false`. Set `true` to add '/' when closing single tags\n * (`
`). This is needed only for full CommonMark compatibility. In real\n * world you will need HTML output.\n * - __breaks__ - `false`. Set `true` to convert `\\n` in paragraphs into `
`.\n * - __langPrefix__ - `language-`. CSS language class prefix for fenced blocks.\n * Can be useful for external highlighters.\n * - __linkify__ - `false`. Set `true` to autoconvert URL-like text to links.\n * - __typographer__ - `false`. Set `true` to enable [some language-neutral\n * replacement](https://github.com/markdown-it/markdown-it/blob/master/lib/rules_core/replacements.js) +\n * quotes beautification (smartquotes).\n * - __quotes__ - `“”‘’`, String or Array. Double + single quotes replacement\n * pairs, when typographer enabled and smartquotes on. For example, you can\n * use `'«»„“'` for Russian, `'„“‚‘'` for German, and\n * `['«\\xA0', '\\xA0»', '‹\\xA0', '\\xA0›']` for French (including nbsp).\n * - __highlight__ - `null`. Highlighter function for fenced code blocks.\n * Highlighter `function (str, lang)` should return escaped HTML. It can also\n * return empty string if the source was not changed and should be escaped\n * externaly. If result starts with `):\n *\n * ```javascript\n * var hljs = require('highlight.js') // https://highlightjs.org/\n *\n * // Actual default values\n * var md = require('markdown-it')({\n * highlight: function (str, lang) {\n * if (lang && hljs.getLanguage(lang)) {\n * try {\n * return '
' +\n *                hljs.highlight(lang, str, true).value +\n *                '
';\n * } catch (__) {}\n * }\n *\n * return '
' + md.utils.escapeHtml(str) + '
';\n * }\n * });\n * ```\n *\n **/\nfunction MarkdownIt(presetName, options) {\n if (!(this instanceof MarkdownIt)) {\n return new MarkdownIt(presetName, options);\n }\n\n if (!options) {\n if (!utils.isString(presetName)) {\n options = presetName || {};\n presetName = 'default';\n }\n }\n\n /**\n * MarkdownIt#inline -> ParserInline\n *\n * Instance of [[ParserInline]]. You may need it to add new rules when\n * writing plugins. For simple rules control use [[MarkdownIt.disable]] and\n * [[MarkdownIt.enable]].\n **/\n this.inline = new ParserInline();\n\n /**\n * MarkdownIt#block -> ParserBlock\n *\n * Instance of [[ParserBlock]]. You may need it to add new rules when\n * writing plugins. For simple rules control use [[MarkdownIt.disable]] and\n * [[MarkdownIt.enable]].\n **/\n this.block = new ParserBlock();\n\n /**\n * MarkdownIt#core -> Core\n *\n * Instance of [[Core]] chain executor. You may need it to add new rules when\n * writing plugins. For simple rules control use [[MarkdownIt.disable]] and\n * [[MarkdownIt.enable]].\n **/\n this.core = new ParserCore();\n\n /**\n * MarkdownIt#renderer -> Renderer\n *\n * Instance of [[Renderer]]. Use it to modify output look. Or to add rendering\n * rules for new token types, generated by plugins.\n *\n * ##### Example\n *\n * ```javascript\n * var md = require('markdown-it')();\n *\n * function myToken(tokens, idx, options, env, self) {\n * //...\n * return result;\n * };\n *\n * md.renderer.rules['my_token'] = myToken\n * ```\n *\n * See [[Renderer]] docs and [source code](https://github.com/markdown-it/markdown-it/blob/master/lib/renderer.js).\n **/\n this.renderer = new Renderer();\n\n /**\n * MarkdownIt#linkify -> LinkifyIt\n *\n * [linkify-it](https://github.com/markdown-it/linkify-it) instance.\n * Used by [linkify](https://github.com/markdown-it/markdown-it/blob/master/lib/rules_core/linkify.js)\n * rule.\n **/\n this.linkify = new LinkifyIt();\n\n /**\n * MarkdownIt#validateLink(url) -> Boolean\n *\n * Link validation function. CommonMark allows too much in links. By default\n * we disable `javascript:`, `vbscript:`, `file:` schemas, and almost all `data:...` schemas\n * except some embedded image types.\n *\n * You can change this behaviour:\n *\n * ```javascript\n * var md = require('markdown-it')();\n * // enable everything\n * md.validateLink = function () { return true; }\n * ```\n **/\n this.validateLink = validateLink;\n\n /**\n * MarkdownIt#normalizeLink(url) -> String\n *\n * Function used to encode link url to a machine-readable format,\n * which includes url-encoding, punycode, etc.\n **/\n this.normalizeLink = normalizeLink;\n\n /**\n * MarkdownIt#normalizeLinkText(url) -> String\n *\n * Function used to decode link url to a human-readable format`\n **/\n this.normalizeLinkText = normalizeLinkText;\n\n\n // Expose utils & helpers for easy acces from plugins\n\n /**\n * MarkdownIt#utils -> utils\n *\n * Assorted utility functions, useful to write plugins. See details\n * [here](https://github.com/markdown-it/markdown-it/blob/master/lib/common/utils.js).\n **/\n this.utils = utils;\n\n /**\n * MarkdownIt#helpers -> helpers\n *\n * Link components parser functions, useful to write plugins. See details\n * [here](https://github.com/markdown-it/markdown-it/blob/master/lib/helpers).\n **/\n this.helpers = utils.assign({}, helpers);\n\n\n this.options = {};\n this.configure(presetName);\n\n if (options) { this.set(options); }\n}\n\n\n/** chainable\n * MarkdownIt.set(options)\n *\n * Set parser options (in the same format as in constructor). Probably, you\n * will never need it, but you can change options after constructor call.\n *\n * ##### Example\n *\n * ```javascript\n * var md = require('markdown-it')()\n * .set({ html: true, breaks: true })\n * .set({ typographer, true });\n * ```\n *\n * __Note:__ To achieve the best possible performance, don't modify a\n * `markdown-it` instance options on the fly. If you need multiple configurations\n * it's best to create multiple instances and initialize each with separate\n * config.\n **/\nMarkdownIt.prototype.set = function (options) {\n utils.assign(this.options, options);\n return this;\n};\n\n\n/** chainable, internal\n * MarkdownIt.configure(presets)\n *\n * Batch load of all options and compenent settings. This is internal method,\n * and you probably will not need it. But if you will - see available presets\n * and data structure [here](https://github.com/markdown-it/markdown-it/tree/master/lib/presets)\n *\n * We strongly recommend to use presets instead of direct config loads. That\n * will give better compatibility with next versions.\n **/\nMarkdownIt.prototype.configure = function (presets) {\n var self = this, presetName;\n\n if (utils.isString(presets)) {\n presetName = presets;\n presets = config[presetName];\n if (!presets) { throw new Error('Wrong `markdown-it` preset \"' + presetName + '\", check name'); }\n }\n\n if (!presets) { throw new Error('Wrong `markdown-it` preset, can\\'t be empty'); }\n\n if (presets.options) { self.set(presets.options); }\n\n if (presets.components) {\n Object.keys(presets.components).forEach(function (name) {\n if (presets.components[name].rules) {\n self[name].ruler.enableOnly(presets.components[name].rules);\n }\n if (presets.components[name].rules2) {\n self[name].ruler2.enableOnly(presets.components[name].rules2);\n }\n });\n }\n return this;\n};\n\n\n/** chainable\n * MarkdownIt.enable(list, ignoreInvalid)\n * - list (String|Array): rule name or list of rule names to enable\n * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found.\n *\n * Enable list or rules. It will automatically find appropriate components,\n * containing rules with given names. If rule not found, and `ignoreInvalid`\n * not set - throws exception.\n *\n * ##### Example\n *\n * ```javascript\n * var md = require('markdown-it')()\n * .enable(['sub', 'sup'])\n * .disable('smartquotes');\n * ```\n **/\nMarkdownIt.prototype.enable = function (list, ignoreInvalid) {\n var result = [];\n\n if (!Array.isArray(list)) { list = [ list ]; }\n\n [ 'core', 'block', 'inline' ].forEach(function (chain) {\n result = result.concat(this[chain].ruler.enable(list, true));\n }, this);\n\n result = result.concat(this.inline.ruler2.enable(list, true));\n\n var missed = list.filter(function (name) { return result.indexOf(name) < 0; });\n\n if (missed.length && !ignoreInvalid) {\n throw new Error('MarkdownIt. Failed to enable unknown rule(s): ' + missed);\n }\n\n return this;\n};\n\n\n/** chainable\n * MarkdownIt.disable(list, ignoreInvalid)\n * - list (String|Array): rule name or list of rule names to disable.\n * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found.\n *\n * The same as [[MarkdownIt.enable]], but turn specified rules off.\n **/\nMarkdownIt.prototype.disable = function (list, ignoreInvalid) {\n var result = [];\n\n if (!Array.isArray(list)) { list = [ list ]; }\n\n [ 'core', 'block', 'inline' ].forEach(function (chain) {\n result = result.concat(this[chain].ruler.disable(list, true));\n }, this);\n\n result = result.concat(this.inline.ruler2.disable(list, true));\n\n var missed = list.filter(function (name) { return result.indexOf(name) < 0; });\n\n if (missed.length && !ignoreInvalid) {\n throw new Error('MarkdownIt. Failed to disable unknown rule(s): ' + missed);\n }\n return this;\n};\n\n\n/** chainable\n * MarkdownIt.use(plugin, params)\n *\n * Load specified plugin with given params into current parser instance.\n * It's just a sugar to call `plugin(md, params)` with curring.\n *\n * ##### Example\n *\n * ```javascript\n * var iterator = require('markdown-it-for-inline');\n * var md = require('markdown-it')()\n * .use(iterator, 'foo_replace', 'text', function (tokens, idx) {\n * tokens[idx].content = tokens[idx].content.replace(/foo/g, 'bar');\n * });\n * ```\n **/\nMarkdownIt.prototype.use = function (plugin /*, params, ... */) {\n var args = [ this ].concat(Array.prototype.slice.call(arguments, 1));\n plugin.apply(plugin, args);\n return this;\n};\n\n\n/** internal\n * MarkdownIt.parse(src, env) -> Array\n * - src (String): source string\n * - env (Object): environment sandbox\n *\n * Parse input string and return list of block tokens (special token type\n * \"inline\" will contain list of inline tokens). You should not call this\n * method directly, until you write custom renderer (for example, to produce\n * AST).\n *\n * `env` is used to pass data between \"distributed\" rules and return additional\n * metadata like reference info, needed for the renderer. It also can be used to\n * inject data in specific cases. Usually, you will be ok to pass `{}`,\n * and then pass updated object to renderer.\n **/\nMarkdownIt.prototype.parse = function (src, env) {\n if (typeof src !== 'string') {\n throw new Error('Input data should be a String');\n }\n\n var state = new this.core.State(src, this, env);\n\n this.core.process(state);\n\n return state.tokens;\n};\n\n\n/**\n * MarkdownIt.render(src [, env]) -> String\n * - src (String): source string\n * - env (Object): environment sandbox\n *\n * Render markdown string into html. It does all magic for you :).\n *\n * `env` can be used to inject additional metadata (`{}` by default).\n * But you will not need it with high probability. See also comment\n * in [[MarkdownIt.parse]].\n **/\nMarkdownIt.prototype.render = function (src, env) {\n env = env || {};\n\n return this.renderer.render(this.parse(src, env), this.options, env);\n};\n\n\n/** internal\n * MarkdownIt.parseInline(src, env) -> Array\n * - src (String): source string\n * - env (Object): environment sandbox\n *\n * The same as [[MarkdownIt.parse]] but skip all block rules. It returns the\n * block tokens list with the single `inline` element, containing parsed inline\n * tokens in `children` property. Also updates `env` object.\n **/\nMarkdownIt.prototype.parseInline = function (src, env) {\n var state = new this.core.State(src, this, env);\n\n state.inlineMode = true;\n this.core.process(state);\n\n return state.tokens;\n};\n\n\n/**\n * MarkdownIt.renderInline(src [, env]) -> String\n * - src (String): source string\n * - env (Object): environment sandbox\n *\n * Similar to [[MarkdownIt.render]] but for single paragraph content. Result\n * will NOT be wrapped into `

` tags.\n **/\nMarkdownIt.prototype.renderInline = function (src, env) {\n env = env || {};\n\n return this.renderer.render(this.parseInline(src, env), this.options, env);\n};\n\n\nmodule.exports = MarkdownIt;\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/index.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/parser_block.js": -/*!******************************************************!*\ - !*** ./node_modules/markdown-it/lib/parser_block.js ***! - \******************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("/** internal\n * class ParserBlock\n *\n * Block-level tokenizer.\n **/\n\n\n\nvar Ruler = __webpack_require__(/*! ./ruler */ \"./node_modules/markdown-it/lib/ruler.js\");\n\n\nvar _rules = [\n // First 2 params - rule name & source. Secondary array - list of rules,\n // which can be terminated by this one.\n [ 'table', __webpack_require__(/*! ./rules_block/table */ \"./node_modules/markdown-it/lib/rules_block/table.js\"), [ 'paragraph', 'reference' ] ],\n [ 'code', __webpack_require__(/*! ./rules_block/code */ \"./node_modules/markdown-it/lib/rules_block/code.js\") ],\n [ 'fence', __webpack_require__(/*! ./rules_block/fence */ \"./node_modules/markdown-it/lib/rules_block/fence.js\"), [ 'paragraph', 'reference', 'blockquote', 'list' ] ],\n [ 'blockquote', __webpack_require__(/*! ./rules_block/blockquote */ \"./node_modules/markdown-it/lib/rules_block/blockquote.js\"), [ 'paragraph', 'reference', 'blockquote', 'list' ] ],\n [ 'hr', __webpack_require__(/*! ./rules_block/hr */ \"./node_modules/markdown-it/lib/rules_block/hr.js\"), [ 'paragraph', 'reference', 'blockquote', 'list' ] ],\n [ 'list', __webpack_require__(/*! ./rules_block/list */ \"./node_modules/markdown-it/lib/rules_block/list.js\"), [ 'paragraph', 'reference', 'blockquote' ] ],\n [ 'reference', __webpack_require__(/*! ./rules_block/reference */ \"./node_modules/markdown-it/lib/rules_block/reference.js\") ],\n [ 'heading', __webpack_require__(/*! ./rules_block/heading */ \"./node_modules/markdown-it/lib/rules_block/heading.js\"), [ 'paragraph', 'reference', 'blockquote' ] ],\n [ 'lheading', __webpack_require__(/*! ./rules_block/lheading */ \"./node_modules/markdown-it/lib/rules_block/lheading.js\") ],\n [ 'html_block', __webpack_require__(/*! ./rules_block/html_block */ \"./node_modules/markdown-it/lib/rules_block/html_block.js\"), [ 'paragraph', 'reference', 'blockquote' ] ],\n [ 'paragraph', __webpack_require__(/*! ./rules_block/paragraph */ \"./node_modules/markdown-it/lib/rules_block/paragraph.js\") ]\n];\n\n\n/**\n * new ParserBlock()\n **/\nfunction ParserBlock() {\n /**\n * ParserBlock#ruler -> Ruler\n *\n * [[Ruler]] instance. Keep configuration of block rules.\n **/\n this.ruler = new Ruler();\n\n for (var i = 0; i < _rules.length; i++) {\n this.ruler.push(_rules[i][0], _rules[i][1], { alt: (_rules[i][2] || []).slice() });\n }\n}\n\n\n// Generate tokens for input range\n//\nParserBlock.prototype.tokenize = function (state, startLine, endLine) {\n var ok, i,\n rules = this.ruler.getRules(''),\n len = rules.length,\n line = startLine,\n hasEmptyLines = false,\n maxNesting = state.md.options.maxNesting;\n\n while (line < endLine) {\n state.line = line = state.skipEmptyLines(line);\n if (line >= endLine) { break; }\n\n // Termination condition for nested calls.\n // Nested calls currently used for blockquotes & lists\n if (state.sCount[line] < state.blkIndent) { break; }\n\n // If nesting level exceeded - skip tail to the end. That's not ordinary\n // situation and we should not care about content.\n if (state.level >= maxNesting) {\n state.line = endLine;\n break;\n }\n\n // Try all possible rules.\n // On success, rule should:\n //\n // - update `state.line`\n // - update `state.tokens`\n // - return true\n\n for (i = 0; i < len; i++) {\n ok = rules[i](state, line, endLine, false);\n if (ok) { break; }\n }\n\n // set state.tight if we had an empty line before current tag\n // i.e. latest empty line should not count\n state.tight = !hasEmptyLines;\n\n // paragraph might \"eat\" one newline after it in nested lists\n if (state.isEmpty(state.line - 1)) {\n hasEmptyLines = true;\n }\n\n line = state.line;\n\n if (line < endLine && state.isEmpty(line)) {\n hasEmptyLines = true;\n line++;\n state.line = line;\n }\n }\n};\n\n\n/**\n * ParserBlock.parse(str, md, env, outTokens)\n *\n * Process input string and push block tokens into `outTokens`\n **/\nParserBlock.prototype.parse = function (src, md, env, outTokens) {\n var state;\n\n if (!src) { return; }\n\n state = new this.State(src, md, env, outTokens);\n\n this.tokenize(state, state.line, state.lineMax);\n};\n\n\nParserBlock.prototype.State = __webpack_require__(/*! ./rules_block/state_block */ \"./node_modules/markdown-it/lib/rules_block/state_block.js\");\n\n\nmodule.exports = ParserBlock;\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/parser_block.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/parser_core.js": -/*!*****************************************************!*\ - !*** ./node_modules/markdown-it/lib/parser_core.js ***! - \*****************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("/** internal\n * class Core\n *\n * Top-level rules executor. Glues block/inline parsers and does intermediate\n * transformations.\n **/\n\n\n\nvar Ruler = __webpack_require__(/*! ./ruler */ \"./node_modules/markdown-it/lib/ruler.js\");\n\n\nvar _rules = [\n [ 'normalize', __webpack_require__(/*! ./rules_core/normalize */ \"./node_modules/markdown-it/lib/rules_core/normalize.js\") ],\n [ 'block', __webpack_require__(/*! ./rules_core/block */ \"./node_modules/markdown-it/lib/rules_core/block.js\") ],\n [ 'inline', __webpack_require__(/*! ./rules_core/inline */ \"./node_modules/markdown-it/lib/rules_core/inline.js\") ],\n [ 'linkify', __webpack_require__(/*! ./rules_core/linkify */ \"./node_modules/markdown-it/lib/rules_core/linkify.js\") ],\n [ 'replacements', __webpack_require__(/*! ./rules_core/replacements */ \"./node_modules/markdown-it/lib/rules_core/replacements.js\") ],\n [ 'smartquotes', __webpack_require__(/*! ./rules_core/smartquotes */ \"./node_modules/markdown-it/lib/rules_core/smartquotes.js\") ]\n];\n\n\n/**\n * new Core()\n **/\nfunction Core() {\n /**\n * Core#ruler -> Ruler\n *\n * [[Ruler]] instance. Keep configuration of core rules.\n **/\n this.ruler = new Ruler();\n\n for (var i = 0; i < _rules.length; i++) {\n this.ruler.push(_rules[i][0], _rules[i][1]);\n }\n}\n\n\n/**\n * Core.process(state)\n *\n * Executes core chain rules.\n **/\nCore.prototype.process = function (state) {\n var i, l, rules;\n\n rules = this.ruler.getRules('');\n\n for (i = 0, l = rules.length; i < l; i++) {\n rules[i](state);\n }\n};\n\nCore.prototype.State = __webpack_require__(/*! ./rules_core/state_core */ \"./node_modules/markdown-it/lib/rules_core/state_core.js\");\n\n\nmodule.exports = Core;\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/parser_core.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/parser_inline.js": -/*!*******************************************************!*\ - !*** ./node_modules/markdown-it/lib/parser_inline.js ***! - \*******************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("/** internal\n * class ParserInline\n *\n * Tokenizes paragraph content.\n **/\n\n\n\nvar Ruler = __webpack_require__(/*! ./ruler */ \"./node_modules/markdown-it/lib/ruler.js\");\n\n\n////////////////////////////////////////////////////////////////////////////////\n// Parser rules\n\nvar _rules = [\n [ 'text', __webpack_require__(/*! ./rules_inline/text */ \"./node_modules/markdown-it/lib/rules_inline/text.js\") ],\n [ 'newline', __webpack_require__(/*! ./rules_inline/newline */ \"./node_modules/markdown-it/lib/rules_inline/newline.js\") ],\n [ 'escape', __webpack_require__(/*! ./rules_inline/escape */ \"./node_modules/markdown-it/lib/rules_inline/escape.js\") ],\n [ 'backticks', __webpack_require__(/*! ./rules_inline/backticks */ \"./node_modules/markdown-it/lib/rules_inline/backticks.js\") ],\n [ 'strikethrough', __webpack_require__(/*! ./rules_inline/strikethrough */ \"./node_modules/markdown-it/lib/rules_inline/strikethrough.js\").tokenize ],\n [ 'emphasis', __webpack_require__(/*! ./rules_inline/emphasis */ \"./node_modules/markdown-it/lib/rules_inline/emphasis.js\").tokenize ],\n [ 'link', __webpack_require__(/*! ./rules_inline/link */ \"./node_modules/markdown-it/lib/rules_inline/link.js\") ],\n [ 'image', __webpack_require__(/*! ./rules_inline/image */ \"./node_modules/markdown-it/lib/rules_inline/image.js\") ],\n [ 'autolink', __webpack_require__(/*! ./rules_inline/autolink */ \"./node_modules/markdown-it/lib/rules_inline/autolink.js\") ],\n [ 'html_inline', __webpack_require__(/*! ./rules_inline/html_inline */ \"./node_modules/markdown-it/lib/rules_inline/html_inline.js\") ],\n [ 'entity', __webpack_require__(/*! ./rules_inline/entity */ \"./node_modules/markdown-it/lib/rules_inline/entity.js\") ]\n];\n\nvar _rules2 = [\n [ 'balance_pairs', __webpack_require__(/*! ./rules_inline/balance_pairs */ \"./node_modules/markdown-it/lib/rules_inline/balance_pairs.js\") ],\n [ 'strikethrough', __webpack_require__(/*! ./rules_inline/strikethrough */ \"./node_modules/markdown-it/lib/rules_inline/strikethrough.js\").postProcess ],\n [ 'emphasis', __webpack_require__(/*! ./rules_inline/emphasis */ \"./node_modules/markdown-it/lib/rules_inline/emphasis.js\").postProcess ],\n [ 'text_collapse', __webpack_require__(/*! ./rules_inline/text_collapse */ \"./node_modules/markdown-it/lib/rules_inline/text_collapse.js\") ]\n];\n\n\n/**\n * new ParserInline()\n **/\nfunction ParserInline() {\n var i;\n\n /**\n * ParserInline#ruler -> Ruler\n *\n * [[Ruler]] instance. Keep configuration of inline rules.\n **/\n this.ruler = new Ruler();\n\n for (i = 0; i < _rules.length; i++) {\n this.ruler.push(_rules[i][0], _rules[i][1]);\n }\n\n /**\n * ParserInline#ruler2 -> Ruler\n *\n * [[Ruler]] instance. Second ruler used for post-processing\n * (e.g. in emphasis-like rules).\n **/\n this.ruler2 = new Ruler();\n\n for (i = 0; i < _rules2.length; i++) {\n this.ruler2.push(_rules2[i][0], _rules2[i][1]);\n }\n}\n\n\n// Skip single token by running all rules in validation mode;\n// returns `true` if any rule reported success\n//\nParserInline.prototype.skipToken = function (state) {\n var ok, i, pos = state.pos,\n rules = this.ruler.getRules(''),\n len = rules.length,\n maxNesting = state.md.options.maxNesting,\n cache = state.cache;\n\n\n if (typeof cache[pos] !== 'undefined') {\n state.pos = cache[pos];\n return;\n }\n\n if (state.level < maxNesting) {\n for (i = 0; i < len; i++) {\n // Increment state.level and decrement it later to limit recursion.\n // It's harmless to do here, because no tokens are created. But ideally,\n // we'd need a separate private state variable for this purpose.\n //\n state.level++;\n ok = rules[i](state, true);\n state.level--;\n\n if (ok) { break; }\n }\n } else {\n // Too much nesting, just skip until the end of the paragraph.\n //\n // NOTE: this will cause links to behave incorrectly in the following case,\n // when an amount of `[` is exactly equal to `maxNesting + 1`:\n //\n // [[[[[[[[[[[[[[[[[[[[[foo]()\n //\n // TODO: remove this workaround when CM standard will allow nested links\n // (we can replace it by preventing links from being parsed in\n // validation mode)\n //\n state.pos = state.posMax;\n }\n\n if (!ok) { state.pos++; }\n cache[pos] = state.pos;\n};\n\n\n// Generate tokens for input range\n//\nParserInline.prototype.tokenize = function (state) {\n var ok, i,\n rules = this.ruler.getRules(''),\n len = rules.length,\n end = state.posMax,\n maxNesting = state.md.options.maxNesting;\n\n while (state.pos < end) {\n // Try all possible rules.\n // On success, rule should:\n //\n // - update `state.pos`\n // - update `state.tokens`\n // - return true\n\n if (state.level < maxNesting) {\n for (i = 0; i < len; i++) {\n ok = rules[i](state, false);\n if (ok) { break; }\n }\n }\n\n if (ok) {\n if (state.pos >= end) { break; }\n continue;\n }\n\n state.pending += state.src[state.pos++];\n }\n\n if (state.pending) {\n state.pushPending();\n }\n};\n\n\n/**\n * ParserInline.parse(str, md, env, outTokens)\n *\n * Process input string and push inline tokens into `outTokens`\n **/\nParserInline.prototype.parse = function (str, md, env, outTokens) {\n var i, rules, len;\n var state = new this.State(str, md, env, outTokens);\n\n this.tokenize(state);\n\n rules = this.ruler2.getRules('');\n len = rules.length;\n\n for (i = 0; i < len; i++) {\n rules[i](state);\n }\n};\n\n\nParserInline.prototype.State = __webpack_require__(/*! ./rules_inline/state_inline */ \"./node_modules/markdown-it/lib/rules_inline/state_inline.js\");\n\n\nmodule.exports = ParserInline;\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/parser_inline.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/presets/commonmark.js": -/*!************************************************************!*\ - !*** ./node_modules/markdown-it/lib/presets/commonmark.js ***! - \************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Commonmark default options\n\n\n\n\nmodule.exports = {\n options: {\n html: true, // Enable HTML tags in source\n xhtmlOut: true, // Use '/' to close single tags (
)\n breaks: false, // Convert '\\n' in paragraphs into
\n langPrefix: 'language-', // CSS language prefix for fenced blocks\n linkify: false, // autoconvert URL-like texts to links\n\n // Enable some language-neutral replacements + quotes beautification\n typographer: false,\n\n // Double + single quotes replacement pairs, when typographer enabled,\n // and smartquotes on. Could be either a String or an Array.\n //\n // For example, you can use '«»„“' for Russian, '„“‚‘' for German,\n // and ['«\\xA0', '\\xA0»', '‹\\xA0', '\\xA0›'] for French (including nbsp).\n quotes: '\\u201c\\u201d\\u2018\\u2019', /* “”‘’ */\n\n // Highlighter function. Should return escaped HTML,\n // or '' if the source string is not changed and should be escaped externaly.\n // If result starts with )\n breaks: false, // Convert '\\n' in paragraphs into
\n langPrefix: 'language-', // CSS language prefix for fenced blocks\n linkify: false, // autoconvert URL-like texts to links\n\n // Enable some language-neutral replacements + quotes beautification\n typographer: false,\n\n // Double + single quotes replacement pairs, when typographer enabled,\n // and smartquotes on. Could be either a String or an Array.\n //\n // For example, you can use '«»„“' for Russian, '„“‚‘' for German,\n // and ['«\\xA0', '\\xA0»', '‹\\xA0', '\\xA0›'] for French (including nbsp).\n quotes: '\\u201c\\u201d\\u2018\\u2019', /* “”‘’ */\n\n // Highlighter function. Should return escaped HTML,\n // or '' if the source string is not changed and should be escaped externaly.\n // If result starts with )\n breaks: false, // Convert '\\n' in paragraphs into
\n langPrefix: 'language-', // CSS language prefix for fenced blocks\n linkify: false, // autoconvert URL-like texts to links\n\n // Enable some language-neutral replacements + quotes beautification\n typographer: false,\n\n // Double + single quotes replacement pairs, when typographer enabled,\n // and smartquotes on. Could be either a String or an Array.\n //\n // For example, you can use '«»„“' for Russian, '„“‚‘' for German,\n // and ['«\\xA0', '\\xA0»', '‹\\xA0', '\\xA0›'] for French (including nbsp).\n quotes: '\\u201c\\u201d\\u2018\\u2019', /* “”‘’ */\n\n // Highlighter function. Should return escaped HTML,\n // or '' if the source string is not changed and should be escaped externaly.\n // If result starts with ' +\n escapeHtml(tokens[idx].content) +\n '';\n};\n\n\ndefault_rules.code_block = function (tokens, idx, options, env, slf) {\n var token = tokens[idx];\n\n return '' +\n escapeHtml(tokens[idx].content) +\n '\\n';\n};\n\n\ndefault_rules.fence = function (tokens, idx, options, env, slf) {\n var token = tokens[idx],\n info = token.info ? unescapeAll(token.info).trim() : '',\n langName = '',\n langAttrs = '',\n highlighted, i, arr, tmpAttrs, tmpToken;\n\n if (info) {\n arr = info.split(/(\\s+)/g);\n langName = arr[0];\n langAttrs = arr.slice(2).join('');\n }\n\n if (options.highlight) {\n highlighted = options.highlight(token.content, langName, langAttrs) || escapeHtml(token.content);\n } else {\n highlighted = escapeHtml(token.content);\n }\n\n if (highlighted.indexOf(''\n + highlighted\n + '\\n';\n }\n\n\n return '

'\n        + highlighted\n        + '
\\n';\n};\n\n\ndefault_rules.image = function (tokens, idx, options, env, slf) {\n var token = tokens[idx];\n\n // \"alt\" attr MUST be set, even if empty. Because it's mandatory and\n // should be placed on proper position for tests.\n //\n // Replace content with actual value\n\n token.attrs[token.attrIndex('alt')][1] =\n slf.renderInlineAsText(token.children, options, env);\n\n return slf.renderToken(tokens, idx, options);\n};\n\n\ndefault_rules.hardbreak = function (tokens, idx, options /*, env */) {\n return options.xhtmlOut ? '
\\n' : '
\\n';\n};\ndefault_rules.softbreak = function (tokens, idx, options /*, env */) {\n return options.breaks ? (options.xhtmlOut ? '
\\n' : '
\\n') : '\\n';\n};\n\n\ndefault_rules.text = function (tokens, idx /*, options, env */) {\n return escapeHtml(tokens[idx].content);\n};\n\n\ndefault_rules.html_block = function (tokens, idx /*, options, env */) {\n return tokens[idx].content;\n};\ndefault_rules.html_inline = function (tokens, idx /*, options, env */) {\n return tokens[idx].content;\n};\n\n\n/**\n * new Renderer()\n *\n * Creates new [[Renderer]] instance and fill [[Renderer#rules]] with defaults.\n **/\nfunction Renderer() {\n\n /**\n * Renderer#rules -> Object\n *\n * Contains render rules for tokens. Can be updated and extended.\n *\n * ##### Example\n *\n * ```javascript\n * var md = require('markdown-it')();\n *\n * md.renderer.rules.strong_open = function () { return ''; };\n * md.renderer.rules.strong_close = function () { return ''; };\n *\n * var result = md.renderInline(...);\n * ```\n *\n * Each rule is called as independent static function with fixed signature:\n *\n * ```javascript\n * function my_token_render(tokens, idx, options, env, renderer) {\n * // ...\n * return renderedHTML;\n * }\n * ```\n *\n * See [source code](https://github.com/markdown-it/markdown-it/blob/master/lib/renderer.js)\n * for more details and examples.\n **/\n this.rules = assign({}, default_rules);\n}\n\n\n/**\n * Renderer.renderAttrs(token) -> String\n *\n * Render token attributes to string.\n **/\nRenderer.prototype.renderAttrs = function renderAttrs(token) {\n var i, l, result;\n\n if (!token.attrs) { return ''; }\n\n result = '';\n\n for (i = 0, l = token.attrs.length; i < l; i++) {\n result += ' ' + escapeHtml(token.attrs[i][0]) + '=\"' + escapeHtml(token.attrs[i][1]) + '\"';\n }\n\n return result;\n};\n\n\n/**\n * Renderer.renderToken(tokens, idx, options) -> String\n * - tokens (Array): list of tokens\n * - idx (Numbed): token index to render\n * - options (Object): params of parser instance\n *\n * Default token renderer. Can be overriden by custom function\n * in [[Renderer#rules]].\n **/\nRenderer.prototype.renderToken = function renderToken(tokens, idx, options) {\n var nextToken,\n result = '',\n needLf = false,\n token = tokens[idx];\n\n // Tight list paragraphs\n if (token.hidden) {\n return '';\n }\n\n // Insert a newline between hidden paragraph and subsequent opening\n // block-level tag.\n //\n // For example, here we should insert a newline before blockquote:\n // - a\n // >\n //\n if (token.block && token.nesting !== -1 && idx && tokens[idx - 1].hidden) {\n result += '\\n';\n }\n\n // Add token name, e.g. ``.\n //\n needLf = false;\n }\n }\n }\n }\n\n result += needLf ? '>\\n' : '>';\n\n return result;\n};\n\n\n/**\n * Renderer.renderInline(tokens, options, env) -> String\n * - tokens (Array): list on block tokens to renter\n * - options (Object): params of parser instance\n * - env (Object): additional data from parsed input (references, for example)\n *\n * The same as [[Renderer.render]], but for single token of `inline` type.\n **/\nRenderer.prototype.renderInline = function (tokens, options, env) {\n var type,\n result = '',\n rules = this.rules;\n\n for (var i = 0, len = tokens.length; i < len; i++) {\n type = tokens[i].type;\n\n if (typeof rules[type] !== 'undefined') {\n result += rules[type](tokens, i, options, env, this);\n } else {\n result += this.renderToken(tokens, i, options);\n }\n }\n\n return result;\n};\n\n\n/** internal\n * Renderer.renderInlineAsText(tokens, options, env) -> String\n * - tokens (Array): list on block tokens to renter\n * - options (Object): params of parser instance\n * - env (Object): additional data from parsed input (references, for example)\n *\n * Special kludge for image `alt` attributes to conform CommonMark spec.\n * Don't try to use it! Spec requires to show `alt` content with stripped markup,\n * instead of simple escaping.\n **/\nRenderer.prototype.renderInlineAsText = function (tokens, options, env) {\n var result = '';\n\n for (var i = 0, len = tokens.length; i < len; i++) {\n if (tokens[i].type === 'text') {\n result += tokens[i].content;\n } else if (tokens[i].type === 'image') {\n result += this.renderInlineAsText(tokens[i].children, options, env);\n }\n }\n\n return result;\n};\n\n\n/**\n * Renderer.render(tokens, options, env) -> String\n * - tokens (Array): list on block tokens to renter\n * - options (Object): params of parser instance\n * - env (Object): additional data from parsed input (references, for example)\n *\n * Takes token stream and generates HTML. Probably, you will never need to call\n * this method directly.\n **/\nRenderer.prototype.render = function (tokens, options, env) {\n var i, len, type,\n result = '',\n rules = this.rules;\n\n for (i = 0, len = tokens.length; i < len; i++) {\n type = tokens[i].type;\n\n if (type === 'inline') {\n result += this.renderInline(tokens[i].children, options, env);\n } else if (typeof rules[type] !== 'undefined') {\n result += rules[tokens[i].type](tokens, i, options, env, this);\n } else {\n result += this.renderToken(tokens, i, options, env);\n }\n }\n\n return result;\n};\n\nmodule.exports = Renderer;\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/renderer.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/ruler.js": -/*!***********************************************!*\ - !*** ./node_modules/markdown-it/lib/ruler.js ***! - \***********************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("/**\n * class Ruler\n *\n * Helper class, used by [[MarkdownIt#core]], [[MarkdownIt#block]] and\n * [[MarkdownIt#inline]] to manage sequences of functions (rules):\n *\n * - keep rules in defined order\n * - assign the name to each rule\n * - enable/disable rules\n * - add/replace rules\n * - allow assign rules to additional named chains (in the same)\n * - cacheing lists of active rules\n *\n * You will not need use this class directly until write plugins. For simple\n * rules control use [[MarkdownIt.disable]], [[MarkdownIt.enable]] and\n * [[MarkdownIt.use]].\n **/\n\n\n\n/**\n * new Ruler()\n **/\nfunction Ruler() {\n // List of added rules. Each element is:\n //\n // {\n // name: XXX,\n // enabled: Boolean,\n // fn: Function(),\n // alt: [ name2, name3 ]\n // }\n //\n this.__rules__ = [];\n\n // Cached rule chains.\n //\n // First level - chain name, '' for default.\n // Second level - diginal anchor for fast filtering by charcodes.\n //\n this.__cache__ = null;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Helper methods, should not be used directly\n\n\n// Find rule index by name\n//\nRuler.prototype.__find__ = function (name) {\n for (var i = 0; i < this.__rules__.length; i++) {\n if (this.__rules__[i].name === name) {\n return i;\n }\n }\n return -1;\n};\n\n\n// Build rules lookup cache\n//\nRuler.prototype.__compile__ = function () {\n var self = this;\n var chains = [ '' ];\n\n // collect unique names\n self.__rules__.forEach(function (rule) {\n if (!rule.enabled) { return; }\n\n rule.alt.forEach(function (altName) {\n if (chains.indexOf(altName) < 0) {\n chains.push(altName);\n }\n });\n });\n\n self.__cache__ = {};\n\n chains.forEach(function (chain) {\n self.__cache__[chain] = [];\n self.__rules__.forEach(function (rule) {\n if (!rule.enabled) { return; }\n\n if (chain && rule.alt.indexOf(chain) < 0) { return; }\n\n self.__cache__[chain].push(rule.fn);\n });\n });\n};\n\n\n/**\n * Ruler.at(name, fn [, options])\n * - name (String): rule name to replace.\n * - fn (Function): new rule function.\n * - options (Object): new rule options (not mandatory).\n *\n * Replace rule by name with new function & options. Throws error if name not\n * found.\n *\n * ##### Options:\n *\n * - __alt__ - array with names of \"alternate\" chains.\n *\n * ##### Example\n *\n * Replace existing typographer replacement rule with new one:\n *\n * ```javascript\n * var md = require('markdown-it')();\n *\n * md.core.ruler.at('replacements', function replace(state) {\n * //...\n * });\n * ```\n **/\nRuler.prototype.at = function (name, fn, options) {\n var index = this.__find__(name);\n var opt = options || {};\n\n if (index === -1) { throw new Error('Parser rule not found: ' + name); }\n\n this.__rules__[index].fn = fn;\n this.__rules__[index].alt = opt.alt || [];\n this.__cache__ = null;\n};\n\n\n/**\n * Ruler.before(beforeName, ruleName, fn [, options])\n * - beforeName (String): new rule will be added before this one.\n * - ruleName (String): name of added rule.\n * - fn (Function): rule function.\n * - options (Object): rule options (not mandatory).\n *\n * Add new rule to chain before one with given name. See also\n * [[Ruler.after]], [[Ruler.push]].\n *\n * ##### Options:\n *\n * - __alt__ - array with names of \"alternate\" chains.\n *\n * ##### Example\n *\n * ```javascript\n * var md = require('markdown-it')();\n *\n * md.block.ruler.before('paragraph', 'my_rule', function replace(state) {\n * //...\n * });\n * ```\n **/\nRuler.prototype.before = function (beforeName, ruleName, fn, options) {\n var index = this.__find__(beforeName);\n var opt = options || {};\n\n if (index === -1) { throw new Error('Parser rule not found: ' + beforeName); }\n\n this.__rules__.splice(index, 0, {\n name: ruleName,\n enabled: true,\n fn: fn,\n alt: opt.alt || []\n });\n\n this.__cache__ = null;\n};\n\n\n/**\n * Ruler.after(afterName, ruleName, fn [, options])\n * - afterName (String): new rule will be added after this one.\n * - ruleName (String): name of added rule.\n * - fn (Function): rule function.\n * - options (Object): rule options (not mandatory).\n *\n * Add new rule to chain after one with given name. See also\n * [[Ruler.before]], [[Ruler.push]].\n *\n * ##### Options:\n *\n * - __alt__ - array with names of \"alternate\" chains.\n *\n * ##### Example\n *\n * ```javascript\n * var md = require('markdown-it')();\n *\n * md.inline.ruler.after('text', 'my_rule', function replace(state) {\n * //...\n * });\n * ```\n **/\nRuler.prototype.after = function (afterName, ruleName, fn, options) {\n var index = this.__find__(afterName);\n var opt = options || {};\n\n if (index === -1) { throw new Error('Parser rule not found: ' + afterName); }\n\n this.__rules__.splice(index + 1, 0, {\n name: ruleName,\n enabled: true,\n fn: fn,\n alt: opt.alt || []\n });\n\n this.__cache__ = null;\n};\n\n/**\n * Ruler.push(ruleName, fn [, options])\n * - ruleName (String): name of added rule.\n * - fn (Function): rule function.\n * - options (Object): rule options (not mandatory).\n *\n * Push new rule to the end of chain. See also\n * [[Ruler.before]], [[Ruler.after]].\n *\n * ##### Options:\n *\n * - __alt__ - array with names of \"alternate\" chains.\n *\n * ##### Example\n *\n * ```javascript\n * var md = require('markdown-it')();\n *\n * md.core.ruler.push('my_rule', function replace(state) {\n * //...\n * });\n * ```\n **/\nRuler.prototype.push = function (ruleName, fn, options) {\n var opt = options || {};\n\n this.__rules__.push({\n name: ruleName,\n enabled: true,\n fn: fn,\n alt: opt.alt || []\n });\n\n this.__cache__ = null;\n};\n\n\n/**\n * Ruler.enable(list [, ignoreInvalid]) -> Array\n * - list (String|Array): list of rule names to enable.\n * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found.\n *\n * Enable rules with given names. If any rule name not found - throw Error.\n * Errors can be disabled by second param.\n *\n * Returns list of found rule names (if no exception happened).\n *\n * See also [[Ruler.disable]], [[Ruler.enableOnly]].\n **/\nRuler.prototype.enable = function (list, ignoreInvalid) {\n if (!Array.isArray(list)) { list = [ list ]; }\n\n var result = [];\n\n // Search by name and enable\n list.forEach(function (name) {\n var idx = this.__find__(name);\n\n if (idx < 0) {\n if (ignoreInvalid) { return; }\n throw new Error('Rules manager: invalid rule name ' + name);\n }\n this.__rules__[idx].enabled = true;\n result.push(name);\n }, this);\n\n this.__cache__ = null;\n return result;\n};\n\n\n/**\n * Ruler.enableOnly(list [, ignoreInvalid])\n * - list (String|Array): list of rule names to enable (whitelist).\n * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found.\n *\n * Enable rules with given names, and disable everything else. If any rule name\n * not found - throw Error. Errors can be disabled by second param.\n *\n * See also [[Ruler.disable]], [[Ruler.enable]].\n **/\nRuler.prototype.enableOnly = function (list, ignoreInvalid) {\n if (!Array.isArray(list)) { list = [ list ]; }\n\n this.__rules__.forEach(function (rule) { rule.enabled = false; });\n\n this.enable(list, ignoreInvalid);\n};\n\n\n/**\n * Ruler.disable(list [, ignoreInvalid]) -> Array\n * - list (String|Array): list of rule names to disable.\n * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found.\n *\n * Disable rules with given names. If any rule name not found - throw Error.\n * Errors can be disabled by second param.\n *\n * Returns list of found rule names (if no exception happened).\n *\n * See also [[Ruler.enable]], [[Ruler.enableOnly]].\n **/\nRuler.prototype.disable = function (list, ignoreInvalid) {\n if (!Array.isArray(list)) { list = [ list ]; }\n\n var result = [];\n\n // Search by name and disable\n list.forEach(function (name) {\n var idx = this.__find__(name);\n\n if (idx < 0) {\n if (ignoreInvalid) { return; }\n throw new Error('Rules manager: invalid rule name ' + name);\n }\n this.__rules__[idx].enabled = false;\n result.push(name);\n }, this);\n\n this.__cache__ = null;\n return result;\n};\n\n\n/**\n * Ruler.getRules(chainName) -> Array\n *\n * Return array of active functions (rules) for given chain name. It analyzes\n * rules configuration, compiles caches if not exists and returns result.\n *\n * Default chain name is `''` (empty string). It can't be skipped. That's\n * done intentionally, to keep signature monomorphic for high speed.\n **/\nRuler.prototype.getRules = function (chainName) {\n if (this.__cache__ === null) {\n this.__compile__();\n }\n\n // Chain can be empty, if rules disabled. But we still have to return Array.\n return this.__cache__[chainName] || [];\n};\n\nmodule.exports = Ruler;\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/ruler.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_block/blockquote.js": -/*!****************************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_block/blockquote.js ***! - \****************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Block quotes\n\n\n\nvar isSpace = __webpack_require__(/*! ../common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\").isSpace;\n\n\nmodule.exports = function blockquote(state, startLine, endLine, silent) {\n var adjustTab,\n ch,\n i,\n initial,\n l,\n lastLineEmpty,\n lines,\n nextLine,\n offset,\n oldBMarks,\n oldBSCount,\n oldIndent,\n oldParentType,\n oldSCount,\n oldTShift,\n spaceAfterMarker,\n terminate,\n terminatorRules,\n token,\n isOutdented,\n oldLineMax = state.lineMax,\n pos = state.bMarks[startLine] + state.tShift[startLine],\n max = state.eMarks[startLine];\n\n // if it's indented more than 3 spaces, it should be a code block\n if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }\n\n // check the block quote marker\n if (state.src.charCodeAt(pos++) !== 0x3E/* > */) { return false; }\n\n // we know that it's going to be a valid blockquote,\n // so no point trying to find the end of it in silent mode\n if (silent) { return true; }\n\n // set offset past spaces and \">\"\n initial = offset = state.sCount[startLine] + 1;\n\n // skip one optional space after '>'\n if (state.src.charCodeAt(pos) === 0x20 /* space */) {\n // ' > test '\n // ^ -- position start of line here:\n pos++;\n initial++;\n offset++;\n adjustTab = false;\n spaceAfterMarker = true;\n } else if (state.src.charCodeAt(pos) === 0x09 /* tab */) {\n spaceAfterMarker = true;\n\n if ((state.bsCount[startLine] + offset) % 4 === 3) {\n // ' >\\t test '\n // ^ -- position start of line here (tab has width===1)\n pos++;\n initial++;\n offset++;\n adjustTab = false;\n } else {\n // ' >\\t test '\n // ^ -- position start of line here + shift bsCount slightly\n // to make extra space appear\n adjustTab = true;\n }\n } else {\n spaceAfterMarker = false;\n }\n\n oldBMarks = [ state.bMarks[startLine] ];\n state.bMarks[startLine] = pos;\n\n while (pos < max) {\n ch = state.src.charCodeAt(pos);\n\n if (isSpace(ch)) {\n if (ch === 0x09) {\n offset += 4 - (offset + state.bsCount[startLine] + (adjustTab ? 1 : 0)) % 4;\n } else {\n offset++;\n }\n } else {\n break;\n }\n\n pos++;\n }\n\n oldBSCount = [ state.bsCount[startLine] ];\n state.bsCount[startLine] = state.sCount[startLine] + 1 + (spaceAfterMarker ? 1 : 0);\n\n lastLineEmpty = pos >= max;\n\n oldSCount = [ state.sCount[startLine] ];\n state.sCount[startLine] = offset - initial;\n\n oldTShift = [ state.tShift[startLine] ];\n state.tShift[startLine] = pos - state.bMarks[startLine];\n\n terminatorRules = state.md.block.ruler.getRules('blockquote');\n\n oldParentType = state.parentType;\n state.parentType = 'blockquote';\n\n // Search the end of the block\n //\n // Block ends with either:\n // 1. an empty line outside:\n // ```\n // > test\n //\n // ```\n // 2. an empty line inside:\n // ```\n // >\n // test\n // ```\n // 3. another tag:\n // ```\n // > test\n // - - -\n // ```\n for (nextLine = startLine + 1; nextLine < endLine; nextLine++) {\n // check if it's outdented, i.e. it's inside list item and indented\n // less than said list item:\n //\n // ```\n // 1. anything\n // > current blockquote\n // 2. checking this line\n // ```\n isOutdented = state.sCount[nextLine] < state.blkIndent;\n\n pos = state.bMarks[nextLine] + state.tShift[nextLine];\n max = state.eMarks[nextLine];\n\n if (pos >= max) {\n // Case 1: line is not inside the blockquote, and this line is empty.\n break;\n }\n\n if (state.src.charCodeAt(pos++) === 0x3E/* > */ && !isOutdented) {\n // This line is inside the blockquote.\n\n // set offset past spaces and \">\"\n initial = offset = state.sCount[nextLine] + 1;\n\n // skip one optional space after '>'\n if (state.src.charCodeAt(pos) === 0x20 /* space */) {\n // ' > test '\n // ^ -- position start of line here:\n pos++;\n initial++;\n offset++;\n adjustTab = false;\n spaceAfterMarker = true;\n } else if (state.src.charCodeAt(pos) === 0x09 /* tab */) {\n spaceAfterMarker = true;\n\n if ((state.bsCount[nextLine] + offset) % 4 === 3) {\n // ' >\\t test '\n // ^ -- position start of line here (tab has width===1)\n pos++;\n initial++;\n offset++;\n adjustTab = false;\n } else {\n // ' >\\t test '\n // ^ -- position start of line here + shift bsCount slightly\n // to make extra space appear\n adjustTab = true;\n }\n } else {\n spaceAfterMarker = false;\n }\n\n oldBMarks.push(state.bMarks[nextLine]);\n state.bMarks[nextLine] = pos;\n\n while (pos < max) {\n ch = state.src.charCodeAt(pos);\n\n if (isSpace(ch)) {\n if (ch === 0x09) {\n offset += 4 - (offset + state.bsCount[nextLine] + (adjustTab ? 1 : 0)) % 4;\n } else {\n offset++;\n }\n } else {\n break;\n }\n\n pos++;\n }\n\n lastLineEmpty = pos >= max;\n\n oldBSCount.push(state.bsCount[nextLine]);\n state.bsCount[nextLine] = state.sCount[nextLine] + 1 + (spaceAfterMarker ? 1 : 0);\n\n oldSCount.push(state.sCount[nextLine]);\n state.sCount[nextLine] = offset - initial;\n\n oldTShift.push(state.tShift[nextLine]);\n state.tShift[nextLine] = pos - state.bMarks[nextLine];\n continue;\n }\n\n // Case 2: line is not inside the blockquote, and the last line was empty.\n if (lastLineEmpty) { break; }\n\n // Case 3: another tag found.\n terminate = false;\n for (i = 0, l = terminatorRules.length; i < l; i++) {\n if (terminatorRules[i](state, nextLine, endLine, true)) {\n terminate = true;\n break;\n }\n }\n\n if (terminate) {\n // Quirk to enforce \"hard termination mode\" for paragraphs;\n // normally if you call `tokenize(state, startLine, nextLine)`,\n // paragraphs will look below nextLine for paragraph continuation,\n // but if blockquote is terminated by another tag, they shouldn't\n state.lineMax = nextLine;\n\n if (state.blkIndent !== 0) {\n // state.blkIndent was non-zero, we now set it to zero,\n // so we need to re-calculate all offsets to appear as\n // if indent wasn't changed\n oldBMarks.push(state.bMarks[nextLine]);\n oldBSCount.push(state.bsCount[nextLine]);\n oldTShift.push(state.tShift[nextLine]);\n oldSCount.push(state.sCount[nextLine]);\n state.sCount[nextLine] -= state.blkIndent;\n }\n\n break;\n }\n\n oldBMarks.push(state.bMarks[nextLine]);\n oldBSCount.push(state.bsCount[nextLine]);\n oldTShift.push(state.tShift[nextLine]);\n oldSCount.push(state.sCount[nextLine]);\n\n // A negative indentation means that this is a paragraph continuation\n //\n state.sCount[nextLine] = -1;\n }\n\n oldIndent = state.blkIndent;\n state.blkIndent = 0;\n\n token = state.push('blockquote_open', 'blockquote', 1);\n token.markup = '>';\n token.map = lines = [ startLine, 0 ];\n\n state.md.block.tokenize(state, startLine, nextLine);\n\n token = state.push('blockquote_close', 'blockquote', -1);\n token.markup = '>';\n\n state.lineMax = oldLineMax;\n state.parentType = oldParentType;\n lines[1] = state.line;\n\n // Restore original tShift; this might not be necessary since the parser\n // has already been here, but just to make sure we can do that.\n for (i = 0; i < oldTShift.length; i++) {\n state.bMarks[i + startLine] = oldBMarks[i];\n state.tShift[i + startLine] = oldTShift[i];\n state.sCount[i + startLine] = oldSCount[i];\n state.bsCount[i + startLine] = oldBSCount[i];\n }\n state.blkIndent = oldIndent;\n\n return true;\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_block/blockquote.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_block/code.js": -/*!**********************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_block/code.js ***! - \**********************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Code block (4 spaces padded)\n\n\n\n\nmodule.exports = function code(state, startLine, endLine/*, silent*/) {\n var nextLine, last, token;\n\n if (state.sCount[startLine] - state.blkIndent < 4) { return false; }\n\n last = nextLine = startLine + 1;\n\n while (nextLine < endLine) {\n if (state.isEmpty(nextLine)) {\n nextLine++;\n continue;\n }\n\n if (state.sCount[nextLine] - state.blkIndent >= 4) {\n nextLine++;\n last = nextLine;\n continue;\n }\n break;\n }\n\n state.line = last;\n\n token = state.push('code_block', 'code', 0);\n token.content = state.getLines(startLine, last, 4 + state.blkIndent, true);\n token.map = [ startLine, state.line ];\n\n return true;\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_block/code.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_block/fence.js": -/*!***********************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_block/fence.js ***! - \***********************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// fences (``` lang, ~~~ lang)\n\n\n\n\nmodule.exports = function fence(state, startLine, endLine, silent) {\n var marker, len, params, nextLine, mem, token, markup,\n haveEndMarker = false,\n pos = state.bMarks[startLine] + state.tShift[startLine],\n max = state.eMarks[startLine];\n\n // if it's indented more than 3 spaces, it should be a code block\n if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }\n\n if (pos + 3 > max) { return false; }\n\n marker = state.src.charCodeAt(pos);\n\n if (marker !== 0x7E/* ~ */ && marker !== 0x60 /* ` */) {\n return false;\n }\n\n // scan marker length\n mem = pos;\n pos = state.skipChars(pos, marker);\n\n len = pos - mem;\n\n if (len < 3) { return false; }\n\n markup = state.src.slice(mem, pos);\n params = state.src.slice(pos, max);\n\n if (marker === 0x60 /* ` */) {\n if (params.indexOf(String.fromCharCode(marker)) >= 0) {\n return false;\n }\n }\n\n // Since start is found, we can report success here in validation mode\n if (silent) { return true; }\n\n // search end of block\n nextLine = startLine;\n\n for (;;) {\n nextLine++;\n if (nextLine >= endLine) {\n // unclosed block should be autoclosed by end of document.\n // also block seems to be autoclosed by end of parent\n break;\n }\n\n pos = mem = state.bMarks[nextLine] + state.tShift[nextLine];\n max = state.eMarks[nextLine];\n\n if (pos < max && state.sCount[nextLine] < state.blkIndent) {\n // non-empty line with negative indent should stop the list:\n // - ```\n // test\n break;\n }\n\n if (state.src.charCodeAt(pos) !== marker) { continue; }\n\n if (state.sCount[nextLine] - state.blkIndent >= 4) {\n // closing fence should be indented less than 4 spaces\n continue;\n }\n\n pos = state.skipChars(pos, marker);\n\n // closing code fence must be at least as long as the opening one\n if (pos - mem < len) { continue; }\n\n // make sure tail has spaces only\n pos = state.skipSpaces(pos);\n\n if (pos < max) { continue; }\n\n haveEndMarker = true;\n // found!\n break;\n }\n\n // If a fence has heading spaces, they should be removed from its inner block\n len = state.sCount[startLine];\n\n state.line = nextLine + (haveEndMarker ? 1 : 0);\n\n token = state.push('fence', 'code', 0);\n token.info = params;\n token.content = state.getLines(startLine + 1, nextLine, len, true);\n token.markup = markup;\n token.map = [ startLine, state.line ];\n\n return true;\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_block/fence.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_block/heading.js": -/*!*************************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_block/heading.js ***! - \*************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// heading (#, ##, ...)\n\n\n\nvar isSpace = __webpack_require__(/*! ../common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\").isSpace;\n\n\nmodule.exports = function heading(state, startLine, endLine, silent) {\n var ch, level, tmp, token,\n pos = state.bMarks[startLine] + state.tShift[startLine],\n max = state.eMarks[startLine];\n\n // if it's indented more than 3 spaces, it should be a code block\n if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }\n\n ch = state.src.charCodeAt(pos);\n\n if (ch !== 0x23/* # */ || pos >= max) { return false; }\n\n // count heading level\n level = 1;\n ch = state.src.charCodeAt(++pos);\n while (ch === 0x23/* # */ && pos < max && level <= 6) {\n level++;\n ch = state.src.charCodeAt(++pos);\n }\n\n if (level > 6 || (pos < max && !isSpace(ch))) { return false; }\n\n if (silent) { return true; }\n\n // Let's cut tails like ' ### ' from the end of string\n\n max = state.skipSpacesBack(max, pos);\n tmp = state.skipCharsBack(max, 0x23, pos); // #\n if (tmp > pos && isSpace(state.src.charCodeAt(tmp - 1))) {\n max = tmp;\n }\n\n state.line = startLine + 1;\n\n token = state.push('heading_open', 'h' + String(level), 1);\n token.markup = '########'.slice(0, level);\n token.map = [ startLine, state.line ];\n\n token = state.push('inline', '', 0);\n token.content = state.src.slice(pos, max).trim();\n token.map = [ startLine, state.line ];\n token.children = [];\n\n token = state.push('heading_close', 'h' + String(level), -1);\n token.markup = '########'.slice(0, level);\n\n return true;\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_block/heading.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_block/hr.js": -/*!********************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_block/hr.js ***! - \********************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Horizontal rule\n\n\n\nvar isSpace = __webpack_require__(/*! ../common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\").isSpace;\n\n\nmodule.exports = function hr(state, startLine, endLine, silent) {\n var marker, cnt, ch, token,\n pos = state.bMarks[startLine] + state.tShift[startLine],\n max = state.eMarks[startLine];\n\n // if it's indented more than 3 spaces, it should be a code block\n if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }\n\n marker = state.src.charCodeAt(pos++);\n\n // Check hr marker\n if (marker !== 0x2A/* * */ &&\n marker !== 0x2D/* - */ &&\n marker !== 0x5F/* _ */) {\n return false;\n }\n\n // markers can be mixed with spaces, but there should be at least 3 of them\n\n cnt = 1;\n while (pos < max) {\n ch = state.src.charCodeAt(pos++);\n if (ch !== marker && !isSpace(ch)) { return false; }\n if (ch === marker) { cnt++; }\n }\n\n if (cnt < 3) { return false; }\n\n if (silent) { return true; }\n\n state.line = startLine + 1;\n\n token = state.push('hr', 'hr', 0);\n token.map = [ startLine, state.line ];\n token.markup = Array(cnt + 1).join(String.fromCharCode(marker));\n\n return true;\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_block/hr.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_block/html_block.js": -/*!****************************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_block/html_block.js ***! - \****************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// HTML block\n\n\n\n\nvar block_names = __webpack_require__(/*! ../common/html_blocks */ \"./node_modules/markdown-it/lib/common/html_blocks.js\");\nvar HTML_OPEN_CLOSE_TAG_RE = __webpack_require__(/*! ../common/html_re */ \"./node_modules/markdown-it/lib/common/html_re.js\").HTML_OPEN_CLOSE_TAG_RE;\n\n// An array of opening and corresponding closing sequences for html tags,\n// last argument defines whether it can terminate a paragraph or not\n//\nvar HTML_SEQUENCES = [\n [ /^<(script|pre|style)(?=(\\s|>|$))/i, /<\\/(script|pre|style)>/i, true ],\n [ /^/, true ],\n [ /^<\\?/, /\\?>/, true ],\n [ /^/, true ],\n [ /^/, true ],\n [ new RegExp('^|$))', 'i'), /^$/, true ],\n [ new RegExp(HTML_OPEN_CLOSE_TAG_RE.source + '\\\\s*$'), /^$/, false ]\n];\n\n\nmodule.exports = function html_block(state, startLine, endLine, silent) {\n var i, nextLine, token, lineText,\n pos = state.bMarks[startLine] + state.tShift[startLine],\n max = state.eMarks[startLine];\n\n // if it's indented more than 3 spaces, it should be a code block\n if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }\n\n if (!state.md.options.html) { return false; }\n\n if (state.src.charCodeAt(pos) !== 0x3C/* < */) { return false; }\n\n lineText = state.src.slice(pos, max);\n\n for (i = 0; i < HTML_SEQUENCES.length; i++) {\n if (HTML_SEQUENCES[i][0].test(lineText)) { break; }\n }\n\n if (i === HTML_SEQUENCES.length) { return false; }\n\n if (silent) {\n // true if this sequence can be a terminator, false otherwise\n return HTML_SEQUENCES[i][2];\n }\n\n nextLine = startLine + 1;\n\n // If we are here - we detected HTML block.\n // Let's roll down till block end.\n if (!HTML_SEQUENCES[i][1].test(lineText)) {\n for (; nextLine < endLine; nextLine++) {\n if (state.sCount[nextLine] < state.blkIndent) { break; }\n\n pos = state.bMarks[nextLine] + state.tShift[nextLine];\n max = state.eMarks[nextLine];\n lineText = state.src.slice(pos, max);\n\n if (HTML_SEQUENCES[i][1].test(lineText)) {\n if (lineText.length !== 0) { nextLine++; }\n break;\n }\n }\n }\n\n state.line = nextLine;\n\n token = state.push('html_block', '', 0);\n token.map = [ startLine, nextLine ];\n token.content = state.getLines(startLine, nextLine, state.blkIndent, true);\n\n return true;\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_block/html_block.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_block/lheading.js": -/*!**************************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_block/lheading.js ***! - \**************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// lheading (---, ===)\n\n\n\n\nmodule.exports = function lheading(state, startLine, endLine/*, silent*/) {\n var content, terminate, i, l, token, pos, max, level, marker,\n nextLine = startLine + 1, oldParentType,\n terminatorRules = state.md.block.ruler.getRules('paragraph');\n\n // if it's indented more than 3 spaces, it should be a code block\n if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }\n\n oldParentType = state.parentType;\n state.parentType = 'paragraph'; // use paragraph to match terminatorRules\n\n // jump line-by-line until empty one or EOF\n for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) {\n // this would be a code block normally, but after paragraph\n // it's considered a lazy continuation regardless of what's there\n if (state.sCount[nextLine] - state.blkIndent > 3) { continue; }\n\n //\n // Check for underline in setext header\n //\n if (state.sCount[nextLine] >= state.blkIndent) {\n pos = state.bMarks[nextLine] + state.tShift[nextLine];\n max = state.eMarks[nextLine];\n\n if (pos < max) {\n marker = state.src.charCodeAt(pos);\n\n if (marker === 0x2D/* - */ || marker === 0x3D/* = */) {\n pos = state.skipChars(pos, marker);\n pos = state.skipSpaces(pos);\n\n if (pos >= max) {\n level = (marker === 0x3D/* = */ ? 1 : 2);\n break;\n }\n }\n }\n }\n\n // quirk for blockquotes, this line should already be checked by that rule\n if (state.sCount[nextLine] < 0) { continue; }\n\n // Some tags can terminate paragraph without empty line.\n terminate = false;\n for (i = 0, l = terminatorRules.length; i < l; i++) {\n if (terminatorRules[i](state, nextLine, endLine, true)) {\n terminate = true;\n break;\n }\n }\n if (terminate) { break; }\n }\n\n if (!level) {\n // Didn't find valid underline\n return false;\n }\n\n content = state.getLines(startLine, nextLine, state.blkIndent, false).trim();\n\n state.line = nextLine + 1;\n\n token = state.push('heading_open', 'h' + String(level), 1);\n token.markup = String.fromCharCode(marker);\n token.map = [ startLine, state.line ];\n\n token = state.push('inline', '', 0);\n token.content = content;\n token.map = [ startLine, state.line - 1 ];\n token.children = [];\n\n token = state.push('heading_close', 'h' + String(level), -1);\n token.markup = String.fromCharCode(marker);\n\n state.parentType = oldParentType;\n\n return true;\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_block/lheading.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_block/list.js": -/*!**********************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_block/list.js ***! - \**********************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Lists\n\n\n\nvar isSpace = __webpack_require__(/*! ../common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\").isSpace;\n\n\n// Search `[-+*][\\n ]`, returns next pos after marker on success\n// or -1 on fail.\nfunction skipBulletListMarker(state, startLine) {\n var marker, pos, max, ch;\n\n pos = state.bMarks[startLine] + state.tShift[startLine];\n max = state.eMarks[startLine];\n\n marker = state.src.charCodeAt(pos++);\n // Check bullet\n if (marker !== 0x2A/* * */ &&\n marker !== 0x2D/* - */ &&\n marker !== 0x2B/* + */) {\n return -1;\n }\n\n if (pos < max) {\n ch = state.src.charCodeAt(pos);\n\n if (!isSpace(ch)) {\n // \" -test \" - is not a list item\n return -1;\n }\n }\n\n return pos;\n}\n\n// Search `\\d+[.)][\\n ]`, returns next pos after marker on success\n// or -1 on fail.\nfunction skipOrderedListMarker(state, startLine) {\n var ch,\n start = state.bMarks[startLine] + state.tShift[startLine],\n pos = start,\n max = state.eMarks[startLine];\n\n // List marker should have at least 2 chars (digit + dot)\n if (pos + 1 >= max) { return -1; }\n\n ch = state.src.charCodeAt(pos++);\n\n if (ch < 0x30/* 0 */ || ch > 0x39/* 9 */) { return -1; }\n\n for (;;) {\n // EOL -> fail\n if (pos >= max) { return -1; }\n\n ch = state.src.charCodeAt(pos++);\n\n if (ch >= 0x30/* 0 */ && ch <= 0x39/* 9 */) {\n\n // List marker should have no more than 9 digits\n // (prevents integer overflow in browsers)\n if (pos - start >= 10) { return -1; }\n\n continue;\n }\n\n // found valid marker\n if (ch === 0x29/* ) */ || ch === 0x2e/* . */) {\n break;\n }\n\n return -1;\n }\n\n\n if (pos < max) {\n ch = state.src.charCodeAt(pos);\n\n if (!isSpace(ch)) {\n // \" 1.test \" - is not a list item\n return -1;\n }\n }\n return pos;\n}\n\nfunction markTightParagraphs(state, idx) {\n var i, l,\n level = state.level + 2;\n\n for (i = idx + 2, l = state.tokens.length - 2; i < l; i++) {\n if (state.tokens[i].level === level && state.tokens[i].type === 'paragraph_open') {\n state.tokens[i + 2].hidden = true;\n state.tokens[i].hidden = true;\n i += 2;\n }\n }\n}\n\n\nmodule.exports = function list(state, startLine, endLine, silent) {\n var ch,\n contentStart,\n i,\n indent,\n indentAfterMarker,\n initial,\n isOrdered,\n itemLines,\n l,\n listLines,\n listTokIdx,\n markerCharCode,\n markerValue,\n max,\n nextLine,\n offset,\n oldListIndent,\n oldParentType,\n oldSCount,\n oldTShift,\n oldTight,\n pos,\n posAfterMarker,\n prevEmptyEnd,\n start,\n terminate,\n terminatorRules,\n token,\n isTerminatingParagraph = false,\n tight = true;\n\n // if it's indented more than 3 spaces, it should be a code block\n if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }\n\n // Special case:\n // - item 1\n // - item 2\n // - item 3\n // - item 4\n // - this one is a paragraph continuation\n if (state.listIndent >= 0 &&\n state.sCount[startLine] - state.listIndent >= 4 &&\n state.sCount[startLine] < state.blkIndent) {\n return false;\n }\n\n // limit conditions when list can interrupt\n // a paragraph (validation mode only)\n if (silent && state.parentType === 'paragraph') {\n // Next list item should still terminate previous list item;\n //\n // This code can fail if plugins use blkIndent as well as lists,\n // but I hope the spec gets fixed long before that happens.\n //\n if (state.tShift[startLine] >= state.blkIndent) {\n isTerminatingParagraph = true;\n }\n }\n\n // Detect list type and position after marker\n if ((posAfterMarker = skipOrderedListMarker(state, startLine)) >= 0) {\n isOrdered = true;\n start = state.bMarks[startLine] + state.tShift[startLine];\n markerValue = Number(state.src.substr(start, posAfterMarker - start - 1));\n\n // If we're starting a new ordered list right after\n // a paragraph, it should start with 1.\n if (isTerminatingParagraph && markerValue !== 1) return false;\n\n } else if ((posAfterMarker = skipBulletListMarker(state, startLine)) >= 0) {\n isOrdered = false;\n\n } else {\n return false;\n }\n\n // If we're starting a new unordered list right after\n // a paragraph, first line should not be empty.\n if (isTerminatingParagraph) {\n if (state.skipSpaces(posAfterMarker) >= state.eMarks[startLine]) return false;\n }\n\n // We should terminate list on style change. Remember first one to compare.\n markerCharCode = state.src.charCodeAt(posAfterMarker - 1);\n\n // For validation mode we can terminate immediately\n if (silent) { return true; }\n\n // Start list\n listTokIdx = state.tokens.length;\n\n if (isOrdered) {\n token = state.push('ordered_list_open', 'ol', 1);\n if (markerValue !== 1) {\n token.attrs = [ [ 'start', markerValue ] ];\n }\n\n } else {\n token = state.push('bullet_list_open', 'ul', 1);\n }\n\n token.map = listLines = [ startLine, 0 ];\n token.markup = String.fromCharCode(markerCharCode);\n\n //\n // Iterate list items\n //\n\n nextLine = startLine;\n prevEmptyEnd = false;\n terminatorRules = state.md.block.ruler.getRules('list');\n\n oldParentType = state.parentType;\n state.parentType = 'list';\n\n while (nextLine < endLine) {\n pos = posAfterMarker;\n max = state.eMarks[nextLine];\n\n initial = offset = state.sCount[nextLine] + posAfterMarker - (state.bMarks[startLine] + state.tShift[startLine]);\n\n while (pos < max) {\n ch = state.src.charCodeAt(pos);\n\n if (ch === 0x09) {\n offset += 4 - (offset + state.bsCount[nextLine]) % 4;\n } else if (ch === 0x20) {\n offset++;\n } else {\n break;\n }\n\n pos++;\n }\n\n contentStart = pos;\n\n if (contentStart >= max) {\n // trimming space in \"- \\n 3\" case, indent is 1 here\n indentAfterMarker = 1;\n } else {\n indentAfterMarker = offset - initial;\n }\n\n // If we have more than 4 spaces, the indent is 1\n // (the rest is just indented code block)\n if (indentAfterMarker > 4) { indentAfterMarker = 1; }\n\n // \" - test\"\n // ^^^^^ - calculating total length of this thing\n indent = initial + indentAfterMarker;\n\n // Run subparser & write tokens\n token = state.push('list_item_open', 'li', 1);\n token.markup = String.fromCharCode(markerCharCode);\n token.map = itemLines = [ startLine, 0 ];\n\n // change current state, then restore it after parser subcall\n oldTight = state.tight;\n oldTShift = state.tShift[startLine];\n oldSCount = state.sCount[startLine];\n\n // - example list\n // ^ listIndent position will be here\n // ^ blkIndent position will be here\n //\n oldListIndent = state.listIndent;\n state.listIndent = state.blkIndent;\n state.blkIndent = indent;\n\n state.tight = true;\n state.tShift[startLine] = contentStart - state.bMarks[startLine];\n state.sCount[startLine] = offset;\n\n if (contentStart >= max && state.isEmpty(startLine + 1)) {\n // workaround for this case\n // (list item is empty, list terminates before \"foo\"):\n // ~~~~~~~~\n // -\n //\n // foo\n // ~~~~~~~~\n state.line = Math.min(state.line + 2, endLine);\n } else {\n state.md.block.tokenize(state, startLine, endLine, true);\n }\n\n // If any of list item is tight, mark list as tight\n if (!state.tight || prevEmptyEnd) {\n tight = false;\n }\n // Item become loose if finish with empty line,\n // but we should filter last element, because it means list finish\n prevEmptyEnd = (state.line - startLine) > 1 && state.isEmpty(state.line - 1);\n\n state.blkIndent = state.listIndent;\n state.listIndent = oldListIndent;\n state.tShift[startLine] = oldTShift;\n state.sCount[startLine] = oldSCount;\n state.tight = oldTight;\n\n token = state.push('list_item_close', 'li', -1);\n token.markup = String.fromCharCode(markerCharCode);\n\n nextLine = startLine = state.line;\n itemLines[1] = nextLine;\n contentStart = state.bMarks[startLine];\n\n if (nextLine >= endLine) { break; }\n\n //\n // Try to check if list is terminated or continued.\n //\n if (state.sCount[nextLine] < state.blkIndent) { break; }\n\n // if it's indented more than 3 spaces, it should be a code block\n if (state.sCount[startLine] - state.blkIndent >= 4) { break; }\n\n // fail if terminating block found\n terminate = false;\n for (i = 0, l = terminatorRules.length; i < l; i++) {\n if (terminatorRules[i](state, nextLine, endLine, true)) {\n terminate = true;\n break;\n }\n }\n if (terminate) { break; }\n\n // fail if list has another type\n if (isOrdered) {\n posAfterMarker = skipOrderedListMarker(state, nextLine);\n if (posAfterMarker < 0) { break; }\n } else {\n posAfterMarker = skipBulletListMarker(state, nextLine);\n if (posAfterMarker < 0) { break; }\n }\n\n if (markerCharCode !== state.src.charCodeAt(posAfterMarker - 1)) { break; }\n }\n\n // Finalize list\n if (isOrdered) {\n token = state.push('ordered_list_close', 'ol', -1);\n } else {\n token = state.push('bullet_list_close', 'ul', -1);\n }\n token.markup = String.fromCharCode(markerCharCode);\n\n listLines[1] = nextLine;\n state.line = nextLine;\n\n state.parentType = oldParentType;\n\n // mark paragraphs tight if needed\n if (tight) {\n markTightParagraphs(state, listTokIdx);\n }\n\n return true;\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_block/list.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_block/paragraph.js": -/*!***************************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_block/paragraph.js ***! - \***************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Paragraph\n\n\n\n\nmodule.exports = function paragraph(state, startLine/*, endLine*/) {\n var content, terminate, i, l, token, oldParentType,\n nextLine = startLine + 1,\n terminatorRules = state.md.block.ruler.getRules('paragraph'),\n endLine = state.lineMax;\n\n oldParentType = state.parentType;\n state.parentType = 'paragraph';\n\n // jump line-by-line until empty one or EOF\n for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) {\n // this would be a code block normally, but after paragraph\n // it's considered a lazy continuation regardless of what's there\n if (state.sCount[nextLine] - state.blkIndent > 3) { continue; }\n\n // quirk for blockquotes, this line should already be checked by that rule\n if (state.sCount[nextLine] < 0) { continue; }\n\n // Some tags can terminate paragraph without empty line.\n terminate = false;\n for (i = 0, l = terminatorRules.length; i < l; i++) {\n if (terminatorRules[i](state, nextLine, endLine, true)) {\n terminate = true;\n break;\n }\n }\n if (terminate) { break; }\n }\n\n content = state.getLines(startLine, nextLine, state.blkIndent, false).trim();\n\n state.line = nextLine;\n\n token = state.push('paragraph_open', 'p', 1);\n token.map = [ startLine, state.line ];\n\n token = state.push('inline', '', 0);\n token.content = content;\n token.map = [ startLine, state.line ];\n token.children = [];\n\n token = state.push('paragraph_close', 'p', -1);\n\n state.parentType = oldParentType;\n\n return true;\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_block/paragraph.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_block/reference.js": -/*!***************************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_block/reference.js ***! - \***************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\n\nvar normalizeReference = __webpack_require__(/*! ../common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\").normalizeReference;\nvar isSpace = __webpack_require__(/*! ../common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\").isSpace;\n\n\nmodule.exports = function reference(state, startLine, _endLine, silent) {\n var ch,\n destEndPos,\n destEndLineNo,\n endLine,\n href,\n i,\n l,\n label,\n labelEnd,\n oldParentType,\n res,\n start,\n str,\n terminate,\n terminatorRules,\n title,\n lines = 0,\n pos = state.bMarks[startLine] + state.tShift[startLine],\n max = state.eMarks[startLine],\n nextLine = startLine + 1;\n\n // if it's indented more than 3 spaces, it should be a code block\n if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }\n\n if (state.src.charCodeAt(pos) !== 0x5B/* [ */) { return false; }\n\n // Simple check to quickly interrupt scan on [link](url) at the start of line.\n // Can be useful on practice: https://github.com/markdown-it/markdown-it/issues/54\n while (++pos < max) {\n if (state.src.charCodeAt(pos) === 0x5D /* ] */ &&\n state.src.charCodeAt(pos - 1) !== 0x5C/* \\ */) {\n if (pos + 1 === max) { return false; }\n if (state.src.charCodeAt(pos + 1) !== 0x3A/* : */) { return false; }\n break;\n }\n }\n\n endLine = state.lineMax;\n\n // jump line-by-line until empty one or EOF\n terminatorRules = state.md.block.ruler.getRules('reference');\n\n oldParentType = state.parentType;\n state.parentType = 'reference';\n\n for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) {\n // this would be a code block normally, but after paragraph\n // it's considered a lazy continuation regardless of what's there\n if (state.sCount[nextLine] - state.blkIndent > 3) { continue; }\n\n // quirk for blockquotes, this line should already be checked by that rule\n if (state.sCount[nextLine] < 0) { continue; }\n\n // Some tags can terminate paragraph without empty line.\n terminate = false;\n for (i = 0, l = terminatorRules.length; i < l; i++) {\n if (terminatorRules[i](state, nextLine, endLine, true)) {\n terminate = true;\n break;\n }\n }\n if (terminate) { break; }\n }\n\n str = state.getLines(startLine, nextLine, state.blkIndent, false).trim();\n max = str.length;\n\n for (pos = 1; pos < max; pos++) {\n ch = str.charCodeAt(pos);\n if (ch === 0x5B /* [ */) {\n return false;\n } else if (ch === 0x5D /* ] */) {\n labelEnd = pos;\n break;\n } else if (ch === 0x0A /* \\n */) {\n lines++;\n } else if (ch === 0x5C /* \\ */) {\n pos++;\n if (pos < max && str.charCodeAt(pos) === 0x0A) {\n lines++;\n }\n }\n }\n\n if (labelEnd < 0 || str.charCodeAt(labelEnd + 1) !== 0x3A/* : */) { return false; }\n\n // [label]: destination 'title'\n // ^^^ skip optional whitespace here\n for (pos = labelEnd + 2; pos < max; pos++) {\n ch = str.charCodeAt(pos);\n if (ch === 0x0A) {\n lines++;\n } else if (isSpace(ch)) {\n /*eslint no-empty:0*/\n } else {\n break;\n }\n }\n\n // [label]: destination 'title'\n // ^^^^^^^^^^^ parse this\n res = state.md.helpers.parseLinkDestination(str, pos, max);\n if (!res.ok) { return false; }\n\n href = state.md.normalizeLink(res.str);\n if (!state.md.validateLink(href)) { return false; }\n\n pos = res.pos;\n lines += res.lines;\n\n // save cursor state, we could require to rollback later\n destEndPos = pos;\n destEndLineNo = lines;\n\n // [label]: destination 'title'\n // ^^^ skipping those spaces\n start = pos;\n for (; pos < max; pos++) {\n ch = str.charCodeAt(pos);\n if (ch === 0x0A) {\n lines++;\n } else if (isSpace(ch)) {\n /*eslint no-empty:0*/\n } else {\n break;\n }\n }\n\n // [label]: destination 'title'\n // ^^^^^^^ parse this\n res = state.md.helpers.parseLinkTitle(str, pos, max);\n if (pos < max && start !== pos && res.ok) {\n title = res.str;\n pos = res.pos;\n lines += res.lines;\n } else {\n title = '';\n pos = destEndPos;\n lines = destEndLineNo;\n }\n\n // skip trailing spaces until the rest of the line\n while (pos < max) {\n ch = str.charCodeAt(pos);\n if (!isSpace(ch)) { break; }\n pos++;\n }\n\n if (pos < max && str.charCodeAt(pos) !== 0x0A) {\n if (title) {\n // garbage at the end of the line after title,\n // but it could still be a valid reference if we roll back\n title = '';\n pos = destEndPos;\n lines = destEndLineNo;\n while (pos < max) {\n ch = str.charCodeAt(pos);\n if (!isSpace(ch)) { break; }\n pos++;\n }\n }\n }\n\n if (pos < max && str.charCodeAt(pos) !== 0x0A) {\n // garbage at the end of the line\n return false;\n }\n\n label = normalizeReference(str.slice(1, labelEnd));\n if (!label) {\n // CommonMark 0.20 disallows empty labels\n return false;\n }\n\n // Reference can not terminate anything. This check is for safety only.\n /*istanbul ignore if*/\n if (silent) { return true; }\n\n if (typeof state.env.references === 'undefined') {\n state.env.references = {};\n }\n if (typeof state.env.references[label] === 'undefined') {\n state.env.references[label] = { title: title, href: href };\n }\n\n state.parentType = oldParentType;\n\n state.line = startLine + lines + 1;\n return true;\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_block/reference.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_block/state_block.js": -/*!*****************************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_block/state_block.js ***! - \*****************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Parser state class\n\n\n\nvar Token = __webpack_require__(/*! ../token */ \"./node_modules/markdown-it/lib/token.js\");\nvar isSpace = __webpack_require__(/*! ../common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\").isSpace;\n\n\nfunction StateBlock(src, md, env, tokens) {\n var ch, s, start, pos, len, indent, offset, indent_found;\n\n this.src = src;\n\n // link to parser instance\n this.md = md;\n\n this.env = env;\n\n //\n // Internal state vartiables\n //\n\n this.tokens = tokens;\n\n this.bMarks = []; // line begin offsets for fast jumps\n this.eMarks = []; // line end offsets for fast jumps\n this.tShift = []; // offsets of the first non-space characters (tabs not expanded)\n this.sCount = []; // indents for each line (tabs expanded)\n\n // An amount of virtual spaces (tabs expanded) between beginning\n // of each line (bMarks) and real beginning of that line.\n //\n // It exists only as a hack because blockquotes override bMarks\n // losing information in the process.\n //\n // It's used only when expanding tabs, you can think about it as\n // an initial tab length, e.g. bsCount=21 applied to string `\\t123`\n // means first tab should be expanded to 4-21%4 === 3 spaces.\n //\n this.bsCount = [];\n\n // block parser variables\n this.blkIndent = 0; // required block content indent (for example, if we are\n // inside a list, it would be positioned after list marker)\n this.line = 0; // line index in src\n this.lineMax = 0; // lines count\n this.tight = false; // loose/tight mode for lists\n this.ddIndent = -1; // indent of the current dd block (-1 if there isn't any)\n this.listIndent = -1; // indent of the current list block (-1 if there isn't any)\n\n // can be 'blockquote', 'list', 'root', 'paragraph' or 'reference'\n // used in lists to determine if they interrupt a paragraph\n this.parentType = 'root';\n\n this.level = 0;\n\n // renderer\n this.result = '';\n\n // Create caches\n // Generate markers.\n s = this.src;\n indent_found = false;\n\n for (start = pos = indent = offset = 0, len = s.length; pos < len; pos++) {\n ch = s.charCodeAt(pos);\n\n if (!indent_found) {\n if (isSpace(ch)) {\n indent++;\n\n if (ch === 0x09) {\n offset += 4 - offset % 4;\n } else {\n offset++;\n }\n continue;\n } else {\n indent_found = true;\n }\n }\n\n if (ch === 0x0A || pos === len - 1) {\n if (ch !== 0x0A) { pos++; }\n this.bMarks.push(start);\n this.eMarks.push(pos);\n this.tShift.push(indent);\n this.sCount.push(offset);\n this.bsCount.push(0);\n\n indent_found = false;\n indent = 0;\n offset = 0;\n start = pos + 1;\n }\n }\n\n // Push fake entry to simplify cache bounds checks\n this.bMarks.push(s.length);\n this.eMarks.push(s.length);\n this.tShift.push(0);\n this.sCount.push(0);\n this.bsCount.push(0);\n\n this.lineMax = this.bMarks.length - 1; // don't count last fake line\n}\n\n// Push new token to \"stream\".\n//\nStateBlock.prototype.push = function (type, tag, nesting) {\n var token = new Token(type, tag, nesting);\n token.block = true;\n\n if (nesting < 0) this.level--; // closing tag\n token.level = this.level;\n if (nesting > 0) this.level++; // opening tag\n\n this.tokens.push(token);\n return token;\n};\n\nStateBlock.prototype.isEmpty = function isEmpty(line) {\n return this.bMarks[line] + this.tShift[line] >= this.eMarks[line];\n};\n\nStateBlock.prototype.skipEmptyLines = function skipEmptyLines(from) {\n for (var max = this.lineMax; from < max; from++) {\n if (this.bMarks[from] + this.tShift[from] < this.eMarks[from]) {\n break;\n }\n }\n return from;\n};\n\n// Skip spaces from given position.\nStateBlock.prototype.skipSpaces = function skipSpaces(pos) {\n var ch;\n\n for (var max = this.src.length; pos < max; pos++) {\n ch = this.src.charCodeAt(pos);\n if (!isSpace(ch)) { break; }\n }\n return pos;\n};\n\n// Skip spaces from given position in reverse.\nStateBlock.prototype.skipSpacesBack = function skipSpacesBack(pos, min) {\n if (pos <= min) { return pos; }\n\n while (pos > min) {\n if (!isSpace(this.src.charCodeAt(--pos))) { return pos + 1; }\n }\n return pos;\n};\n\n// Skip char codes from given position\nStateBlock.prototype.skipChars = function skipChars(pos, code) {\n for (var max = this.src.length; pos < max; pos++) {\n if (this.src.charCodeAt(pos) !== code) { break; }\n }\n return pos;\n};\n\n// Skip char codes reverse from given position - 1\nStateBlock.prototype.skipCharsBack = function skipCharsBack(pos, code, min) {\n if (pos <= min) { return pos; }\n\n while (pos > min) {\n if (code !== this.src.charCodeAt(--pos)) { return pos + 1; }\n }\n return pos;\n};\n\n// cut lines range from source.\nStateBlock.prototype.getLines = function getLines(begin, end, indent, keepLastLF) {\n var i, lineIndent, ch, first, last, queue, lineStart,\n line = begin;\n\n if (begin >= end) {\n return '';\n }\n\n queue = new Array(end - begin);\n\n for (i = 0; line < end; line++, i++) {\n lineIndent = 0;\n lineStart = first = this.bMarks[line];\n\n if (line + 1 < end || keepLastLF) {\n // No need for bounds check because we have fake entry on tail.\n last = this.eMarks[line] + 1;\n } else {\n last = this.eMarks[line];\n }\n\n while (first < last && lineIndent < indent) {\n ch = this.src.charCodeAt(first);\n\n if (isSpace(ch)) {\n if (ch === 0x09) {\n lineIndent += 4 - (lineIndent + this.bsCount[line]) % 4;\n } else {\n lineIndent++;\n }\n } else if (first - lineStart < this.tShift[line]) {\n // patched tShift masked characters to look like spaces (blockquotes, list markers)\n lineIndent++;\n } else {\n break;\n }\n\n first++;\n }\n\n if (lineIndent > indent) {\n // partially expanding tabs in code blocks, e.g '\\t\\tfoobar'\n // with indent=2 becomes ' \\tfoobar'\n queue[i] = new Array(lineIndent - indent + 1).join(' ') + this.src.slice(first, last);\n } else {\n queue[i] = this.src.slice(first, last);\n }\n }\n\n return queue.join('');\n};\n\n// re-export Token class to use in block rules\nStateBlock.prototype.Token = Token;\n\n\nmodule.exports = StateBlock;\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_block/state_block.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_block/table.js": -/*!***********************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_block/table.js ***! - \***********************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// GFM table, https://github.github.com/gfm/#tables-extension-\n\n\n\nvar isSpace = __webpack_require__(/*! ../common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\").isSpace;\n\n\nfunction getLine(state, line) {\n var pos = state.bMarks[line] + state.tShift[line],\n max = state.eMarks[line];\n\n return state.src.substr(pos, max - pos);\n}\n\nfunction escapedSplit(str) {\n var result = [],\n pos = 0,\n max = str.length,\n ch,\n isEscaped = false,\n lastPos = 0,\n current = '';\n\n ch = str.charCodeAt(pos);\n\n while (pos < max) {\n if (ch === 0x7c/* | */) {\n if (!isEscaped) {\n // pipe separating cells, '|'\n result.push(current + str.substring(lastPos, pos));\n current = '';\n lastPos = pos + 1;\n } else {\n // escaped pipe, '\\|'\n current += str.substring(lastPos, pos - 1);\n lastPos = pos;\n }\n }\n\n isEscaped = (ch === 0x5c/* \\ */);\n pos++;\n\n ch = str.charCodeAt(pos);\n }\n\n result.push(current + str.substring(lastPos));\n\n return result;\n}\n\n\nmodule.exports = function table(state, startLine, endLine, silent) {\n var ch, lineText, pos, i, l, nextLine, columns, columnCount, token,\n aligns, t, tableLines, tbodyLines, oldParentType, terminate,\n terminatorRules;\n\n // should have at least two lines\n if (startLine + 2 > endLine) { return false; }\n\n nextLine = startLine + 1;\n\n if (state.sCount[nextLine] < state.blkIndent) { return false; }\n\n // if it's indented more than 3 spaces, it should be a code block\n if (state.sCount[nextLine] - state.blkIndent >= 4) { return false; }\n\n // first character of the second line should be '|', '-', ':',\n // and no other characters are allowed but spaces;\n // basically, this is the equivalent of /^[-:|][-:|\\s]*$/ regexp\n\n pos = state.bMarks[nextLine] + state.tShift[nextLine];\n if (pos >= state.eMarks[nextLine]) { return false; }\n\n ch = state.src.charCodeAt(pos++);\n if (ch !== 0x7C/* | */ && ch !== 0x2D/* - */ && ch !== 0x3A/* : */) { return false; }\n\n while (pos < state.eMarks[nextLine]) {\n ch = state.src.charCodeAt(pos);\n\n if (ch !== 0x7C/* | */ && ch !== 0x2D/* - */ && ch !== 0x3A/* : */ && !isSpace(ch)) { return false; }\n\n pos++;\n }\n\n lineText = getLine(state, startLine + 1);\n\n columns = lineText.split('|');\n aligns = [];\n for (i = 0; i < columns.length; i++) {\n t = columns[i].trim();\n if (!t) {\n // allow empty columns before and after table, but not in between columns;\n // e.g. allow ` |---| `, disallow ` ---||--- `\n if (i === 0 || i === columns.length - 1) {\n continue;\n } else {\n return false;\n }\n }\n\n if (!/^:?-+:?$/.test(t)) { return false; }\n if (t.charCodeAt(t.length - 1) === 0x3A/* : */) {\n aligns.push(t.charCodeAt(0) === 0x3A/* : */ ? 'center' : 'right');\n } else if (t.charCodeAt(0) === 0x3A/* : */) {\n aligns.push('left');\n } else {\n aligns.push('');\n }\n }\n\n lineText = getLine(state, startLine).trim();\n if (lineText.indexOf('|') === -1) { return false; }\n if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }\n columns = escapedSplit(lineText);\n if (columns.length && columns[0] === '') columns.shift();\n if (columns.length && columns[columns.length - 1] === '') columns.pop();\n\n // header row will define an amount of columns in the entire table,\n // and align row should be exactly the same (the rest of the rows can differ)\n columnCount = columns.length;\n if (columnCount === 0 || columnCount !== aligns.length) { return false; }\n\n if (silent) { return true; }\n\n oldParentType = state.parentType;\n state.parentType = 'table';\n\n // use 'blockquote' lists for termination because it's\n // the most similar to tables\n terminatorRules = state.md.block.ruler.getRules('blockquote');\n\n token = state.push('table_open', 'table', 1);\n token.map = tableLines = [ startLine, 0 ];\n\n token = state.push('thead_open', 'thead', 1);\n token.map = [ startLine, startLine + 1 ];\n\n token = state.push('tr_open', 'tr', 1);\n token.map = [ startLine, startLine + 1 ];\n\n for (i = 0; i < columns.length; i++) {\n token = state.push('th_open', 'th', 1);\n if (aligns[i]) {\n token.attrs = [ [ 'style', 'text-align:' + aligns[i] ] ];\n }\n\n token = state.push('inline', '', 0);\n token.content = columns[i].trim();\n token.children = [];\n\n token = state.push('th_close', 'th', -1);\n }\n\n token = state.push('tr_close', 'tr', -1);\n token = state.push('thead_close', 'thead', -1);\n\n for (nextLine = startLine + 2; nextLine < endLine; nextLine++) {\n if (state.sCount[nextLine] < state.blkIndent) { break; }\n\n terminate = false;\n for (i = 0, l = terminatorRules.length; i < l; i++) {\n if (terminatorRules[i](state, nextLine, endLine, true)) {\n terminate = true;\n break;\n }\n }\n\n if (terminate) { break; }\n lineText = getLine(state, nextLine).trim();\n if (!lineText) { break; }\n if (state.sCount[nextLine] - state.blkIndent >= 4) { break; }\n columns = escapedSplit(lineText);\n if (columns.length && columns[0] === '') columns.shift();\n if (columns.length && columns[columns.length - 1] === '') columns.pop();\n\n if (nextLine === startLine + 2) {\n token = state.push('tbody_open', 'tbody', 1);\n token.map = tbodyLines = [ startLine + 2, 0 ];\n }\n\n token = state.push('tr_open', 'tr', 1);\n token.map = [ nextLine, nextLine + 1 ];\n\n for (i = 0; i < columnCount; i++) {\n token = state.push('td_open', 'td', 1);\n if (aligns[i]) {\n token.attrs = [ [ 'style', 'text-align:' + aligns[i] ] ];\n }\n\n token = state.push('inline', '', 0);\n token.content = columns[i] ? columns[i].trim() : '';\n token.children = [];\n\n token = state.push('td_close', 'td', -1);\n }\n token = state.push('tr_close', 'tr', -1);\n }\n\n if (tbodyLines) {\n token = state.push('tbody_close', 'tbody', -1);\n tbodyLines[1] = nextLine;\n }\n\n token = state.push('table_close', 'table', -1);\n tableLines[1] = nextLine;\n\n state.parentType = oldParentType;\n state.line = nextLine;\n return true;\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_block/table.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_core/block.js": -/*!**********************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_core/block.js ***! - \**********************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\n\nmodule.exports = function block(state) {\n var token;\n\n if (state.inlineMode) {\n token = new state.Token('inline', '', 0);\n token.content = state.src;\n token.map = [ 0, 1 ];\n token.children = [];\n state.tokens.push(token);\n } else {\n state.md.block.parse(state.src, state.md, state.env, state.tokens);\n }\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_core/block.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_core/inline.js": -/*!***********************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_core/inline.js ***! - \***********************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nmodule.exports = function inline(state) {\n var tokens = state.tokens, tok, i, l;\n\n // Parse inlines\n for (i = 0, l = tokens.length; i < l; i++) {\n tok = tokens[i];\n if (tok.type === 'inline') {\n state.md.inline.parse(tok.content, state.md, state.env, tok.children);\n }\n }\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_core/inline.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_core/linkify.js": -/*!************************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_core/linkify.js ***! - \************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Replace link-like texts with link nodes.\n//\n// Currently restricted by `md.validateLink()` to http/https/ftp\n//\n\n\n\nvar arrayReplaceAt = __webpack_require__(/*! ../common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\").arrayReplaceAt;\n\n\nfunction isLinkOpen(str) {\n return /^\\s]/i.test(str);\n}\nfunction isLinkClose(str) {\n return /^<\\/a\\s*>/i.test(str);\n}\n\n\nmodule.exports = function linkify(state) {\n var i, j, l, tokens, token, currentToken, nodes, ln, text, pos, lastPos,\n level, htmlLinkLevel, url, fullUrl, urlText,\n blockTokens = state.tokens,\n links;\n\n if (!state.md.options.linkify) { return; }\n\n for (j = 0, l = blockTokens.length; j < l; j++) {\n if (blockTokens[j].type !== 'inline' ||\n !state.md.linkify.pretest(blockTokens[j].content)) {\n continue;\n }\n\n tokens = blockTokens[j].children;\n\n htmlLinkLevel = 0;\n\n // We scan from the end, to keep position when new tags added.\n // Use reversed logic in links start/end match\n for (i = tokens.length - 1; i >= 0; i--) {\n currentToken = tokens[i];\n\n // Skip content of markdown links\n if (currentToken.type === 'link_close') {\n i--;\n while (tokens[i].level !== currentToken.level && tokens[i].type !== 'link_open') {\n i--;\n }\n continue;\n }\n\n // Skip content of html tag links\n if (currentToken.type === 'html_inline') {\n if (isLinkOpen(currentToken.content) && htmlLinkLevel > 0) {\n htmlLinkLevel--;\n }\n if (isLinkClose(currentToken.content)) {\n htmlLinkLevel++;\n }\n }\n if (htmlLinkLevel > 0) { continue; }\n\n if (currentToken.type === 'text' && state.md.linkify.test(currentToken.content)) {\n\n text = currentToken.content;\n links = state.md.linkify.match(text);\n\n // Now split string to nodes\n nodes = [];\n level = currentToken.level;\n lastPos = 0;\n\n for (ln = 0; ln < links.length; ln++) {\n\n url = links[ln].url;\n fullUrl = state.md.normalizeLink(url);\n if (!state.md.validateLink(fullUrl)) { continue; }\n\n urlText = links[ln].text;\n\n // Linkifier might send raw hostnames like \"example.com\", where url\n // starts with domain name. So we prepend http:// in those cases,\n // and remove it afterwards.\n //\n if (!links[ln].schema) {\n urlText = state.md.normalizeLinkText('http://' + urlText).replace(/^http:\\/\\//, '');\n } else if (links[ln].schema === 'mailto:' && !/^mailto:/i.test(urlText)) {\n urlText = state.md.normalizeLinkText('mailto:' + urlText).replace(/^mailto:/, '');\n } else {\n urlText = state.md.normalizeLinkText(urlText);\n }\n\n pos = links[ln].index;\n\n if (pos > lastPos) {\n token = new state.Token('text', '', 0);\n token.content = text.slice(lastPos, pos);\n token.level = level;\n nodes.push(token);\n }\n\n token = new state.Token('link_open', 'a', 1);\n token.attrs = [ [ 'href', fullUrl ] ];\n token.level = level++;\n token.markup = 'linkify';\n token.info = 'auto';\n nodes.push(token);\n\n token = new state.Token('text', '', 0);\n token.content = urlText;\n token.level = level;\n nodes.push(token);\n\n token = new state.Token('link_close', 'a', -1);\n token.level = --level;\n token.markup = 'linkify';\n token.info = 'auto';\n nodes.push(token);\n\n lastPos = links[ln].lastIndex;\n }\n if (lastPos < text.length) {\n token = new state.Token('text', '', 0);\n token.content = text.slice(lastPos);\n token.level = level;\n nodes.push(token);\n }\n\n // replace current node\n blockTokens[j].children = tokens = arrayReplaceAt(tokens, i, nodes);\n }\n }\n }\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_core/linkify.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_core/normalize.js": -/*!**************************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_core/normalize.js ***! - \**************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Normalize input string\n\n\n\n\n// https://spec.commonmark.org/0.29/#line-ending\nvar NEWLINES_RE = /\\r\\n?|\\n/g;\nvar NULL_RE = /\\0/g;\n\n\nmodule.exports = function normalize(state) {\n var str;\n\n // Normalize newlines\n str = state.src.replace(NEWLINES_RE, '\\n');\n\n // Replace NULL characters\n str = str.replace(NULL_RE, '\\uFFFD');\n\n state.src = str;\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_core/normalize.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_core/replacements.js": -/*!*****************************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_core/replacements.js ***! - \*****************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Simple typographic replacements\n//\n// (c) (C) → ©\n// (tm) (TM) → ™\n// (r) (R) → ®\n// +- → ±\n// (p) (P) -> §\n// ... → … (also ?.... → ?.., !.... → !..)\n// ???????? → ???, !!!!! → !!!, `,,` → `,`\n// -- → –, --- → —\n//\n\n\n// TODO:\n// - fractionals 1/2, 1/4, 3/4 -> ½, ¼, ¾\n// - miltiplication 2 x 4 -> 2 × 4\n\nvar RARE_RE = /\\+-|\\.\\.|\\?\\?\\?\\?|!!!!|,,|--/;\n\n// Workaround for phantomjs - need regex without /g flag,\n// or root check will fail every second time\nvar SCOPED_ABBR_TEST_RE = /\\((c|tm|r|p)\\)/i;\n\nvar SCOPED_ABBR_RE = /\\((c|tm|r|p)\\)/ig;\nvar SCOPED_ABBR = {\n c: '©',\n r: '®',\n p: '§',\n tm: '™'\n};\n\nfunction replaceFn(match, name) {\n return SCOPED_ABBR[name.toLowerCase()];\n}\n\nfunction replace_scoped(inlineTokens) {\n var i, token, inside_autolink = 0;\n\n for (i = inlineTokens.length - 1; i >= 0; i--) {\n token = inlineTokens[i];\n\n if (token.type === 'text' && !inside_autolink) {\n token.content = token.content.replace(SCOPED_ABBR_RE, replaceFn);\n }\n\n if (token.type === 'link_open' && token.info === 'auto') {\n inside_autolink--;\n }\n\n if (token.type === 'link_close' && token.info === 'auto') {\n inside_autolink++;\n }\n }\n}\n\nfunction replace_rare(inlineTokens) {\n var i, token, inside_autolink = 0;\n\n for (i = inlineTokens.length - 1; i >= 0; i--) {\n token = inlineTokens[i];\n\n if (token.type === 'text' && !inside_autolink) {\n if (RARE_RE.test(token.content)) {\n token.content = token.content\n .replace(/\\+-/g, '±')\n // .., ..., ....... -> …\n // but ?..... & !..... -> ?.. & !..\n .replace(/\\.{2,}/g, '…').replace(/([?!])…/g, '$1..')\n .replace(/([?!]){4,}/g, '$1$1$1').replace(/,{2,}/g, ',')\n // em-dash\n .replace(/(^|[^-])---(?=[^-]|$)/mg, '$1\\u2014')\n // en-dash\n .replace(/(^|\\s)--(?=\\s|$)/mg, '$1\\u2013')\n .replace(/(^|[^-\\s])--(?=[^-\\s]|$)/mg, '$1\\u2013');\n }\n }\n\n if (token.type === 'link_open' && token.info === 'auto') {\n inside_autolink--;\n }\n\n if (token.type === 'link_close' && token.info === 'auto') {\n inside_autolink++;\n }\n }\n}\n\n\nmodule.exports = function replace(state) {\n var blkIdx;\n\n if (!state.md.options.typographer) { return; }\n\n for (blkIdx = state.tokens.length - 1; blkIdx >= 0; blkIdx--) {\n\n if (state.tokens[blkIdx].type !== 'inline') { continue; }\n\n if (SCOPED_ABBR_TEST_RE.test(state.tokens[blkIdx].content)) {\n replace_scoped(state.tokens[blkIdx].children);\n }\n\n if (RARE_RE.test(state.tokens[blkIdx].content)) {\n replace_rare(state.tokens[blkIdx].children);\n }\n\n }\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_core/replacements.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_core/smartquotes.js": -/*!****************************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_core/smartquotes.js ***! - \****************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Convert straight quotation marks to typographic ones\n//\n\n\n\nvar isWhiteSpace = __webpack_require__(/*! ../common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\").isWhiteSpace;\nvar isPunctChar = __webpack_require__(/*! ../common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\").isPunctChar;\nvar isMdAsciiPunct = __webpack_require__(/*! ../common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\").isMdAsciiPunct;\n\nvar QUOTE_TEST_RE = /['\"]/;\nvar QUOTE_RE = /['\"]/g;\nvar APOSTROPHE = '\\u2019'; /* ’ */\n\n\nfunction replaceAt(str, index, ch) {\n return str.substr(0, index) + ch + str.substr(index + 1);\n}\n\nfunction process_inlines(tokens, state) {\n var i, token, text, t, pos, max, thisLevel, item, lastChar, nextChar,\n isLastPunctChar, isNextPunctChar, isLastWhiteSpace, isNextWhiteSpace,\n canOpen, canClose, j, isSingle, stack, openQuote, closeQuote;\n\n stack = [];\n\n for (i = 0; i < tokens.length; i++) {\n token = tokens[i];\n\n thisLevel = tokens[i].level;\n\n for (j = stack.length - 1; j >= 0; j--) {\n if (stack[j].level <= thisLevel) { break; }\n }\n stack.length = j + 1;\n\n if (token.type !== 'text') { continue; }\n\n text = token.content;\n pos = 0;\n max = text.length;\n\n /*eslint no-labels:0,block-scoped-var:0*/\n OUTER:\n while (pos < max) {\n QUOTE_RE.lastIndex = pos;\n t = QUOTE_RE.exec(text);\n if (!t) { break; }\n\n canOpen = canClose = true;\n pos = t.index + 1;\n isSingle = (t[0] === \"'\");\n\n // Find previous character,\n // default to space if it's the beginning of the line\n //\n lastChar = 0x20;\n\n if (t.index - 1 >= 0) {\n lastChar = text.charCodeAt(t.index - 1);\n } else {\n for (j = i - 1; j >= 0; j--) {\n if (tokens[j].type === 'softbreak' || tokens[j].type === 'hardbreak') break; // lastChar defaults to 0x20\n if (!tokens[j].content) continue; // should skip all tokens except 'text', 'html_inline' or 'code_inline'\n\n lastChar = tokens[j].content.charCodeAt(tokens[j].content.length - 1);\n break;\n }\n }\n\n // Find next character,\n // default to space if it's the end of the line\n //\n nextChar = 0x20;\n\n if (pos < max) {\n nextChar = text.charCodeAt(pos);\n } else {\n for (j = i + 1; j < tokens.length; j++) {\n if (tokens[j].type === 'softbreak' || tokens[j].type === 'hardbreak') break; // nextChar defaults to 0x20\n if (!tokens[j].content) continue; // should skip all tokens except 'text', 'html_inline' or 'code_inline'\n\n nextChar = tokens[j].content.charCodeAt(0);\n break;\n }\n }\n\n isLastPunctChar = isMdAsciiPunct(lastChar) || isPunctChar(String.fromCharCode(lastChar));\n isNextPunctChar = isMdAsciiPunct(nextChar) || isPunctChar(String.fromCharCode(nextChar));\n\n isLastWhiteSpace = isWhiteSpace(lastChar);\n isNextWhiteSpace = isWhiteSpace(nextChar);\n\n if (isNextWhiteSpace) {\n canOpen = false;\n } else if (isNextPunctChar) {\n if (!(isLastWhiteSpace || isLastPunctChar)) {\n canOpen = false;\n }\n }\n\n if (isLastWhiteSpace) {\n canClose = false;\n } else if (isLastPunctChar) {\n if (!(isNextWhiteSpace || isNextPunctChar)) {\n canClose = false;\n }\n }\n\n if (nextChar === 0x22 /* \" */ && t[0] === '\"') {\n if (lastChar >= 0x30 /* 0 */ && lastChar <= 0x39 /* 9 */) {\n // special case: 1\"\" - count first quote as an inch\n canClose = canOpen = false;\n }\n }\n\n if (canOpen && canClose) {\n // Replace quotes in the middle of punctuation sequence, but not\n // in the middle of the words, i.e.:\n //\n // 1. foo \" bar \" baz - not replaced\n // 2. foo-\"-bar-\"-baz - replaced\n // 3. foo\"bar\"baz - not replaced\n //\n canOpen = isLastPunctChar;\n canClose = isNextPunctChar;\n }\n\n if (!canOpen && !canClose) {\n // middle of word\n if (isSingle) {\n token.content = replaceAt(token.content, t.index, APOSTROPHE);\n }\n continue;\n }\n\n if (canClose) {\n // this could be a closing quote, rewind the stack to get a match\n for (j = stack.length - 1; j >= 0; j--) {\n item = stack[j];\n if (stack[j].level < thisLevel) { break; }\n if (item.single === isSingle && stack[j].level === thisLevel) {\n item = stack[j];\n\n if (isSingle) {\n openQuote = state.md.options.quotes[2];\n closeQuote = state.md.options.quotes[3];\n } else {\n openQuote = state.md.options.quotes[0];\n closeQuote = state.md.options.quotes[1];\n }\n\n // replace token.content *before* tokens[item.token].content,\n // because, if they are pointing at the same token, replaceAt\n // could mess up indices when quote length != 1\n token.content = replaceAt(token.content, t.index, closeQuote);\n tokens[item.token].content = replaceAt(\n tokens[item.token].content, item.pos, openQuote);\n\n pos += closeQuote.length - 1;\n if (item.token === i) { pos += openQuote.length - 1; }\n\n text = token.content;\n max = text.length;\n\n stack.length = j;\n continue OUTER;\n }\n }\n }\n\n if (canOpen) {\n stack.push({\n token: i,\n pos: t.index,\n single: isSingle,\n level: thisLevel\n });\n } else if (canClose && isSingle) {\n token.content = replaceAt(token.content, t.index, APOSTROPHE);\n }\n }\n }\n}\n\n\nmodule.exports = function smartquotes(state) {\n /*eslint max-depth:0*/\n var blkIdx;\n\n if (!state.md.options.typographer) { return; }\n\n for (blkIdx = state.tokens.length - 1; blkIdx >= 0; blkIdx--) {\n\n if (state.tokens[blkIdx].type !== 'inline' ||\n !QUOTE_TEST_RE.test(state.tokens[blkIdx].content)) {\n continue;\n }\n\n process_inlines(state.tokens[blkIdx].children, state);\n }\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_core/smartquotes.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_core/state_core.js": -/*!***************************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_core/state_core.js ***! - \***************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Core state object\n//\n\n\nvar Token = __webpack_require__(/*! ../token */ \"./node_modules/markdown-it/lib/token.js\");\n\n\nfunction StateCore(src, md, env) {\n this.src = src;\n this.env = env;\n this.tokens = [];\n this.inlineMode = false;\n this.md = md; // link to parser instance\n}\n\n// re-export Token class to use in core rules\nStateCore.prototype.Token = Token;\n\n\nmodule.exports = StateCore;\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_core/state_core.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_inline/autolink.js": -/*!***************************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_inline/autolink.js ***! - \***************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Process autolinks ''\n\n\n\n\n/*eslint max-len:0*/\nvar EMAIL_RE = /^([a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)$/;\nvar AUTOLINK_RE = /^([a-zA-Z][a-zA-Z0-9+.\\-]{1,31}):([^<>\\x00-\\x20]*)$/;\n\n\nmodule.exports = function autolink(state, silent) {\n var url, fullUrl, token, ch, start, max,\n pos = state.pos;\n\n if (state.src.charCodeAt(pos) !== 0x3C/* < */) { return false; }\n\n start = state.pos;\n max = state.posMax;\n\n for (;;) {\n if (++pos >= max) return false;\n\n ch = state.src.charCodeAt(pos);\n\n if (ch === 0x3C /* < */) return false;\n if (ch === 0x3E /* > */) break;\n }\n\n url = state.src.slice(start + 1, pos);\n\n if (AUTOLINK_RE.test(url)) {\n fullUrl = state.md.normalizeLink(url);\n if (!state.md.validateLink(fullUrl)) { return false; }\n\n if (!silent) {\n token = state.push('link_open', 'a', 1);\n token.attrs = [ [ 'href', fullUrl ] ];\n token.markup = 'autolink';\n token.info = 'auto';\n\n token = state.push('text', '', 0);\n token.content = state.md.normalizeLinkText(url);\n\n token = state.push('link_close', 'a', -1);\n token.markup = 'autolink';\n token.info = 'auto';\n }\n\n state.pos += url.length + 2;\n return true;\n }\n\n if (EMAIL_RE.test(url)) {\n fullUrl = state.md.normalizeLink('mailto:' + url);\n if (!state.md.validateLink(fullUrl)) { return false; }\n\n if (!silent) {\n token = state.push('link_open', 'a', 1);\n token.attrs = [ [ 'href', fullUrl ] ];\n token.markup = 'autolink';\n token.info = 'auto';\n\n token = state.push('text', '', 0);\n token.content = state.md.normalizeLinkText(url);\n\n token = state.push('link_close', 'a', -1);\n token.markup = 'autolink';\n token.info = 'auto';\n }\n\n state.pos += url.length + 2;\n return true;\n }\n\n return false;\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_inline/autolink.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_inline/backticks.js": -/*!****************************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_inline/backticks.js ***! - \****************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Parse backticks\n\n\n\n\nmodule.exports = function backtick(state, silent) {\n var start, max, marker, token, matchStart, matchEnd, openerLength, closerLength,\n pos = state.pos,\n ch = state.src.charCodeAt(pos);\n\n if (ch !== 0x60/* ` */) { return false; }\n\n start = pos;\n pos++;\n max = state.posMax;\n\n // scan marker length\n while (pos < max && state.src.charCodeAt(pos) === 0x60/* ` */) { pos++; }\n\n marker = state.src.slice(start, pos);\n openerLength = marker.length;\n\n if (state.backticksScanned && (state.backticks[openerLength] || 0) <= start) {\n if (!silent) state.pending += marker;\n state.pos += openerLength;\n return true;\n }\n\n matchStart = matchEnd = pos;\n\n // Nothing found in the cache, scan until the end of the line (or until marker is found)\n while ((matchStart = state.src.indexOf('`', matchEnd)) !== -1) {\n matchEnd = matchStart + 1;\n\n // scan marker length\n while (matchEnd < max && state.src.charCodeAt(matchEnd) === 0x60/* ` */) { matchEnd++; }\n\n closerLength = matchEnd - matchStart;\n\n if (closerLength === openerLength) {\n // Found matching closer length.\n if (!silent) {\n token = state.push('code_inline', 'code', 0);\n token.markup = marker;\n token.content = state.src.slice(pos, matchStart)\n .replace(/\\n/g, ' ')\n .replace(/^ (.+) $/, '$1');\n }\n state.pos = matchEnd;\n return true;\n }\n\n // Some different length found, put it in cache as upper limit of where closer can be found\n state.backticks[closerLength] = matchStart;\n }\n\n // Scanned through the end, didn't find anything\n state.backticksScanned = true;\n\n if (!silent) state.pending += marker;\n state.pos += openerLength;\n return true;\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_inline/backticks.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_inline/balance_pairs.js": -/*!********************************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_inline/balance_pairs.js ***! - \********************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// For each opening emphasis-like marker find a matching closing one\n//\n\n\n\nfunction processDelimiters(state, delimiters) {\n var closerIdx, openerIdx, closer, opener, minOpenerIdx, newMinOpenerIdx,\n isOddMatch, lastJump,\n openersBottom = {},\n max = delimiters.length;\n\n for (closerIdx = 0; closerIdx < max; closerIdx++) {\n closer = delimiters[closerIdx];\n\n // Length is only used for emphasis-specific \"rule of 3\",\n // if it's not defined (in strikethrough or 3rd party plugins),\n // we can default it to 0 to disable those checks.\n //\n closer.length = closer.length || 0;\n\n if (!closer.close) continue;\n\n // Previously calculated lower bounds (previous fails)\n // for each marker and each delimiter length modulo 3.\n if (!openersBottom.hasOwnProperty(closer.marker)) {\n openersBottom[closer.marker] = [ -1, -1, -1 ];\n }\n\n minOpenerIdx = openersBottom[closer.marker][closer.length % 3];\n\n openerIdx = closerIdx - closer.jump - 1;\n newMinOpenerIdx = openerIdx;\n\n for (; openerIdx > minOpenerIdx; openerIdx -= opener.jump + 1) {\n opener = delimiters[openerIdx];\n\n if (opener.marker !== closer.marker) continue;\n\n if (opener.open && opener.end < 0) {\n\n isOddMatch = false;\n\n // from spec:\n //\n // If one of the delimiters can both open and close emphasis, then the\n // sum of the lengths of the delimiter runs containing the opening and\n // closing delimiters must not be a multiple of 3 unless both lengths\n // are multiples of 3.\n //\n if (opener.close || closer.open) {\n if ((opener.length + closer.length) % 3 === 0) {\n if (opener.length % 3 !== 0 || closer.length % 3 !== 0) {\n isOddMatch = true;\n }\n }\n }\n\n if (!isOddMatch) {\n // If previous delimiter cannot be an opener, we can safely skip\n // the entire sequence in future checks. This is required to make\n // sure algorithm has linear complexity (see *_*_*_*_*_... case).\n //\n lastJump = openerIdx > 0 && !delimiters[openerIdx - 1].open ?\n delimiters[openerIdx - 1].jump + 1 :\n 0;\n\n closer.jump = closerIdx - openerIdx + lastJump;\n closer.open = false;\n opener.end = closerIdx;\n opener.jump = lastJump;\n opener.close = false;\n newMinOpenerIdx = -1;\n break;\n }\n }\n }\n\n if (newMinOpenerIdx !== -1) {\n // If match for this delimiter run failed, we want to set lower bound for\n // future lookups. This is required to make sure algorithm has linear\n // complexity.\n //\n // See details here:\n // https://github.com/commonmark/cmark/issues/178#issuecomment-270417442\n //\n openersBottom[closer.marker][(closer.length || 0) % 3] = newMinOpenerIdx;\n }\n }\n}\n\n\nmodule.exports = function link_pairs(state) {\n var curr,\n tokens_meta = state.tokens_meta,\n max = state.tokens_meta.length;\n\n processDelimiters(state, state.delimiters);\n\n for (curr = 0; curr < max; curr++) {\n if (tokens_meta[curr] && tokens_meta[curr].delimiters) {\n processDelimiters(state, tokens_meta[curr].delimiters);\n }\n }\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_inline/balance_pairs.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_inline/emphasis.js": -/*!***************************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_inline/emphasis.js ***! - \***************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Process *this* and _that_\n//\n\n\n\n// Insert each marker as a separate text token, and add it to delimiter list\n//\nmodule.exports.tokenize = function emphasis(state, silent) {\n var i, scanned, token,\n start = state.pos,\n marker = state.src.charCodeAt(start);\n\n if (silent) { return false; }\n\n if (marker !== 0x5F /* _ */ && marker !== 0x2A /* * */) { return false; }\n\n scanned = state.scanDelims(state.pos, marker === 0x2A);\n\n for (i = 0; i < scanned.length; i++) {\n token = state.push('text', '', 0);\n token.content = String.fromCharCode(marker);\n\n state.delimiters.push({\n // Char code of the starting marker (number).\n //\n marker: marker,\n\n // Total length of these series of delimiters.\n //\n length: scanned.length,\n\n // An amount of characters before this one that's equivalent to\n // current one. In plain English: if this delimiter does not open\n // an emphasis, neither do previous `jump` characters.\n //\n // Used to skip sequences like \"*****\" in one step, for 1st asterisk\n // value will be 0, for 2nd it's 1 and so on.\n //\n jump: i,\n\n // A position of the token this delimiter corresponds to.\n //\n token: state.tokens.length - 1,\n\n // If this delimiter is matched as a valid opener, `end` will be\n // equal to its position, otherwise it's `-1`.\n //\n end: -1,\n\n // Boolean flags that determine if this delimiter could open or close\n // an emphasis.\n //\n open: scanned.can_open,\n close: scanned.can_close\n });\n }\n\n state.pos += scanned.length;\n\n return true;\n};\n\n\nfunction postProcess(state, delimiters) {\n var i,\n startDelim,\n endDelim,\n token,\n ch,\n isStrong,\n max = delimiters.length;\n\n for (i = max - 1; i >= 0; i--) {\n startDelim = delimiters[i];\n\n if (startDelim.marker !== 0x5F/* _ */ && startDelim.marker !== 0x2A/* * */) {\n continue;\n }\n\n // Process only opening markers\n if (startDelim.end === -1) {\n continue;\n }\n\n endDelim = delimiters[startDelim.end];\n\n // If the previous delimiter has the same marker and is adjacent to this one,\n // merge those into one strong delimiter.\n //\n // `whatever` -> `whatever`\n //\n isStrong = i > 0 &&\n delimiters[i - 1].end === startDelim.end + 1 &&\n delimiters[i - 1].token === startDelim.token - 1 &&\n delimiters[startDelim.end + 1].token === endDelim.token + 1 &&\n delimiters[i - 1].marker === startDelim.marker;\n\n ch = String.fromCharCode(startDelim.marker);\n\n token = state.tokens[startDelim.token];\n token.type = isStrong ? 'strong_open' : 'em_open';\n token.tag = isStrong ? 'strong' : 'em';\n token.nesting = 1;\n token.markup = isStrong ? ch + ch : ch;\n token.content = '';\n\n token = state.tokens[endDelim.token];\n token.type = isStrong ? 'strong_close' : 'em_close';\n token.tag = isStrong ? 'strong' : 'em';\n token.nesting = -1;\n token.markup = isStrong ? ch + ch : ch;\n token.content = '';\n\n if (isStrong) {\n state.tokens[delimiters[i - 1].token].content = '';\n state.tokens[delimiters[startDelim.end + 1].token].content = '';\n i--;\n }\n }\n}\n\n\n// Walk through delimiter list and replace text tokens with tags\n//\nmodule.exports.postProcess = function emphasis(state) {\n var curr,\n tokens_meta = state.tokens_meta,\n max = state.tokens_meta.length;\n\n postProcess(state, state.delimiters);\n\n for (curr = 0; curr < max; curr++) {\n if (tokens_meta[curr] && tokens_meta[curr].delimiters) {\n postProcess(state, tokens_meta[curr].delimiters);\n }\n }\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_inline/emphasis.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_inline/entity.js": -/*!*************************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_inline/entity.js ***! - \*************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Process html entity - {, ¯, ", ...\n\n\n\nvar entities = __webpack_require__(/*! ../common/entities */ \"./node_modules/markdown-it/lib/common/entities.js\");\nvar has = __webpack_require__(/*! ../common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\").has;\nvar isValidEntityCode = __webpack_require__(/*! ../common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\").isValidEntityCode;\nvar fromCodePoint = __webpack_require__(/*! ../common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\").fromCodePoint;\n\n\nvar DIGITAL_RE = /^&#((?:x[a-f0-9]{1,6}|[0-9]{1,7}));/i;\nvar NAMED_RE = /^&([a-z][a-z0-9]{1,31});/i;\n\n\nmodule.exports = function entity(state, silent) {\n var ch, code, match, pos = state.pos, max = state.posMax;\n\n if (state.src.charCodeAt(pos) !== 0x26/* & */) { return false; }\n\n if (pos + 1 < max) {\n ch = state.src.charCodeAt(pos + 1);\n\n if (ch === 0x23 /* # */) {\n match = state.src.slice(pos).match(DIGITAL_RE);\n if (match) {\n if (!silent) {\n code = match[1][0].toLowerCase() === 'x' ? parseInt(match[1].slice(1), 16) : parseInt(match[1], 10);\n state.pending += isValidEntityCode(code) ? fromCodePoint(code) : fromCodePoint(0xFFFD);\n }\n state.pos += match[0].length;\n return true;\n }\n } else {\n match = state.src.slice(pos).match(NAMED_RE);\n if (match) {\n if (has(entities, match[1])) {\n if (!silent) { state.pending += entities[match[1]]; }\n state.pos += match[0].length;\n return true;\n }\n }\n }\n }\n\n if (!silent) { state.pending += '&'; }\n state.pos++;\n return true;\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_inline/entity.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_inline/escape.js": -/*!*************************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_inline/escape.js ***! - \*************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Process escaped chars and hardbreaks\n\n\n\nvar isSpace = __webpack_require__(/*! ../common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\").isSpace;\n\nvar ESCAPED = [];\n\nfor (var i = 0; i < 256; i++) { ESCAPED.push(0); }\n\n'\\\\!\"#$%&\\'()*+,./:;<=>?@[]^_`{|}~-'\n .split('').forEach(function (ch) { ESCAPED[ch.charCodeAt(0)] = 1; });\n\n\nmodule.exports = function escape(state, silent) {\n var ch, pos = state.pos, max = state.posMax;\n\n if (state.src.charCodeAt(pos) !== 0x5C/* \\ */) { return false; }\n\n pos++;\n\n if (pos < max) {\n ch = state.src.charCodeAt(pos);\n\n if (ch < 256 && ESCAPED[ch] !== 0) {\n if (!silent) { state.pending += state.src[pos]; }\n state.pos += 2;\n return true;\n }\n\n if (ch === 0x0A) {\n if (!silent) {\n state.push('hardbreak', 'br', 0);\n }\n\n pos++;\n // skip leading whitespaces from next line\n while (pos < max) {\n ch = state.src.charCodeAt(pos);\n if (!isSpace(ch)) { break; }\n pos++;\n }\n\n state.pos = pos;\n return true;\n }\n }\n\n if (!silent) { state.pending += '\\\\'; }\n state.pos++;\n return true;\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_inline/escape.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_inline/html_inline.js": -/*!******************************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_inline/html_inline.js ***! - \******************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Process html tags\n\n\n\n\nvar HTML_TAG_RE = __webpack_require__(/*! ../common/html_re */ \"./node_modules/markdown-it/lib/common/html_re.js\").HTML_TAG_RE;\n\n\nfunction isLetter(ch) {\n /*eslint no-bitwise:0*/\n var lc = ch | 0x20; // to lower case\n return (lc >= 0x61/* a */) && (lc <= 0x7a/* z */);\n}\n\n\nmodule.exports = function html_inline(state, silent) {\n var ch, match, max, token,\n pos = state.pos;\n\n if (!state.md.options.html) { return false; }\n\n // Check start\n max = state.posMax;\n if (state.src.charCodeAt(pos) !== 0x3C/* < */ ||\n pos + 2 >= max) {\n return false;\n }\n\n // Quick fail on second char\n ch = state.src.charCodeAt(pos + 1);\n if (ch !== 0x21/* ! */ &&\n ch !== 0x3F/* ? */ &&\n ch !== 0x2F/* / */ &&\n !isLetter(ch)) {\n return false;\n }\n\n match = state.src.slice(pos).match(HTML_TAG_RE);\n if (!match) { return false; }\n\n if (!silent) {\n token = state.push('html_inline', '', 0);\n token.content = state.src.slice(pos, pos + match[0].length);\n }\n state.pos += match[0].length;\n return true;\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_inline/html_inline.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_inline/image.js": -/*!************************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_inline/image.js ***! - \************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Process ![image]( \"title\")\n\n\n\nvar normalizeReference = __webpack_require__(/*! ../common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\").normalizeReference;\nvar isSpace = __webpack_require__(/*! ../common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\").isSpace;\n\n\nmodule.exports = function image(state, silent) {\n var attrs,\n code,\n content,\n label,\n labelEnd,\n labelStart,\n pos,\n ref,\n res,\n title,\n token,\n tokens,\n start,\n href = '',\n oldPos = state.pos,\n max = state.posMax;\n\n if (state.src.charCodeAt(state.pos) !== 0x21/* ! */) { return false; }\n if (state.src.charCodeAt(state.pos + 1) !== 0x5B/* [ */) { return false; }\n\n labelStart = state.pos + 2;\n labelEnd = state.md.helpers.parseLinkLabel(state, state.pos + 1, false);\n\n // parser failed to find ']', so it's not a valid link\n if (labelEnd < 0) { return false; }\n\n pos = labelEnd + 1;\n if (pos < max && state.src.charCodeAt(pos) === 0x28/* ( */) {\n //\n // Inline link\n //\n\n // [link]( \"title\" )\n // ^^ skipping these spaces\n pos++;\n for (; pos < max; pos++) {\n code = state.src.charCodeAt(pos);\n if (!isSpace(code) && code !== 0x0A) { break; }\n }\n if (pos >= max) { return false; }\n\n // [link]( \"title\" )\n // ^^^^^^ parsing link destination\n start = pos;\n res = state.md.helpers.parseLinkDestination(state.src, pos, state.posMax);\n if (res.ok) {\n href = state.md.normalizeLink(res.str);\n if (state.md.validateLink(href)) {\n pos = res.pos;\n } else {\n href = '';\n }\n }\n\n // [link]( \"title\" )\n // ^^ skipping these spaces\n start = pos;\n for (; pos < max; pos++) {\n code = state.src.charCodeAt(pos);\n if (!isSpace(code) && code !== 0x0A) { break; }\n }\n\n // [link]( \"title\" )\n // ^^^^^^^ parsing link title\n res = state.md.helpers.parseLinkTitle(state.src, pos, state.posMax);\n if (pos < max && start !== pos && res.ok) {\n title = res.str;\n pos = res.pos;\n\n // [link]( \"title\" )\n // ^^ skipping these spaces\n for (; pos < max; pos++) {\n code = state.src.charCodeAt(pos);\n if (!isSpace(code) && code !== 0x0A) { break; }\n }\n } else {\n title = '';\n }\n\n if (pos >= max || state.src.charCodeAt(pos) !== 0x29/* ) */) {\n state.pos = oldPos;\n return false;\n }\n pos++;\n } else {\n //\n // Link reference\n //\n if (typeof state.env.references === 'undefined') { return false; }\n\n if (pos < max && state.src.charCodeAt(pos) === 0x5B/* [ */) {\n start = pos + 1;\n pos = state.md.helpers.parseLinkLabel(state, pos);\n if (pos >= 0) {\n label = state.src.slice(start, pos++);\n } else {\n pos = labelEnd + 1;\n }\n } else {\n pos = labelEnd + 1;\n }\n\n // covers label === '' and label === undefined\n // (collapsed reference link and shortcut reference link respectively)\n if (!label) { label = state.src.slice(labelStart, labelEnd); }\n\n ref = state.env.references[normalizeReference(label)];\n if (!ref) {\n state.pos = oldPos;\n return false;\n }\n href = ref.href;\n title = ref.title;\n }\n\n //\n // We found the end of the link, and know for a fact it's a valid link;\n // so all that's left to do is to call tokenizer.\n //\n if (!silent) {\n content = state.src.slice(labelStart, labelEnd);\n\n state.md.inline.parse(\n content,\n state.md,\n state.env,\n tokens = []\n );\n\n token = state.push('image', 'img', 0);\n token.attrs = attrs = [ [ 'src', href ], [ 'alt', '' ] ];\n token.children = tokens;\n token.content = content;\n\n if (title) {\n attrs.push([ 'title', title ]);\n }\n }\n\n state.pos = pos;\n state.posMax = max;\n return true;\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_inline/image.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_inline/link.js": -/*!***********************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_inline/link.js ***! - \***********************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Process [link]( \"stuff\")\n\n\n\nvar normalizeReference = __webpack_require__(/*! ../common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\").normalizeReference;\nvar isSpace = __webpack_require__(/*! ../common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\").isSpace;\n\n\nmodule.exports = function link(state, silent) {\n var attrs,\n code,\n label,\n labelEnd,\n labelStart,\n pos,\n res,\n ref,\n token,\n href = '',\n title = '',\n oldPos = state.pos,\n max = state.posMax,\n start = state.pos,\n parseReference = true;\n\n if (state.src.charCodeAt(state.pos) !== 0x5B/* [ */) { return false; }\n\n labelStart = state.pos + 1;\n labelEnd = state.md.helpers.parseLinkLabel(state, state.pos, true);\n\n // parser failed to find ']', so it's not a valid link\n if (labelEnd < 0) { return false; }\n\n pos = labelEnd + 1;\n if (pos < max && state.src.charCodeAt(pos) === 0x28/* ( */) {\n //\n // Inline link\n //\n\n // might have found a valid shortcut link, disable reference parsing\n parseReference = false;\n\n // [link]( \"title\" )\n // ^^ skipping these spaces\n pos++;\n for (; pos < max; pos++) {\n code = state.src.charCodeAt(pos);\n if (!isSpace(code) && code !== 0x0A) { break; }\n }\n if (pos >= max) { return false; }\n\n // [link]( \"title\" )\n // ^^^^^^ parsing link destination\n start = pos;\n res = state.md.helpers.parseLinkDestination(state.src, pos, state.posMax);\n if (res.ok) {\n href = state.md.normalizeLink(res.str);\n if (state.md.validateLink(href)) {\n pos = res.pos;\n } else {\n href = '';\n }\n\n // [link]( \"title\" )\n // ^^ skipping these spaces\n start = pos;\n for (; pos < max; pos++) {\n code = state.src.charCodeAt(pos);\n if (!isSpace(code) && code !== 0x0A) { break; }\n }\n\n // [link]( \"title\" )\n // ^^^^^^^ parsing link title\n res = state.md.helpers.parseLinkTitle(state.src, pos, state.posMax);\n if (pos < max && start !== pos && res.ok) {\n title = res.str;\n pos = res.pos;\n\n // [link]( \"title\" )\n // ^^ skipping these spaces\n for (; pos < max; pos++) {\n code = state.src.charCodeAt(pos);\n if (!isSpace(code) && code !== 0x0A) { break; }\n }\n }\n }\n\n if (pos >= max || state.src.charCodeAt(pos) !== 0x29/* ) */) {\n // parsing a valid shortcut link failed, fallback to reference\n parseReference = true;\n }\n pos++;\n }\n\n if (parseReference) {\n //\n // Link reference\n //\n if (typeof state.env.references === 'undefined') { return false; }\n\n if (pos < max && state.src.charCodeAt(pos) === 0x5B/* [ */) {\n start = pos + 1;\n pos = state.md.helpers.parseLinkLabel(state, pos);\n if (pos >= 0) {\n label = state.src.slice(start, pos++);\n } else {\n pos = labelEnd + 1;\n }\n } else {\n pos = labelEnd + 1;\n }\n\n // covers label === '' and label === undefined\n // (collapsed reference link and shortcut reference link respectively)\n if (!label) { label = state.src.slice(labelStart, labelEnd); }\n\n ref = state.env.references[normalizeReference(label)];\n if (!ref) {\n state.pos = oldPos;\n return false;\n }\n href = ref.href;\n title = ref.title;\n }\n\n //\n // We found the end of the link, and know for a fact it's a valid link;\n // so all that's left to do is to call tokenizer.\n //\n if (!silent) {\n state.pos = labelStart;\n state.posMax = labelEnd;\n\n token = state.push('link_open', 'a', 1);\n token.attrs = attrs = [ [ 'href', href ] ];\n if (title) {\n attrs.push([ 'title', title ]);\n }\n\n state.md.inline.tokenize(state);\n\n token = state.push('link_close', 'a', -1);\n }\n\n state.pos = pos;\n state.posMax = max;\n return true;\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_inline/link.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_inline/newline.js": -/*!**************************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_inline/newline.js ***! - \**************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Proceess '\\n'\n\n\n\nvar isSpace = __webpack_require__(/*! ../common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\").isSpace;\n\n\nmodule.exports = function newline(state, silent) {\n var pmax, max, pos = state.pos;\n\n if (state.src.charCodeAt(pos) !== 0x0A/* \\n */) { return false; }\n\n pmax = state.pending.length - 1;\n max = state.posMax;\n\n // ' \\n' -> hardbreak\n // Lookup in pending chars is bad practice! Don't copy to other rules!\n // Pending string is stored in concat mode, indexed lookups will cause\n // convertion to flat mode.\n if (!silent) {\n if (pmax >= 0 && state.pending.charCodeAt(pmax) === 0x20) {\n if (pmax >= 1 && state.pending.charCodeAt(pmax - 1) === 0x20) {\n state.pending = state.pending.replace(/ +$/, '');\n state.push('hardbreak', 'br', 0);\n } else {\n state.pending = state.pending.slice(0, -1);\n state.push('softbreak', 'br', 0);\n }\n\n } else {\n state.push('softbreak', 'br', 0);\n }\n }\n\n pos++;\n\n // skip heading spaces for next line\n while (pos < max && isSpace(state.src.charCodeAt(pos))) { pos++; }\n\n state.pos = pos;\n return true;\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_inline/newline.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_inline/state_inline.js": -/*!*******************************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_inline/state_inline.js ***! - \*******************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Inline parser state\n\n\n\n\nvar Token = __webpack_require__(/*! ../token */ \"./node_modules/markdown-it/lib/token.js\");\nvar isWhiteSpace = __webpack_require__(/*! ../common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\").isWhiteSpace;\nvar isPunctChar = __webpack_require__(/*! ../common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\").isPunctChar;\nvar isMdAsciiPunct = __webpack_require__(/*! ../common/utils */ \"./node_modules/markdown-it/lib/common/utils.js\").isMdAsciiPunct;\n\n\nfunction StateInline(src, md, env, outTokens) {\n this.src = src;\n this.env = env;\n this.md = md;\n this.tokens = outTokens;\n this.tokens_meta = Array(outTokens.length);\n\n this.pos = 0;\n this.posMax = this.src.length;\n this.level = 0;\n this.pending = '';\n this.pendingLevel = 0;\n\n // Stores { start: end } pairs. Useful for backtrack\n // optimization of pairs parse (emphasis, strikes).\n this.cache = {};\n\n // List of emphasis-like delimiters for current tag\n this.delimiters = [];\n\n // Stack of delimiter lists for upper level tags\n this._prev_delimiters = [];\n\n // backtick length => last seen position\n this.backticks = {};\n this.backticksScanned = false;\n}\n\n\n// Flush pending text\n//\nStateInline.prototype.pushPending = function () {\n var token = new Token('text', '', 0);\n token.content = this.pending;\n token.level = this.pendingLevel;\n this.tokens.push(token);\n this.pending = '';\n return token;\n};\n\n\n// Push new token to \"stream\".\n// If pending text exists - flush it as text token\n//\nStateInline.prototype.push = function (type, tag, nesting) {\n if (this.pending) {\n this.pushPending();\n }\n\n var token = new Token(type, tag, nesting);\n var token_meta = null;\n\n if (nesting < 0) {\n // closing tag\n this.level--;\n this.delimiters = this._prev_delimiters.pop();\n }\n\n token.level = this.level;\n\n if (nesting > 0) {\n // opening tag\n this.level++;\n this._prev_delimiters.push(this.delimiters);\n this.delimiters = [];\n token_meta = { delimiters: this.delimiters };\n }\n\n this.pendingLevel = this.level;\n this.tokens.push(token);\n this.tokens_meta.push(token_meta);\n return token;\n};\n\n\n// Scan a sequence of emphasis-like markers, and determine whether\n// it can start an emphasis sequence or end an emphasis sequence.\n//\n// - start - position to scan from (it should point at a valid marker);\n// - canSplitWord - determine if these markers can be found inside a word\n//\nStateInline.prototype.scanDelims = function (start, canSplitWord) {\n var pos = start, lastChar, nextChar, count, can_open, can_close,\n isLastWhiteSpace, isLastPunctChar,\n isNextWhiteSpace, isNextPunctChar,\n left_flanking = true,\n right_flanking = true,\n max = this.posMax,\n marker = this.src.charCodeAt(start);\n\n // treat beginning of the line as a whitespace\n lastChar = start > 0 ? this.src.charCodeAt(start - 1) : 0x20;\n\n while (pos < max && this.src.charCodeAt(pos) === marker) { pos++; }\n\n count = pos - start;\n\n // treat end of the line as a whitespace\n nextChar = pos < max ? this.src.charCodeAt(pos) : 0x20;\n\n isLastPunctChar = isMdAsciiPunct(lastChar) || isPunctChar(String.fromCharCode(lastChar));\n isNextPunctChar = isMdAsciiPunct(nextChar) || isPunctChar(String.fromCharCode(nextChar));\n\n isLastWhiteSpace = isWhiteSpace(lastChar);\n isNextWhiteSpace = isWhiteSpace(nextChar);\n\n if (isNextWhiteSpace) {\n left_flanking = false;\n } else if (isNextPunctChar) {\n if (!(isLastWhiteSpace || isLastPunctChar)) {\n left_flanking = false;\n }\n }\n\n if (isLastWhiteSpace) {\n right_flanking = false;\n } else if (isLastPunctChar) {\n if (!(isNextWhiteSpace || isNextPunctChar)) {\n right_flanking = false;\n }\n }\n\n if (!canSplitWord) {\n can_open = left_flanking && (!right_flanking || isLastPunctChar);\n can_close = right_flanking && (!left_flanking || isNextPunctChar);\n } else {\n can_open = left_flanking;\n can_close = right_flanking;\n }\n\n return {\n can_open: can_open,\n can_close: can_close,\n length: count\n };\n};\n\n\n// re-export Token class to use in block rules\nStateInline.prototype.Token = Token;\n\n\nmodule.exports = StateInline;\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_inline/state_inline.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_inline/strikethrough.js": -/*!********************************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_inline/strikethrough.js ***! - \********************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// ~~strike through~~\n//\n\n\n\n// Insert each marker as a separate text token, and add it to delimiter list\n//\nmodule.exports.tokenize = function strikethrough(state, silent) {\n var i, scanned, token, len, ch,\n start = state.pos,\n marker = state.src.charCodeAt(start);\n\n if (silent) { return false; }\n\n if (marker !== 0x7E/* ~ */) { return false; }\n\n scanned = state.scanDelims(state.pos, true);\n len = scanned.length;\n ch = String.fromCharCode(marker);\n\n if (len < 2) { return false; }\n\n if (len % 2) {\n token = state.push('text', '', 0);\n token.content = ch;\n len--;\n }\n\n for (i = 0; i < len; i += 2) {\n token = state.push('text', '', 0);\n token.content = ch + ch;\n\n state.delimiters.push({\n marker: marker,\n length: 0, // disable \"rule of 3\" length checks meant for emphasis\n jump: i,\n token: state.tokens.length - 1,\n end: -1,\n open: scanned.can_open,\n close: scanned.can_close\n });\n }\n\n state.pos += scanned.length;\n\n return true;\n};\n\n\nfunction postProcess(state, delimiters) {\n var i, j,\n startDelim,\n endDelim,\n token,\n loneMarkers = [],\n max = delimiters.length;\n\n for (i = 0; i < max; i++) {\n startDelim = delimiters[i];\n\n if (startDelim.marker !== 0x7E/* ~ */) {\n continue;\n }\n\n if (startDelim.end === -1) {\n continue;\n }\n\n endDelim = delimiters[startDelim.end];\n\n token = state.tokens[startDelim.token];\n token.type = 's_open';\n token.tag = 's';\n token.nesting = 1;\n token.markup = '~~';\n token.content = '';\n\n token = state.tokens[endDelim.token];\n token.type = 's_close';\n token.tag = 's';\n token.nesting = -1;\n token.markup = '~~';\n token.content = '';\n\n if (state.tokens[endDelim.token - 1].type === 'text' &&\n state.tokens[endDelim.token - 1].content === '~') {\n\n loneMarkers.push(endDelim.token - 1);\n }\n }\n\n // If a marker sequence has an odd number of characters, it's splitted\n // like this: `~~~~~` -> `~` + `~~` + `~~`, leaving one marker at the\n // start of the sequence.\n //\n // So, we have to move all those markers after subsequent s_close tags.\n //\n while (loneMarkers.length) {\n i = loneMarkers.pop();\n j = i + 1;\n\n while (j < state.tokens.length && state.tokens[j].type === 's_close') {\n j++;\n }\n\n j--;\n\n if (i !== j) {\n token = state.tokens[j];\n state.tokens[j] = state.tokens[i];\n state.tokens[i] = token;\n }\n }\n}\n\n\n// Walk through delimiter list and replace text tokens with tags\n//\nmodule.exports.postProcess = function strikethrough(state) {\n var curr,\n tokens_meta = state.tokens_meta,\n max = state.tokens_meta.length;\n\n postProcess(state, state.delimiters);\n\n for (curr = 0; curr < max; curr++) {\n if (tokens_meta[curr] && tokens_meta[curr].delimiters) {\n postProcess(state, tokens_meta[curr].delimiters);\n }\n }\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_inline/strikethrough.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_inline/text.js": -/*!***********************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_inline/text.js ***! - \***********************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Skip text characters for text token, place those to pending buffer\n// and increment current pos\n\n\n\n\n// Rule to skip pure text\n// '{}$%@~+=:' reserved for extentions\n\n// !, \", #, $, %, &, ', (, ), *, +, ,, -, ., /, :, ;, <, =, >, ?, @, [, \\, ], ^, _, `, {, |, }, or ~\n\n// !!!! Don't confuse with \"Markdown ASCII Punctuation\" chars\n// http://spec.commonmark.org/0.15/#ascii-punctuation-character\nfunction isTerminatorChar(ch) {\n switch (ch) {\n case 0x0A/* \\n */:\n case 0x21/* ! */:\n case 0x23/* # */:\n case 0x24/* $ */:\n case 0x25/* % */:\n case 0x26/* & */:\n case 0x2A/* * */:\n case 0x2B/* + */:\n case 0x2D/* - */:\n case 0x3A/* : */:\n case 0x3C/* < */:\n case 0x3D/* = */:\n case 0x3E/* > */:\n case 0x40/* @ */:\n case 0x5B/* [ */:\n case 0x5C/* \\ */:\n case 0x5D/* ] */:\n case 0x5E/* ^ */:\n case 0x5F/* _ */:\n case 0x60/* ` */:\n case 0x7B/* { */:\n case 0x7D/* } */:\n case 0x7E/* ~ */:\n return true;\n default:\n return false;\n }\n}\n\nmodule.exports = function text(state, silent) {\n var pos = state.pos;\n\n while (pos < state.posMax && !isTerminatorChar(state.src.charCodeAt(pos))) {\n pos++;\n }\n\n if (pos === state.pos) { return false; }\n\n if (!silent) { state.pending += state.src.slice(state.pos, pos); }\n\n state.pos = pos;\n\n return true;\n};\n\n// Alternative implementation, for memory.\n//\n// It costs 10% of performance, but allows extend terminators list, if place it\n// to `ParcerInline` property. Probably, will switch to it sometime, such\n// flexibility required.\n\n/*\nvar TERMINATOR_RE = /[\\n!#$%&*+\\-:<=>@[\\\\\\]^_`{}~]/;\n\nmodule.exports = function text(state, silent) {\n var pos = state.pos,\n idx = state.src.slice(pos).search(TERMINATOR_RE);\n\n // first char is terminator -> empty text\n if (idx === 0) { return false; }\n\n // no terminator -> text till end of string\n if (idx < 0) {\n if (!silent) { state.pending += state.src.slice(pos); }\n state.pos = state.src.length;\n return true;\n }\n\n if (!silent) { state.pending += state.src.slice(pos, pos + idx); }\n\n state.pos += idx;\n\n return true;\n};*/\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_inline/text.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/rules_inline/text_collapse.js": -/*!********************************************************************!*\ - !*** ./node_modules/markdown-it/lib/rules_inline/text_collapse.js ***! - \********************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Clean up tokens after emphasis and strikethrough postprocessing:\n// merge adjacent text nodes into one and re-calculate all token levels\n//\n// This is necessary because initially emphasis delimiter markers (*, _, ~)\n// are treated as their own separate text tokens. Then emphasis rule either\n// leaves them as text (needed to merge with adjacent text) or turns them\n// into opening/closing tags (which messes up levels inside).\n//\n\n\n\nmodule.exports = function text_collapse(state) {\n var curr, last,\n level = 0,\n tokens = state.tokens,\n max = state.tokens.length;\n\n for (curr = last = 0; curr < max; curr++) {\n // re-calculate levels after emphasis/strikethrough turns some text nodes\n // into opening/closing tags\n if (tokens[curr].nesting < 0) level--; // closing tag\n tokens[curr].level = level;\n if (tokens[curr].nesting > 0) level++; // opening tag\n\n if (tokens[curr].type === 'text' &&\n curr + 1 < max &&\n tokens[curr + 1].type === 'text') {\n\n // collapse two adjacent text nodes\n tokens[curr + 1].content = tokens[curr].content + tokens[curr + 1].content;\n } else {\n if (curr !== last) { tokens[last] = tokens[curr]; }\n\n last++;\n }\n }\n\n if (curr !== last) {\n tokens.length = last;\n }\n};\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/rules_inline/text_collapse.js?"); - -/***/ }), - -/***/ "./node_modules/markdown-it/lib/token.js": -/*!***********************************************!*\ - !*** ./node_modules/markdown-it/lib/token.js ***! - \***********************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Token class\n\n\n\n\n/**\n * class Token\n **/\n\n/**\n * new Token(type, tag, nesting)\n *\n * Create new token and fill passed properties.\n **/\nfunction Token(type, tag, nesting) {\n /**\n * Token#type -> String\n *\n * Type of the token (string, e.g. \"paragraph_open\")\n **/\n this.type = type;\n\n /**\n * Token#tag -> String\n *\n * html tag name, e.g. \"p\"\n **/\n this.tag = tag;\n\n /**\n * Token#attrs -> Array\n *\n * Html attributes. Format: `[ [ name1, value1 ], [ name2, value2 ] ]`\n **/\n this.attrs = null;\n\n /**\n * Token#map -> Array\n *\n * Source map info. Format: `[ line_begin, line_end ]`\n **/\n this.map = null;\n\n /**\n * Token#nesting -> Number\n *\n * Level change (number in {-1, 0, 1} set), where:\n *\n * - `1` means the tag is opening\n * - `0` means the tag is self-closing\n * - `-1` means the tag is closing\n **/\n this.nesting = nesting;\n\n /**\n * Token#level -> Number\n *\n * nesting level, the same as `state.level`\n **/\n this.level = 0;\n\n /**\n * Token#children -> Array\n *\n * An array of child nodes (inline and img tokens)\n **/\n this.children = null;\n\n /**\n * Token#content -> String\n *\n * In a case of self-closing tag (code, html, fence, etc.),\n * it has contents of this tag.\n **/\n this.content = '';\n\n /**\n * Token#markup -> String\n *\n * '*' or '_' for emphasis, fence string for fence, etc.\n **/\n this.markup = '';\n\n /**\n * Token#info -> String\n *\n * fence infostring\n **/\n this.info = '';\n\n /**\n * Token#meta -> Object\n *\n * A place for plugins to store an arbitrary data\n **/\n this.meta = null;\n\n /**\n * Token#block -> Boolean\n *\n * True for block-level tokens, false for inline tokens.\n * Used in renderer to calculate line breaks\n **/\n this.block = false;\n\n /**\n * Token#hidden -> Boolean\n *\n * If it's true, ignore this element when rendering. Used for tight lists\n * to hide paragraphs.\n **/\n this.hidden = false;\n}\n\n\n/**\n * Token.attrIndex(name) -> Number\n *\n * Search attribute index by name.\n **/\nToken.prototype.attrIndex = function attrIndex(name) {\n var attrs, i, len;\n\n if (!this.attrs) { return -1; }\n\n attrs = this.attrs;\n\n for (i = 0, len = attrs.length; i < len; i++) {\n if (attrs[i][0] === name) { return i; }\n }\n return -1;\n};\n\n\n/**\n * Token.attrPush(attrData)\n *\n * Add `[ name, value ]` attribute to list. Init attrs if necessary\n **/\nToken.prototype.attrPush = function attrPush(attrData) {\n if (this.attrs) {\n this.attrs.push(attrData);\n } else {\n this.attrs = [ attrData ];\n }\n};\n\n\n/**\n * Token.attrSet(name, value)\n *\n * Set `name` attribute to `value`. Override old value if exists.\n **/\nToken.prototype.attrSet = function attrSet(name, value) {\n var idx = this.attrIndex(name),\n attrData = [ name, value ];\n\n if (idx < 0) {\n this.attrPush(attrData);\n } else {\n this.attrs[idx] = attrData;\n }\n};\n\n\n/**\n * Token.attrGet(name)\n *\n * Get the value of attribute `name`, or null if it does not exist.\n **/\nToken.prototype.attrGet = function attrGet(name) {\n var idx = this.attrIndex(name), value = null;\n if (idx >= 0) {\n value = this.attrs[idx][1];\n }\n return value;\n};\n\n\n/**\n * Token.attrJoin(name, value)\n *\n * Join value to existing attribute via space. Or create new attribute if not\n * exists. Useful to operate with token classes.\n **/\nToken.prototype.attrJoin = function attrJoin(name, value) {\n var idx = this.attrIndex(name);\n\n if (idx < 0) {\n this.attrPush([ name, value ]);\n } else {\n this.attrs[idx][1] = this.attrs[idx][1] + ' ' + value;\n }\n};\n\n\nmodule.exports = Token;\n\n\n//# sourceURL=webpack:///./node_modules/markdown-it/lib/token.js?"); - -/***/ }), - -/***/ "./node_modules/mdurl/decode.js": -/*!**************************************!*\ - !*** ./node_modules/mdurl/decode.js ***! - \**************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\n\n\n/* eslint-disable no-bitwise */\n\nvar decodeCache = {};\n\nfunction getDecodeCache(exclude) {\n var i, ch, cache = decodeCache[exclude];\n if (cache) { return cache; }\n\n cache = decodeCache[exclude] = [];\n\n for (i = 0; i < 128; i++) {\n ch = String.fromCharCode(i);\n cache.push(ch);\n }\n\n for (i = 0; i < exclude.length; i++) {\n ch = exclude.charCodeAt(i);\n cache[ch] = '%' + ('0' + ch.toString(16).toUpperCase()).slice(-2);\n }\n\n return cache;\n}\n\n\n// Decode percent-encoded string.\n//\nfunction decode(string, exclude) {\n var cache;\n\n if (typeof exclude !== 'string') {\n exclude = decode.defaultChars;\n }\n\n cache = getDecodeCache(exclude);\n\n return string.replace(/(%[a-f0-9]{2})+/gi, function(seq) {\n var i, l, b1, b2, b3, b4, chr,\n result = '';\n\n for (i = 0, l = seq.length; i < l; i += 3) {\n b1 = parseInt(seq.slice(i + 1, i + 3), 16);\n\n if (b1 < 0x80) {\n result += cache[b1];\n continue;\n }\n\n if ((b1 & 0xE0) === 0xC0 && (i + 3 < l)) {\n // 110xxxxx 10xxxxxx\n b2 = parseInt(seq.slice(i + 4, i + 6), 16);\n\n if ((b2 & 0xC0) === 0x80) {\n chr = ((b1 << 6) & 0x7C0) | (b2 & 0x3F);\n\n if (chr < 0x80) {\n result += '\\ufffd\\ufffd';\n } else {\n result += String.fromCharCode(chr);\n }\n\n i += 3;\n continue;\n }\n }\n\n if ((b1 & 0xF0) === 0xE0 && (i + 6 < l)) {\n // 1110xxxx 10xxxxxx 10xxxxxx\n b2 = parseInt(seq.slice(i + 4, i + 6), 16);\n b3 = parseInt(seq.slice(i + 7, i + 9), 16);\n\n if ((b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80) {\n chr = ((b1 << 12) & 0xF000) | ((b2 << 6) & 0xFC0) | (b3 & 0x3F);\n\n if (chr < 0x800 || (chr >= 0xD800 && chr <= 0xDFFF)) {\n result += '\\ufffd\\ufffd\\ufffd';\n } else {\n result += String.fromCharCode(chr);\n }\n\n i += 6;\n continue;\n }\n }\n\n if ((b1 & 0xF8) === 0xF0 && (i + 9 < l)) {\n // 111110xx 10xxxxxx 10xxxxxx 10xxxxxx\n b2 = parseInt(seq.slice(i + 4, i + 6), 16);\n b3 = parseInt(seq.slice(i + 7, i + 9), 16);\n b4 = parseInt(seq.slice(i + 10, i + 12), 16);\n\n if ((b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80 && (b4 & 0xC0) === 0x80) {\n chr = ((b1 << 18) & 0x1C0000) | ((b2 << 12) & 0x3F000) | ((b3 << 6) & 0xFC0) | (b4 & 0x3F);\n\n if (chr < 0x10000 || chr > 0x10FFFF) {\n result += '\\ufffd\\ufffd\\ufffd\\ufffd';\n } else {\n chr -= 0x10000;\n result += String.fromCharCode(0xD800 + (chr >> 10), 0xDC00 + (chr & 0x3FF));\n }\n\n i += 9;\n continue;\n }\n }\n\n result += '\\ufffd';\n }\n\n return result;\n });\n}\n\n\ndecode.defaultChars = ';/?:@&=+$,#';\ndecode.componentChars = '';\n\n\nmodule.exports = decode;\n\n\n//# sourceURL=webpack:///./node_modules/mdurl/decode.js?"); - -/***/ }), - -/***/ "./node_modules/mdurl/encode.js": -/*!**************************************!*\ - !*** ./node_modules/mdurl/encode.js ***! - \**************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\n\n\nvar encodeCache = {};\n\n\n// Create a lookup array where anything but characters in `chars` string\n// and alphanumeric chars is percent-encoded.\n//\nfunction getEncodeCache(exclude) {\n var i, ch, cache = encodeCache[exclude];\n if (cache) { return cache; }\n\n cache = encodeCache[exclude] = [];\n\n for (i = 0; i < 128; i++) {\n ch = String.fromCharCode(i);\n\n if (/^[0-9a-z]$/i.test(ch)) {\n // always allow unencoded alphanumeric characters\n cache.push(ch);\n } else {\n cache.push('%' + ('0' + i.toString(16).toUpperCase()).slice(-2));\n }\n }\n\n for (i = 0; i < exclude.length; i++) {\n cache[exclude.charCodeAt(i)] = exclude[i];\n }\n\n return cache;\n}\n\n\n// Encode unsafe characters with percent-encoding, skipping already\n// encoded sequences.\n//\n// - string - string to encode\n// - exclude - list of characters to ignore (in addition to a-zA-Z0-9)\n// - keepEscaped - don't encode '%' in a correct escape sequence (default: true)\n//\nfunction encode(string, exclude, keepEscaped) {\n var i, l, code, nextCode, cache,\n result = '';\n\n if (typeof exclude !== 'string') {\n // encode(string, keepEscaped)\n keepEscaped = exclude;\n exclude = encode.defaultChars;\n }\n\n if (typeof keepEscaped === 'undefined') {\n keepEscaped = true;\n }\n\n cache = getEncodeCache(exclude);\n\n for (i = 0, l = string.length; i < l; i++) {\n code = string.charCodeAt(i);\n\n if (keepEscaped && code === 0x25 /* % */ && i + 2 < l) {\n if (/^[0-9a-f]{2}$/i.test(string.slice(i + 1, i + 3))) {\n result += string.slice(i, i + 3);\n i += 2;\n continue;\n }\n }\n\n if (code < 128) {\n result += cache[code];\n continue;\n }\n\n if (code >= 0xD800 && code <= 0xDFFF) {\n if (code >= 0xD800 && code <= 0xDBFF && i + 1 < l) {\n nextCode = string.charCodeAt(i + 1);\n if (nextCode >= 0xDC00 && nextCode <= 0xDFFF) {\n result += encodeURIComponent(string[i] + string[i + 1]);\n i++;\n continue;\n }\n }\n result += '%EF%BF%BD';\n continue;\n }\n\n result += encodeURIComponent(string[i]);\n }\n\n return result;\n}\n\nencode.defaultChars = \";/?:@&=+$,-_.!~*'()#\";\nencode.componentChars = \"-_.!~*'()\";\n\n\nmodule.exports = encode;\n\n\n//# sourceURL=webpack:///./node_modules/mdurl/encode.js?"); - -/***/ }), - -/***/ "./node_modules/mdurl/format.js": -/*!**************************************!*\ - !*** ./node_modules/mdurl/format.js ***! - \**************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\n\n\nmodule.exports = function format(url) {\n var result = '';\n\n result += url.protocol || '';\n result += url.slashes ? '//' : '';\n result += url.auth ? url.auth + '@' : '';\n\n if (url.hostname && url.hostname.indexOf(':') !== -1) {\n // ipv6 address\n result += '[' + url.hostname + ']';\n } else {\n result += url.hostname || '';\n }\n\n result += url.port ? ':' + url.port : '';\n result += url.pathname || '';\n result += url.search || '';\n result += url.hash || '';\n\n return result;\n};\n\n\n//# sourceURL=webpack:///./node_modules/mdurl/format.js?"); - -/***/ }), - -/***/ "./node_modules/mdurl/index.js": -/*!*************************************!*\ - !*** ./node_modules/mdurl/index.js ***! - \*************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\n\nmodule.exports.encode = __webpack_require__(/*! ./encode */ \"./node_modules/mdurl/encode.js\");\nmodule.exports.decode = __webpack_require__(/*! ./decode */ \"./node_modules/mdurl/decode.js\");\nmodule.exports.format = __webpack_require__(/*! ./format */ \"./node_modules/mdurl/format.js\");\nmodule.exports.parse = __webpack_require__(/*! ./parse */ \"./node_modules/mdurl/parse.js\");\n\n\n//# sourceURL=webpack:///./node_modules/mdurl/index.js?"); - -/***/ }), - -/***/ "./node_modules/mdurl/parse.js": -/*!*************************************!*\ - !*** ./node_modules/mdurl/parse.js ***! - \*************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n\n//\n// Changes from joyent/node:\n//\n// 1. No leading slash in paths,\n// e.g. in `url.parse('http://foo?bar')` pathname is ``, not `/`\n//\n// 2. Backslashes are not replaced with slashes,\n// so `http:\\\\example.org\\` is treated like a relative path\n//\n// 3. Trailing colon is treated like a part of the path,\n// i.e. in `http://example.org:foo` pathname is `:foo`\n//\n// 4. Nothing is URL-encoded in the resulting object,\n// (in joyent/node some chars in auth and paths are encoded)\n//\n// 5. `url.parse()` does not have `parseQueryString` argument\n//\n// 6. Removed extraneous result properties: `host`, `path`, `query`, etc.,\n// which can be constructed using other parts of the url.\n//\n\n\nfunction Url() {\n this.protocol = null;\n this.slashes = null;\n this.auth = null;\n this.port = null;\n this.hostname = null;\n this.hash = null;\n this.search = null;\n this.pathname = null;\n}\n\n// Reference: RFC 3986, RFC 1808, RFC 2396\n\n// define these here so at least they only have to be\n// compiled once on the first module load.\nvar protocolPattern = /^([a-z0-9.+-]+:)/i,\n portPattern = /:[0-9]*$/,\n\n // Special case for a simple path URL\n simplePathPattern = /^(\\/\\/?(?!\\/)[^\\?\\s]*)(\\?[^\\s]*)?$/,\n\n // RFC 2396: characters reserved for delimiting URLs.\n // We actually just auto-escape these.\n delims = [ '<', '>', '\"', '`', ' ', '\\r', '\\n', '\\t' ],\n\n // RFC 2396: characters not allowed for various reasons.\n unwise = [ '{', '}', '|', '\\\\', '^', '`' ].concat(delims),\n\n // Allowed by RFCs, but cause of XSS attacks. Always escape these.\n autoEscape = [ '\\'' ].concat(unwise),\n // Characters that are never ever allowed in a hostname.\n // Note that any invalid chars are also handled, but these\n // are the ones that are *expected* to be seen, so we fast-path\n // them.\n nonHostChars = [ '%', '/', '?', ';', '#' ].concat(autoEscape),\n hostEndingChars = [ '/', '?', '#' ],\n hostnameMaxLen = 255,\n hostnamePartPattern = /^[+a-z0-9A-Z_-]{0,63}$/,\n hostnamePartStart = /^([+a-z0-9A-Z_-]{0,63})(.*)$/,\n // protocols that can allow \"unsafe\" and \"unwise\" chars.\n /* eslint-disable no-script-url */\n // protocols that never have a hostname.\n hostlessProtocol = {\n 'javascript': true,\n 'javascript:': true\n },\n // protocols that always contain a // bit.\n slashedProtocol = {\n 'http': true,\n 'https': true,\n 'ftp': true,\n 'gopher': true,\n 'file': true,\n 'http:': true,\n 'https:': true,\n 'ftp:': true,\n 'gopher:': true,\n 'file:': true\n };\n /* eslint-enable no-script-url */\n\nfunction urlParse(url, slashesDenoteHost) {\n if (url && url instanceof Url) { return url; }\n\n var u = new Url();\n u.parse(url, slashesDenoteHost);\n return u;\n}\n\nUrl.prototype.parse = function(url, slashesDenoteHost) {\n var i, l, lowerProto, hec, slashes,\n rest = url;\n\n // trim before proceeding.\n // This is to support parse stuff like \" http://foo.com \\n\"\n rest = rest.trim();\n\n if (!slashesDenoteHost && url.split('#').length === 1) {\n // Try fast path regexp\n var simplePath = simplePathPattern.exec(rest);\n if (simplePath) {\n this.pathname = simplePath[1];\n if (simplePath[2]) {\n this.search = simplePath[2];\n }\n return this;\n }\n }\n\n var proto = protocolPattern.exec(rest);\n if (proto) {\n proto = proto[0];\n lowerProto = proto.toLowerCase();\n this.protocol = proto;\n rest = rest.substr(proto.length);\n }\n\n // figure out if it's got a host\n // user@server is *always* interpreted as a hostname, and url\n // resolution will treat //foo/bar as host=foo,path=bar because that's\n // how the browser resolves relative URLs.\n if (slashesDenoteHost || proto || rest.match(/^\\/\\/[^@\\/]+@[^@\\/]+/)) {\n slashes = rest.substr(0, 2) === '//';\n if (slashes && !(proto && hostlessProtocol[proto])) {\n rest = rest.substr(2);\n this.slashes = true;\n }\n }\n\n if (!hostlessProtocol[proto] &&\n (slashes || (proto && !slashedProtocol[proto]))) {\n\n // there's a hostname.\n // the first instance of /, ?, ;, or # ends the host.\n //\n // If there is an @ in the hostname, then non-host chars *are* allowed\n // to the left of the last @ sign, unless some host-ending character\n // comes *before* the @-sign.\n // URLs are obnoxious.\n //\n // ex:\n // http://a@b@c/ => user:a@b host:c\n // http://a@b?@c => user:a host:c path:/?@c\n\n // v0.12 TODO(isaacs): This is not quite how Chrome does things.\n // Review our test case against browsers more comprehensively.\n\n // find the first instance of any hostEndingChars\n var hostEnd = -1;\n for (i = 0; i < hostEndingChars.length; i++) {\n hec = rest.indexOf(hostEndingChars[i]);\n if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) {\n hostEnd = hec;\n }\n }\n\n // at this point, either we have an explicit point where the\n // auth portion cannot go past, or the last @ char is the decider.\n var auth, atSign;\n if (hostEnd === -1) {\n // atSign can be anywhere.\n atSign = rest.lastIndexOf('@');\n } else {\n // atSign must be in auth portion.\n // http://a@b/c@d => host:b auth:a path:/c@d\n atSign = rest.lastIndexOf('@', hostEnd);\n }\n\n // Now we have a portion which is definitely the auth.\n // Pull that off.\n if (atSign !== -1) {\n auth = rest.slice(0, atSign);\n rest = rest.slice(atSign + 1);\n this.auth = auth;\n }\n\n // the host is the remaining to the left of the first non-host char\n hostEnd = -1;\n for (i = 0; i < nonHostChars.length; i++) {\n hec = rest.indexOf(nonHostChars[i]);\n if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) {\n hostEnd = hec;\n }\n }\n // if we still have not hit it, then the entire thing is a host.\n if (hostEnd === -1) {\n hostEnd = rest.length;\n }\n\n if (rest[hostEnd - 1] === ':') { hostEnd--; }\n var host = rest.slice(0, hostEnd);\n rest = rest.slice(hostEnd);\n\n // pull out port.\n this.parseHost(host);\n\n // we've indicated that there is a hostname,\n // so even if it's empty, it has to be present.\n this.hostname = this.hostname || '';\n\n // if hostname begins with [ and ends with ]\n // assume that it's an IPv6 address.\n var ipv6Hostname = this.hostname[0] === '[' &&\n this.hostname[this.hostname.length - 1] === ']';\n\n // validate a little.\n if (!ipv6Hostname) {\n var hostparts = this.hostname.split(/\\./);\n for (i = 0, l = hostparts.length; i < l; i++) {\n var part = hostparts[i];\n if (!part) { continue; }\n if (!part.match(hostnamePartPattern)) {\n var newpart = '';\n for (var j = 0, k = part.length; j < k; j++) {\n if (part.charCodeAt(j) > 127) {\n // we replace non-ASCII char with a temporary placeholder\n // we need this to make sure size of hostname is not\n // broken by replacing non-ASCII by nothing\n newpart += 'x';\n } else {\n newpart += part[j];\n }\n }\n // we test again with ASCII char only\n if (!newpart.match(hostnamePartPattern)) {\n var validParts = hostparts.slice(0, i);\n var notHost = hostparts.slice(i + 1);\n var bit = part.match(hostnamePartStart);\n if (bit) {\n validParts.push(bit[1]);\n notHost.unshift(bit[2]);\n }\n if (notHost.length) {\n rest = notHost.join('.') + rest;\n }\n this.hostname = validParts.join('.');\n break;\n }\n }\n }\n }\n\n if (this.hostname.length > hostnameMaxLen) {\n this.hostname = '';\n }\n\n // strip [ and ] from the hostname\n // the host field still retains them, though\n if (ipv6Hostname) {\n this.hostname = this.hostname.substr(1, this.hostname.length - 2);\n }\n }\n\n // chop off from the tail first.\n var hash = rest.indexOf('#');\n if (hash !== -1) {\n // got a fragment string.\n this.hash = rest.substr(hash);\n rest = rest.slice(0, hash);\n }\n var qm = rest.indexOf('?');\n if (qm !== -1) {\n this.search = rest.substr(qm);\n rest = rest.slice(0, qm);\n }\n if (rest) { this.pathname = rest; }\n if (slashedProtocol[lowerProto] &&\n this.hostname && !this.pathname) {\n this.pathname = '';\n }\n\n return this;\n};\n\nUrl.prototype.parseHost = function(host) {\n var port = portPattern.exec(host);\n if (port) {\n port = port[0];\n if (port !== ':') {\n this.port = port.substr(1);\n }\n host = host.substr(0, host.length - port.length);\n }\n if (host) { this.hostname = host; }\n};\n\nmodule.exports = urlParse;\n\n\n//# sourceURL=webpack:///./node_modules/mdurl/parse.js?"); - -/***/ }), - -/***/ "./node_modules/uc.micro/categories/Cc/regex.js": -/*!******************************************************!*\ - !*** ./node_modules/uc.micro/categories/Cc/regex.js ***! - \******************************************************/ -/*! no static exports found */ -/***/ (function(module, exports) { - -eval("module.exports=/[\\0-\\x1F\\x7F-\\x9F]/\n\n//# sourceURL=webpack:///./node_modules/uc.micro/categories/Cc/regex.js?"); - -/***/ }), - -/***/ "./node_modules/uc.micro/categories/Cf/regex.js": -/*!******************************************************!*\ - !*** ./node_modules/uc.micro/categories/Cf/regex.js ***! - \******************************************************/ -/*! no static exports found */ -/***/ (function(module, exports) { - -eval("module.exports=/[\\xAD\\u0600-\\u0605\\u061C\\u06DD\\u070F\\u08E2\\u180E\\u200B-\\u200F\\u202A-\\u202E\\u2060-\\u2064\\u2066-\\u206F\\uFEFF\\uFFF9-\\uFFFB]|\\uD804\\uDCBD|\\uD82F[\\uDCA0-\\uDCA3]|\\uD834[\\uDD73-\\uDD7A]|\\uDB40[\\uDC01\\uDC20-\\uDC7F]/\n\n//# sourceURL=webpack:///./node_modules/uc.micro/categories/Cf/regex.js?"); - -/***/ }), - -/***/ "./node_modules/uc.micro/categories/P/regex.js": -/*!*****************************************************!*\ - !*** ./node_modules/uc.micro/categories/P/regex.js ***! - \*****************************************************/ -/*! no static exports found */ -/***/ (function(module, exports) { - -eval("module.exports=/[!-#%-\\*,-/:;\\?@\\[-\\]_\\{\\}\\xA1\\xA7\\xAB\\xB6\\xB7\\xBB\\xBF\\u037E\\u0387\\u055A-\\u055F\\u0589\\u058A\\u05BE\\u05C0\\u05C3\\u05C6\\u05F3\\u05F4\\u0609\\u060A\\u060C\\u060D\\u061B\\u061E\\u061F\\u066A-\\u066D\\u06D4\\u0700-\\u070D\\u07F7-\\u07F9\\u0830-\\u083E\\u085E\\u0964\\u0965\\u0970\\u09FD\\u0AF0\\u0DF4\\u0E4F\\u0E5A\\u0E5B\\u0F04-\\u0F12\\u0F14\\u0F3A-\\u0F3D\\u0F85\\u0FD0-\\u0FD4\\u0FD9\\u0FDA\\u104A-\\u104F\\u10FB\\u1360-\\u1368\\u1400\\u166D\\u166E\\u169B\\u169C\\u16EB-\\u16ED\\u1735\\u1736\\u17D4-\\u17D6\\u17D8-\\u17DA\\u1800-\\u180A\\u1944\\u1945\\u1A1E\\u1A1F\\u1AA0-\\u1AA6\\u1AA8-\\u1AAD\\u1B5A-\\u1B60\\u1BFC-\\u1BFF\\u1C3B-\\u1C3F\\u1C7E\\u1C7F\\u1CC0-\\u1CC7\\u1CD3\\u2010-\\u2027\\u2030-\\u2043\\u2045-\\u2051\\u2053-\\u205E\\u207D\\u207E\\u208D\\u208E\\u2308-\\u230B\\u2329\\u232A\\u2768-\\u2775\\u27C5\\u27C6\\u27E6-\\u27EF\\u2983-\\u2998\\u29D8-\\u29DB\\u29FC\\u29FD\\u2CF9-\\u2CFC\\u2CFE\\u2CFF\\u2D70\\u2E00-\\u2E2E\\u2E30-\\u2E49\\u3001-\\u3003\\u3008-\\u3011\\u3014-\\u301F\\u3030\\u303D\\u30A0\\u30FB\\uA4FE\\uA4FF\\uA60D-\\uA60F\\uA673\\uA67E\\uA6F2-\\uA6F7\\uA874-\\uA877\\uA8CE\\uA8CF\\uA8F8-\\uA8FA\\uA8FC\\uA92E\\uA92F\\uA95F\\uA9C1-\\uA9CD\\uA9DE\\uA9DF\\uAA5C-\\uAA5F\\uAADE\\uAADF\\uAAF0\\uAAF1\\uABEB\\uFD3E\\uFD3F\\uFE10-\\uFE19\\uFE30-\\uFE52\\uFE54-\\uFE61\\uFE63\\uFE68\\uFE6A\\uFE6B\\uFF01-\\uFF03\\uFF05-\\uFF0A\\uFF0C-\\uFF0F\\uFF1A\\uFF1B\\uFF1F\\uFF20\\uFF3B-\\uFF3D\\uFF3F\\uFF5B\\uFF5D\\uFF5F-\\uFF65]|\\uD800[\\uDD00-\\uDD02\\uDF9F\\uDFD0]|\\uD801\\uDD6F|\\uD802[\\uDC57\\uDD1F\\uDD3F\\uDE50-\\uDE58\\uDE7F\\uDEF0-\\uDEF6\\uDF39-\\uDF3F\\uDF99-\\uDF9C]|\\uD804[\\uDC47-\\uDC4D\\uDCBB\\uDCBC\\uDCBE-\\uDCC1\\uDD40-\\uDD43\\uDD74\\uDD75\\uDDC5-\\uDDC9\\uDDCD\\uDDDB\\uDDDD-\\uDDDF\\uDE38-\\uDE3D\\uDEA9]|\\uD805[\\uDC4B-\\uDC4F\\uDC5B\\uDC5D\\uDCC6\\uDDC1-\\uDDD7\\uDE41-\\uDE43\\uDE60-\\uDE6C\\uDF3C-\\uDF3E]|\\uD806[\\uDE3F-\\uDE46\\uDE9A-\\uDE9C\\uDE9E-\\uDEA2]|\\uD807[\\uDC41-\\uDC45\\uDC70\\uDC71]|\\uD809[\\uDC70-\\uDC74]|\\uD81A[\\uDE6E\\uDE6F\\uDEF5\\uDF37-\\uDF3B\\uDF44]|\\uD82F\\uDC9F|\\uD836[\\uDE87-\\uDE8B]|\\uD83A[\\uDD5E\\uDD5F]/\n\n//# sourceURL=webpack:///./node_modules/uc.micro/categories/P/regex.js?"); - -/***/ }), - -/***/ "./node_modules/uc.micro/categories/Z/regex.js": -/*!*****************************************************!*\ - !*** ./node_modules/uc.micro/categories/Z/regex.js ***! - \*****************************************************/ -/*! no static exports found */ -/***/ (function(module, exports) { - -eval("module.exports=/[ \\xA0\\u1680\\u2000-\\u200A\\u202F\\u205F\\u3000]/\n\n//# sourceURL=webpack:///./node_modules/uc.micro/categories/Z/regex.js?"); - -/***/ }), - -/***/ "./node_modules/uc.micro/index.js": -/*!****************************************!*\ - !*** ./node_modules/uc.micro/index.js ***! - \****************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nexports.Any = __webpack_require__(/*! ./properties/Any/regex */ \"./node_modules/uc.micro/properties/Any/regex.js\");\nexports.Cc = __webpack_require__(/*! ./categories/Cc/regex */ \"./node_modules/uc.micro/categories/Cc/regex.js\");\nexports.Cf = __webpack_require__(/*! ./categories/Cf/regex */ \"./node_modules/uc.micro/categories/Cf/regex.js\");\nexports.P = __webpack_require__(/*! ./categories/P/regex */ \"./node_modules/uc.micro/categories/P/regex.js\");\nexports.Z = __webpack_require__(/*! ./categories/Z/regex */ \"./node_modules/uc.micro/categories/Z/regex.js\");\n\n\n//# sourceURL=webpack:///./node_modules/uc.micro/index.js?"); - -/***/ }), - -/***/ "./node_modules/uc.micro/properties/Any/regex.js": -/*!*******************************************************!*\ - !*** ./node_modules/uc.micro/properties/Any/regex.js ***! - \*******************************************************/ -/*! no static exports found */ -/***/ (function(module, exports) { - -eval("module.exports=/[\\0-\\uD7FF\\uE000-\\uFFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]|[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?:[^\\uD800-\\uDBFF]|^)[\\uDC00-\\uDFFF]/\n\n//# sourceURL=webpack:///./node_modules/uc.micro/properties/Any/regex.js?"); - -/***/ }), - -/***/ "./notebook/index.ts": -/*!***************************!*\ - !*** ./notebook/index.ts ***! - \***************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst MarkdownIt = __webpack_require__(/*! markdown-it */ \"./node_modules/markdown-it/index.js\");\n(function () {\n const markdownIt = new MarkdownIt();\n globalThis.extendMarkdownIt = ((f) => {\n f(markdownIt);\n });\n const notebook = acquireNotebookRendererApi('notebookCoreTestRenderer');\n notebook.onDidCreateMarkdown(({ element, content }) => {\n console.log('did create markdown cell');\n const rendered = markdownIt.render(content);\n element.innerHTML = rendered;\n });\n console.log('markdown-it');\n}());\n\n\n//# sourceURL=webpack:///./notebook/index.ts?"); - -/***/ }) - -/******/ }); \ No newline at end of file +!function(e){var t={};function r(n){if(t[n])return t[n].exports;var s=t[n]={i:n,l:!1,exports:{}};return e[n].call(s.exports,s,s.exports,r),s.l=!0,s.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var s in e)r.d(n,s,function(t){return e[t]}.bind(null,s));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=9)}([function(e,t,r){"use strict";var n=Object.prototype.hasOwnProperty;function s(e,t){return n.call(e,t)}function o(e){return!(e>=55296&&e<=57343)&&(!(e>=64976&&e<=65007)&&(65535!=(65535&e)&&65534!=(65535&e)&&(!(e>=0&&e<=8)&&(11!==e&&(!(e>=14&&e<=31)&&(!(e>=127&&e<=159)&&!(e>1114111)))))))}function i(e){if(e>65535){var t=55296+((e-=65536)>>10),r=56320+(1023&e);return String.fromCharCode(t,r)}return String.fromCharCode(e)}var a=/\\([!"#$%&'()*+,\-.\/:;<=>?@[\\\]^_`{|}~])/g,u=new RegExp(a.source+"|"+/&([a-z#][a-z0-9]{1,31});/gi.source,"gi"),c=/^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))/i,l=r(3);var p=/[&<>"]/,h=/[&<>"]/g,f={"&":"&","<":"<",">":">",'"':"""};function d(e){return f[e]}var m=/[.?*+^$[\]\\(){}|-]/g;var g=r(4);t.lib={},t.lib.mdurl=r(5),t.lib.ucmicro=r(17),t.assign=function(e){var t=Array.prototype.slice.call(arguments,1);return t.forEach((function(t){if(t){if("object"!=typeof t)throw new TypeError(t+"must be object");Object.keys(t).forEach((function(r){e[r]=t[r]}))}})),e},t.isString=function(e){return"[object String]"===function(e){return Object.prototype.toString.call(e)}(e)},t.has=s,t.unescapeMd=function(e){return e.indexOf("\\")<0?e:e.replace(a,"$1")},t.unescapeAll=function(e){return e.indexOf("\\")<0&&e.indexOf("&")<0?e:e.replace(u,(function(e,t,r){return t||function(e,t){var r=0;return s(l,t)?l[t]:35===t.charCodeAt(0)&&c.test(t)&&o(r="x"===t[1].toLowerCase()?parseInt(t.slice(2),16):parseInt(t.slice(1),10))?i(r):e}(e,r)}))},t.isValidEntityCode=o,t.fromCodePoint=i,t.escapeHtml=function(e){return p.test(e)?e.replace(h,d):e},t.arrayReplaceAt=function(e,t,r){return[].concat(e.slice(0,t),r,e.slice(t+1))},t.isSpace=function(e){switch(e){case 9:case 32:return!0}return!1},t.isWhiteSpace=function(e){if(e>=8192&&e<=8202)return!0;switch(e){case 9:case 10:case 11:case 12:case 13:case 32:case 160:case 5760:case 8239:case 8287:case 12288:return!0}return!1},t.isMdAsciiPunct=function(e){switch(e){case 33:case 34:case 35:case 36:case 37:case 38:case 39:case 40:case 41:case 42:case 43:case 44:case 45:case 46:case 47:case 58:case 59:case 60:case 61:case 62:case 63:case 64:case 91:case 92:case 93:case 94:case 95:case 96:case 123:case 124:case 125:case 126:return!0;default:return!1}},t.isPunctChar=function(e){return g.test(e)},t.escapeRE=function(e){return e.replace(m,"\\$&")},t.normalizeReference=function(e){return e=e.trim().replace(/\s+/g," "),"Ṿ"==="ẞ".toLowerCase()&&(e=e.replace(/ẞ/g,"ß")),e.toLowerCase().toUpperCase()}},function(e,t,r){"use strict";function n(){this.__rules__=[],this.__cache__=null}n.prototype.__find__=function(e){for(var t=0;t=0&&(r=this.attrs[t][1]),r},n.prototype.attrJoin=function(e,t){var r=this.attrIndex(e);r<0?this.attrPush([e,t]):this.attrs[r][1]=this.attrs[r][1]+" "+t},e.exports=n},function(e,t,r){"use strict";e.exports=r(12)},function(e,t){e.exports=/[!-#%-\*,-/:;\?@\[-\]_\{\}\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u09FD\u0AF0\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E49\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC9\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD805[\uDC4B-\uDC4F\uDC5B\uDC5D\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDE60-\uDE6C\uDF3C-\uDF3E]|\uD806[\uDE3F-\uDE46\uDE9A-\uDE9C\uDE9E-\uDEA2]|\uD807[\uDC41-\uDC45\uDC70\uDC71]|\uD809[\uDC70-\uDC74]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]|\uD83A[\uDD5E\uDD5F]/},function(e,t,r){"use strict";e.exports.encode=r(13),e.exports.decode=r(14),e.exports.format=r(15),e.exports.parse=r(16)},function(e,t,r){"use strict";var n="<[A-Za-z][A-Za-z0-9\\-]*(?:\\s+[a-zA-Z_:][a-zA-Z0-9:._-]*(?:\\s*=\\s*(?:[^\"'=<>`\\x00-\\x20]+|'[^']*'|\"[^\"]*\"))?)*\\s*\\/?>",s="<\\/[A-Za-z][A-Za-z0-9\\-]*\\s*>",o=new RegExp("^(?:"+n+"|"+s+"|\x3c!----\x3e|\x3c!--(?:-?[^>-])(?:-?[^-])*--\x3e|<[?][\\s\\S]*?[?]>|]*>|)"),i=new RegExp("^(?:"+n+"|"+s+")");e.exports.HTML_TAG_RE=o,e.exports.HTML_OPEN_CLOSE_TAG_RE=i},function(e,t,r){"use strict";function n(e,t){var r,n,s,o,i,a=[],u=t.length;for(r=0;r=0;r--)95!==(n=t[r]).marker&&42!==n.marker||-1!==n.end&&(s=t[n.end],a=r>0&&t[r-1].end===n.end+1&&t[r-1].token===n.token-1&&t[n.end+1].token===s.token+1&&t[r-1].marker===n.marker,i=String.fromCharCode(n.marker),(o=e.tokens[n.token]).type=a?"strong_open":"em_open",o.tag=a?"strong":"em",o.nesting=1,o.markup=a?i+i:i,o.content="",(o=e.tokens[s.token]).type=a?"strong_close":"em_close",o.tag=a?"strong":"em",o.nesting=-1,o.markup=a?i+i:i,o.content="",a&&(e.tokens[t[r-1].token].content="",e.tokens[t[n.end+1].token].content="",r--))}e.exports.tokenize=function(e,t){var r,n,s=e.pos,o=e.src.charCodeAt(s);if(t)return!1;if(95!==o&&42!==o)return!1;for(n=e.scanDelims(e.pos,42===o),r=0;r{t(e)};acquireNotebookRendererApi("notebookCoreTestRenderer").onDidCreateMarkdown(({element:t,content:r})=>{const n=e.render(r);t.innerHTML=n})}()},function(e,t,r){"use strict";e.exports=r(11)},function(e,t,r){"use strict";var n=r(0),s=r(22),o=r(26),i=r(27),a=r(35),u=r(49),c=r(62),l=r(5),p=r(68),h={default:r(71),zero:r(72),commonmark:r(73)},f=/^(vbscript|javascript|file|data):/,d=/^data:image\/(gif|png|jpeg|webp);/;function m(e){var t=e.trim().toLowerCase();return!f.test(t)||!!d.test(t)}var g=["http:","https:","mailto:"];function _(e){var t=l.parse(e,!0);if(t.hostname&&(!t.protocol||g.indexOf(t.protocol)>=0))try{t.hostname=p.toASCII(t.hostname)}catch(e){}return l.encode(l.format(t))}function b(e){var t=l.parse(e,!0);if(t.hostname&&(!t.protocol||g.indexOf(t.protocol)>=0))try{t.hostname=p.toUnicode(t.hostname)}catch(e){}return l.decode(l.format(t),l.decode.defaultChars+"%")}function k(e,t){if(!(this instanceof k))return new k(e,t);t||n.isString(e)||(t=e||{},e="default"),this.inline=new u,this.block=new a,this.core=new i,this.renderer=new o,this.linkify=new c,this.validateLink=m,this.normalizeLink=_,this.normalizeLinkText=b,this.utils=n,this.helpers=n.assign({},s),this.options={},this.configure(e),t&&this.set(t)}k.prototype.set=function(e){return n.assign(this.options,e),this},k.prototype.configure=function(e){var t,r=this;if(n.isString(e)&&!(e=h[t=e]))throw new Error('Wrong `markdown-it` preset "'+t+'", check name');if(!e)throw new Error("Wrong `markdown-it` preset, can't be empty");return e.options&&r.set(e.options),e.components&&Object.keys(e.components).forEach((function(t){e.components[t].rules&&r[t].ruler.enableOnly(e.components[t].rules),e.components[t].rules2&&r[t].ruler2.enableOnly(e.components[t].rules2)})),this},k.prototype.enable=function(e,t){var r=[];Array.isArray(e)||(e=[e]),["core","block","inline"].forEach((function(t){r=r.concat(this[t].ruler.enable(e,!0))}),this),r=r.concat(this.inline.ruler2.enable(e,!0));var n=e.filter((function(e){return r.indexOf(e)<0}));if(n.length&&!t)throw new Error("MarkdownIt. Failed to enable unknown rule(s): "+n);return this},k.prototype.disable=function(e,t){var r=[];Array.isArray(e)||(e=[e]),["core","block","inline"].forEach((function(t){r=r.concat(this[t].ruler.disable(e,!0))}),this),r=r.concat(this.inline.ruler2.disable(e,!0));var n=e.filter((function(e){return r.indexOf(e)<0}));if(n.length&&!t)throw new Error("MarkdownIt. Failed to disable unknown rule(s): "+n);return this},k.prototype.use=function(e){var t=[this].concat(Array.prototype.slice.call(arguments,1));return e.apply(e,t),this},k.prototype.parse=function(e,t){if("string"!=typeof e)throw new Error("Input data should be a String");var r=new this.core.State(e,this,t);return this.core.process(r),r.tokens},k.prototype.render=function(e,t){return t=t||{},this.renderer.render(this.parse(e,t),this.options,t)},k.prototype.parseInline=function(e,t){var r=new this.core.State(e,this,t);return r.inlineMode=!0,this.core.process(r),r.tokens},k.prototype.renderInline=function(e,t){return t=t||{},this.renderer.render(this.parseInline(e,t),this.options,t)},e.exports=k},function(e){e.exports=JSON.parse('{"Aacute":"Á","aacute":"á","Abreve":"Ă","abreve":"ă","ac":"∾","acd":"∿","acE":"∾̳","Acirc":"Â","acirc":"â","acute":"´","Acy":"А","acy":"а","AElig":"Æ","aelig":"æ","af":"⁡","Afr":"𝔄","afr":"𝔞","Agrave":"À","agrave":"à","alefsym":"ℵ","aleph":"ℵ","Alpha":"Α","alpha":"α","Amacr":"Ā","amacr":"ā","amalg":"⨿","amp":"&","AMP":"&","andand":"⩕","And":"⩓","and":"∧","andd":"⩜","andslope":"⩘","andv":"⩚","ang":"∠","ange":"⦤","angle":"∠","angmsdaa":"⦨","angmsdab":"⦩","angmsdac":"⦪","angmsdad":"⦫","angmsdae":"⦬","angmsdaf":"⦭","angmsdag":"⦮","angmsdah":"⦯","angmsd":"∡","angrt":"∟","angrtvb":"⊾","angrtvbd":"⦝","angsph":"∢","angst":"Å","angzarr":"⍼","Aogon":"Ą","aogon":"ą","Aopf":"𝔸","aopf":"𝕒","apacir":"⩯","ap":"≈","apE":"⩰","ape":"≊","apid":"≋","apos":"\'","ApplyFunction":"⁡","approx":"≈","approxeq":"≊","Aring":"Å","aring":"å","Ascr":"𝒜","ascr":"𝒶","Assign":"≔","ast":"*","asymp":"≈","asympeq":"≍","Atilde":"Ã","atilde":"ã","Auml":"Ä","auml":"ä","awconint":"∳","awint":"⨑","backcong":"≌","backepsilon":"϶","backprime":"‵","backsim":"∽","backsimeq":"⋍","Backslash":"∖","Barv":"⫧","barvee":"⊽","barwed":"⌅","Barwed":"⌆","barwedge":"⌅","bbrk":"⎵","bbrktbrk":"⎶","bcong":"≌","Bcy":"Б","bcy":"б","bdquo":"„","becaus":"∵","because":"∵","Because":"∵","bemptyv":"⦰","bepsi":"϶","bernou":"ℬ","Bernoullis":"ℬ","Beta":"Β","beta":"β","beth":"ℶ","between":"≬","Bfr":"𝔅","bfr":"𝔟","bigcap":"⋂","bigcirc":"◯","bigcup":"⋃","bigodot":"⨀","bigoplus":"⨁","bigotimes":"⨂","bigsqcup":"⨆","bigstar":"★","bigtriangledown":"▽","bigtriangleup":"△","biguplus":"⨄","bigvee":"⋁","bigwedge":"⋀","bkarow":"⤍","blacklozenge":"⧫","blacksquare":"▪","blacktriangle":"▴","blacktriangledown":"▾","blacktriangleleft":"◂","blacktriangleright":"▸","blank":"␣","blk12":"▒","blk14":"░","blk34":"▓","block":"█","bne":"=⃥","bnequiv":"≡⃥","bNot":"⫭","bnot":"⌐","Bopf":"𝔹","bopf":"𝕓","bot":"⊥","bottom":"⊥","bowtie":"⋈","boxbox":"⧉","boxdl":"┐","boxdL":"╕","boxDl":"╖","boxDL":"╗","boxdr":"┌","boxdR":"╒","boxDr":"╓","boxDR":"╔","boxh":"─","boxH":"═","boxhd":"┬","boxHd":"╤","boxhD":"╥","boxHD":"╦","boxhu":"┴","boxHu":"╧","boxhU":"╨","boxHU":"╩","boxminus":"⊟","boxplus":"⊞","boxtimes":"⊠","boxul":"┘","boxuL":"╛","boxUl":"╜","boxUL":"╝","boxur":"└","boxuR":"╘","boxUr":"╙","boxUR":"╚","boxv":"│","boxV":"║","boxvh":"┼","boxvH":"╪","boxVh":"╫","boxVH":"╬","boxvl":"┤","boxvL":"╡","boxVl":"╢","boxVL":"╣","boxvr":"├","boxvR":"╞","boxVr":"╟","boxVR":"╠","bprime":"‵","breve":"˘","Breve":"˘","brvbar":"¦","bscr":"𝒷","Bscr":"ℬ","bsemi":"⁏","bsim":"∽","bsime":"⋍","bsolb":"⧅","bsol":"\\\\","bsolhsub":"⟈","bull":"•","bullet":"•","bump":"≎","bumpE":"⪮","bumpe":"≏","Bumpeq":"≎","bumpeq":"≏","Cacute":"Ć","cacute":"ć","capand":"⩄","capbrcup":"⩉","capcap":"⩋","cap":"∩","Cap":"⋒","capcup":"⩇","capdot":"⩀","CapitalDifferentialD":"ⅅ","caps":"∩︀","caret":"⁁","caron":"ˇ","Cayleys":"ℭ","ccaps":"⩍","Ccaron":"Č","ccaron":"č","Ccedil":"Ç","ccedil":"ç","Ccirc":"Ĉ","ccirc":"ĉ","Cconint":"∰","ccups":"⩌","ccupssm":"⩐","Cdot":"Ċ","cdot":"ċ","cedil":"¸","Cedilla":"¸","cemptyv":"⦲","cent":"¢","centerdot":"·","CenterDot":"·","cfr":"𝔠","Cfr":"ℭ","CHcy":"Ч","chcy":"ч","check":"✓","checkmark":"✓","Chi":"Χ","chi":"χ","circ":"ˆ","circeq":"≗","circlearrowleft":"↺","circlearrowright":"↻","circledast":"⊛","circledcirc":"⊚","circleddash":"⊝","CircleDot":"⊙","circledR":"®","circledS":"Ⓢ","CircleMinus":"⊖","CirclePlus":"⊕","CircleTimes":"⊗","cir":"○","cirE":"⧃","cire":"≗","cirfnint":"⨐","cirmid":"⫯","cirscir":"⧂","ClockwiseContourIntegral":"∲","CloseCurlyDoubleQuote":"”","CloseCurlyQuote":"’","clubs":"♣","clubsuit":"♣","colon":":","Colon":"∷","Colone":"⩴","colone":"≔","coloneq":"≔","comma":",","commat":"@","comp":"∁","compfn":"∘","complement":"∁","complexes":"ℂ","cong":"≅","congdot":"⩭","Congruent":"≡","conint":"∮","Conint":"∯","ContourIntegral":"∮","copf":"𝕔","Copf":"ℂ","coprod":"∐","Coproduct":"∐","copy":"©","COPY":"©","copysr":"℗","CounterClockwiseContourIntegral":"∳","crarr":"↵","cross":"✗","Cross":"⨯","Cscr":"𝒞","cscr":"𝒸","csub":"⫏","csube":"⫑","csup":"⫐","csupe":"⫒","ctdot":"⋯","cudarrl":"⤸","cudarrr":"⤵","cuepr":"⋞","cuesc":"⋟","cularr":"↶","cularrp":"⤽","cupbrcap":"⩈","cupcap":"⩆","CupCap":"≍","cup":"∪","Cup":"⋓","cupcup":"⩊","cupdot":"⊍","cupor":"⩅","cups":"∪︀","curarr":"↷","curarrm":"⤼","curlyeqprec":"⋞","curlyeqsucc":"⋟","curlyvee":"⋎","curlywedge":"⋏","curren":"¤","curvearrowleft":"↶","curvearrowright":"↷","cuvee":"⋎","cuwed":"⋏","cwconint":"∲","cwint":"∱","cylcty":"⌭","dagger":"†","Dagger":"‡","daleth":"ℸ","darr":"↓","Darr":"↡","dArr":"⇓","dash":"‐","Dashv":"⫤","dashv":"⊣","dbkarow":"⤏","dblac":"˝","Dcaron":"Ď","dcaron":"ď","Dcy":"Д","dcy":"д","ddagger":"‡","ddarr":"⇊","DD":"ⅅ","dd":"ⅆ","DDotrahd":"⤑","ddotseq":"⩷","deg":"°","Del":"∇","Delta":"Δ","delta":"δ","demptyv":"⦱","dfisht":"⥿","Dfr":"𝔇","dfr":"𝔡","dHar":"⥥","dharl":"⇃","dharr":"⇂","DiacriticalAcute":"´","DiacriticalDot":"˙","DiacriticalDoubleAcute":"˝","DiacriticalGrave":"`","DiacriticalTilde":"˜","diam":"⋄","diamond":"⋄","Diamond":"⋄","diamondsuit":"♦","diams":"♦","die":"¨","DifferentialD":"ⅆ","digamma":"ϝ","disin":"⋲","div":"÷","divide":"÷","divideontimes":"⋇","divonx":"⋇","DJcy":"Ђ","djcy":"ђ","dlcorn":"⌞","dlcrop":"⌍","dollar":"$","Dopf":"𝔻","dopf":"𝕕","Dot":"¨","dot":"˙","DotDot":"⃜","doteq":"≐","doteqdot":"≑","DotEqual":"≐","dotminus":"∸","dotplus":"∔","dotsquare":"⊡","doublebarwedge":"⌆","DoubleContourIntegral":"∯","DoubleDot":"¨","DoubleDownArrow":"⇓","DoubleLeftArrow":"⇐","DoubleLeftRightArrow":"⇔","DoubleLeftTee":"⫤","DoubleLongLeftArrow":"⟸","DoubleLongLeftRightArrow":"⟺","DoubleLongRightArrow":"⟹","DoubleRightArrow":"⇒","DoubleRightTee":"⊨","DoubleUpArrow":"⇑","DoubleUpDownArrow":"⇕","DoubleVerticalBar":"∥","DownArrowBar":"⤓","downarrow":"↓","DownArrow":"↓","Downarrow":"⇓","DownArrowUpArrow":"⇵","DownBreve":"̑","downdownarrows":"⇊","downharpoonleft":"⇃","downharpoonright":"⇂","DownLeftRightVector":"⥐","DownLeftTeeVector":"⥞","DownLeftVectorBar":"⥖","DownLeftVector":"↽","DownRightTeeVector":"⥟","DownRightVectorBar":"⥗","DownRightVector":"⇁","DownTeeArrow":"↧","DownTee":"⊤","drbkarow":"⤐","drcorn":"⌟","drcrop":"⌌","Dscr":"𝒟","dscr":"𝒹","DScy":"Ѕ","dscy":"ѕ","dsol":"⧶","Dstrok":"Đ","dstrok":"đ","dtdot":"⋱","dtri":"▿","dtrif":"▾","duarr":"⇵","duhar":"⥯","dwangle":"⦦","DZcy":"Џ","dzcy":"џ","dzigrarr":"⟿","Eacute":"É","eacute":"é","easter":"⩮","Ecaron":"Ě","ecaron":"ě","Ecirc":"Ê","ecirc":"ê","ecir":"≖","ecolon":"≕","Ecy":"Э","ecy":"э","eDDot":"⩷","Edot":"Ė","edot":"ė","eDot":"≑","ee":"ⅇ","efDot":"≒","Efr":"𝔈","efr":"𝔢","eg":"⪚","Egrave":"È","egrave":"è","egs":"⪖","egsdot":"⪘","el":"⪙","Element":"∈","elinters":"⏧","ell":"ℓ","els":"⪕","elsdot":"⪗","Emacr":"Ē","emacr":"ē","empty":"∅","emptyset":"∅","EmptySmallSquare":"◻","emptyv":"∅","EmptyVerySmallSquare":"▫","emsp13":" ","emsp14":" ","emsp":" ","ENG":"Ŋ","eng":"ŋ","ensp":" ","Eogon":"Ę","eogon":"ę","Eopf":"𝔼","eopf":"𝕖","epar":"⋕","eparsl":"⧣","eplus":"⩱","epsi":"ε","Epsilon":"Ε","epsilon":"ε","epsiv":"ϵ","eqcirc":"≖","eqcolon":"≕","eqsim":"≂","eqslantgtr":"⪖","eqslantless":"⪕","Equal":"⩵","equals":"=","EqualTilde":"≂","equest":"≟","Equilibrium":"⇌","equiv":"≡","equivDD":"⩸","eqvparsl":"⧥","erarr":"⥱","erDot":"≓","escr":"ℯ","Escr":"ℰ","esdot":"≐","Esim":"⩳","esim":"≂","Eta":"Η","eta":"η","ETH":"Ð","eth":"ð","Euml":"Ë","euml":"ë","euro":"€","excl":"!","exist":"∃","Exists":"∃","expectation":"ℰ","exponentiale":"ⅇ","ExponentialE":"ⅇ","fallingdotseq":"≒","Fcy":"Ф","fcy":"ф","female":"♀","ffilig":"ffi","fflig":"ff","ffllig":"ffl","Ffr":"𝔉","ffr":"𝔣","filig":"fi","FilledSmallSquare":"◼","FilledVerySmallSquare":"▪","fjlig":"fj","flat":"♭","fllig":"fl","fltns":"▱","fnof":"ƒ","Fopf":"𝔽","fopf":"𝕗","forall":"∀","ForAll":"∀","fork":"⋔","forkv":"⫙","Fouriertrf":"ℱ","fpartint":"⨍","frac12":"½","frac13":"⅓","frac14":"¼","frac15":"⅕","frac16":"⅙","frac18":"⅛","frac23":"⅔","frac25":"⅖","frac34":"¾","frac35":"⅗","frac38":"⅜","frac45":"⅘","frac56":"⅚","frac58":"⅝","frac78":"⅞","frasl":"⁄","frown":"⌢","fscr":"𝒻","Fscr":"ℱ","gacute":"ǵ","Gamma":"Γ","gamma":"γ","Gammad":"Ϝ","gammad":"ϝ","gap":"⪆","Gbreve":"Ğ","gbreve":"ğ","Gcedil":"Ģ","Gcirc":"Ĝ","gcirc":"ĝ","Gcy":"Г","gcy":"г","Gdot":"Ġ","gdot":"ġ","ge":"≥","gE":"≧","gEl":"⪌","gel":"⋛","geq":"≥","geqq":"≧","geqslant":"⩾","gescc":"⪩","ges":"⩾","gesdot":"⪀","gesdoto":"⪂","gesdotol":"⪄","gesl":"⋛︀","gesles":"⪔","Gfr":"𝔊","gfr":"𝔤","gg":"≫","Gg":"⋙","ggg":"⋙","gimel":"ℷ","GJcy":"Ѓ","gjcy":"ѓ","gla":"⪥","gl":"≷","glE":"⪒","glj":"⪤","gnap":"⪊","gnapprox":"⪊","gne":"⪈","gnE":"≩","gneq":"⪈","gneqq":"≩","gnsim":"⋧","Gopf":"𝔾","gopf":"𝕘","grave":"`","GreaterEqual":"≥","GreaterEqualLess":"⋛","GreaterFullEqual":"≧","GreaterGreater":"⪢","GreaterLess":"≷","GreaterSlantEqual":"⩾","GreaterTilde":"≳","Gscr":"𝒢","gscr":"ℊ","gsim":"≳","gsime":"⪎","gsiml":"⪐","gtcc":"⪧","gtcir":"⩺","gt":">","GT":">","Gt":"≫","gtdot":"⋗","gtlPar":"⦕","gtquest":"⩼","gtrapprox":"⪆","gtrarr":"⥸","gtrdot":"⋗","gtreqless":"⋛","gtreqqless":"⪌","gtrless":"≷","gtrsim":"≳","gvertneqq":"≩︀","gvnE":"≩︀","Hacek":"ˇ","hairsp":" ","half":"½","hamilt":"ℋ","HARDcy":"Ъ","hardcy":"ъ","harrcir":"⥈","harr":"↔","hArr":"⇔","harrw":"↭","Hat":"^","hbar":"ℏ","Hcirc":"Ĥ","hcirc":"ĥ","hearts":"♥","heartsuit":"♥","hellip":"…","hercon":"⊹","hfr":"𝔥","Hfr":"ℌ","HilbertSpace":"ℋ","hksearow":"⤥","hkswarow":"⤦","hoarr":"⇿","homtht":"∻","hookleftarrow":"↩","hookrightarrow":"↪","hopf":"𝕙","Hopf":"ℍ","horbar":"―","HorizontalLine":"─","hscr":"𝒽","Hscr":"ℋ","hslash":"ℏ","Hstrok":"Ħ","hstrok":"ħ","HumpDownHump":"≎","HumpEqual":"≏","hybull":"⁃","hyphen":"‐","Iacute":"Í","iacute":"í","ic":"⁣","Icirc":"Î","icirc":"î","Icy":"И","icy":"и","Idot":"İ","IEcy":"Е","iecy":"е","iexcl":"¡","iff":"⇔","ifr":"𝔦","Ifr":"ℑ","Igrave":"Ì","igrave":"ì","ii":"ⅈ","iiiint":"⨌","iiint":"∭","iinfin":"⧜","iiota":"℩","IJlig":"IJ","ijlig":"ij","Imacr":"Ī","imacr":"ī","image":"ℑ","ImaginaryI":"ⅈ","imagline":"ℐ","imagpart":"ℑ","imath":"ı","Im":"ℑ","imof":"⊷","imped":"Ƶ","Implies":"⇒","incare":"℅","in":"∈","infin":"∞","infintie":"⧝","inodot":"ı","intcal":"⊺","int":"∫","Int":"∬","integers":"ℤ","Integral":"∫","intercal":"⊺","Intersection":"⋂","intlarhk":"⨗","intprod":"⨼","InvisibleComma":"⁣","InvisibleTimes":"⁢","IOcy":"Ё","iocy":"ё","Iogon":"Į","iogon":"į","Iopf":"𝕀","iopf":"𝕚","Iota":"Ι","iota":"ι","iprod":"⨼","iquest":"¿","iscr":"𝒾","Iscr":"ℐ","isin":"∈","isindot":"⋵","isinE":"⋹","isins":"⋴","isinsv":"⋳","isinv":"∈","it":"⁢","Itilde":"Ĩ","itilde":"ĩ","Iukcy":"І","iukcy":"і","Iuml":"Ï","iuml":"ï","Jcirc":"Ĵ","jcirc":"ĵ","Jcy":"Й","jcy":"й","Jfr":"𝔍","jfr":"𝔧","jmath":"ȷ","Jopf":"𝕁","jopf":"𝕛","Jscr":"𝒥","jscr":"𝒿","Jsercy":"Ј","jsercy":"ј","Jukcy":"Є","jukcy":"є","Kappa":"Κ","kappa":"κ","kappav":"ϰ","Kcedil":"Ķ","kcedil":"ķ","Kcy":"К","kcy":"к","Kfr":"𝔎","kfr":"𝔨","kgreen":"ĸ","KHcy":"Х","khcy":"х","KJcy":"Ќ","kjcy":"ќ","Kopf":"𝕂","kopf":"𝕜","Kscr":"𝒦","kscr":"𝓀","lAarr":"⇚","Lacute":"Ĺ","lacute":"ĺ","laemptyv":"⦴","lagran":"ℒ","Lambda":"Λ","lambda":"λ","lang":"⟨","Lang":"⟪","langd":"⦑","langle":"⟨","lap":"⪅","Laplacetrf":"ℒ","laquo":"«","larrb":"⇤","larrbfs":"⤟","larr":"←","Larr":"↞","lArr":"⇐","larrfs":"⤝","larrhk":"↩","larrlp":"↫","larrpl":"⤹","larrsim":"⥳","larrtl":"↢","latail":"⤙","lAtail":"⤛","lat":"⪫","late":"⪭","lates":"⪭︀","lbarr":"⤌","lBarr":"⤎","lbbrk":"❲","lbrace":"{","lbrack":"[","lbrke":"⦋","lbrksld":"⦏","lbrkslu":"⦍","Lcaron":"Ľ","lcaron":"ľ","Lcedil":"Ļ","lcedil":"ļ","lceil":"⌈","lcub":"{","Lcy":"Л","lcy":"л","ldca":"⤶","ldquo":"“","ldquor":"„","ldrdhar":"⥧","ldrushar":"⥋","ldsh":"↲","le":"≤","lE":"≦","LeftAngleBracket":"⟨","LeftArrowBar":"⇤","leftarrow":"←","LeftArrow":"←","Leftarrow":"⇐","LeftArrowRightArrow":"⇆","leftarrowtail":"↢","LeftCeiling":"⌈","LeftDoubleBracket":"⟦","LeftDownTeeVector":"⥡","LeftDownVectorBar":"⥙","LeftDownVector":"⇃","LeftFloor":"⌊","leftharpoondown":"↽","leftharpoonup":"↼","leftleftarrows":"⇇","leftrightarrow":"↔","LeftRightArrow":"↔","Leftrightarrow":"⇔","leftrightarrows":"⇆","leftrightharpoons":"⇋","leftrightsquigarrow":"↭","LeftRightVector":"⥎","LeftTeeArrow":"↤","LeftTee":"⊣","LeftTeeVector":"⥚","leftthreetimes":"⋋","LeftTriangleBar":"⧏","LeftTriangle":"⊲","LeftTriangleEqual":"⊴","LeftUpDownVector":"⥑","LeftUpTeeVector":"⥠","LeftUpVectorBar":"⥘","LeftUpVector":"↿","LeftVectorBar":"⥒","LeftVector":"↼","lEg":"⪋","leg":"⋚","leq":"≤","leqq":"≦","leqslant":"⩽","lescc":"⪨","les":"⩽","lesdot":"⩿","lesdoto":"⪁","lesdotor":"⪃","lesg":"⋚︀","lesges":"⪓","lessapprox":"⪅","lessdot":"⋖","lesseqgtr":"⋚","lesseqqgtr":"⪋","LessEqualGreater":"⋚","LessFullEqual":"≦","LessGreater":"≶","lessgtr":"≶","LessLess":"⪡","lesssim":"≲","LessSlantEqual":"⩽","LessTilde":"≲","lfisht":"⥼","lfloor":"⌊","Lfr":"𝔏","lfr":"𝔩","lg":"≶","lgE":"⪑","lHar":"⥢","lhard":"↽","lharu":"↼","lharul":"⥪","lhblk":"▄","LJcy":"Љ","ljcy":"љ","llarr":"⇇","ll":"≪","Ll":"⋘","llcorner":"⌞","Lleftarrow":"⇚","llhard":"⥫","lltri":"◺","Lmidot":"Ŀ","lmidot":"ŀ","lmoustache":"⎰","lmoust":"⎰","lnap":"⪉","lnapprox":"⪉","lne":"⪇","lnE":"≨","lneq":"⪇","lneqq":"≨","lnsim":"⋦","loang":"⟬","loarr":"⇽","lobrk":"⟦","longleftarrow":"⟵","LongLeftArrow":"⟵","Longleftarrow":"⟸","longleftrightarrow":"⟷","LongLeftRightArrow":"⟷","Longleftrightarrow":"⟺","longmapsto":"⟼","longrightarrow":"⟶","LongRightArrow":"⟶","Longrightarrow":"⟹","looparrowleft":"↫","looparrowright":"↬","lopar":"⦅","Lopf":"𝕃","lopf":"𝕝","loplus":"⨭","lotimes":"⨴","lowast":"∗","lowbar":"_","LowerLeftArrow":"↙","LowerRightArrow":"↘","loz":"◊","lozenge":"◊","lozf":"⧫","lpar":"(","lparlt":"⦓","lrarr":"⇆","lrcorner":"⌟","lrhar":"⇋","lrhard":"⥭","lrm":"‎","lrtri":"⊿","lsaquo":"‹","lscr":"𝓁","Lscr":"ℒ","lsh":"↰","Lsh":"↰","lsim":"≲","lsime":"⪍","lsimg":"⪏","lsqb":"[","lsquo":"‘","lsquor":"‚","Lstrok":"Ł","lstrok":"ł","ltcc":"⪦","ltcir":"⩹","lt":"<","LT":"<","Lt":"≪","ltdot":"⋖","lthree":"⋋","ltimes":"⋉","ltlarr":"⥶","ltquest":"⩻","ltri":"◃","ltrie":"⊴","ltrif":"◂","ltrPar":"⦖","lurdshar":"⥊","luruhar":"⥦","lvertneqq":"≨︀","lvnE":"≨︀","macr":"¯","male":"♂","malt":"✠","maltese":"✠","Map":"⤅","map":"↦","mapsto":"↦","mapstodown":"↧","mapstoleft":"↤","mapstoup":"↥","marker":"▮","mcomma":"⨩","Mcy":"М","mcy":"м","mdash":"—","mDDot":"∺","measuredangle":"∡","MediumSpace":" ","Mellintrf":"ℳ","Mfr":"𝔐","mfr":"𝔪","mho":"℧","micro":"µ","midast":"*","midcir":"⫰","mid":"∣","middot":"·","minusb":"⊟","minus":"−","minusd":"∸","minusdu":"⨪","MinusPlus":"∓","mlcp":"⫛","mldr":"…","mnplus":"∓","models":"⊧","Mopf":"𝕄","mopf":"𝕞","mp":"∓","mscr":"𝓂","Mscr":"ℳ","mstpos":"∾","Mu":"Μ","mu":"μ","multimap":"⊸","mumap":"⊸","nabla":"∇","Nacute":"Ń","nacute":"ń","nang":"∠⃒","nap":"≉","napE":"⩰̸","napid":"≋̸","napos":"ʼn","napprox":"≉","natural":"♮","naturals":"ℕ","natur":"♮","nbsp":" ","nbump":"≎̸","nbumpe":"≏̸","ncap":"⩃","Ncaron":"Ň","ncaron":"ň","Ncedil":"Ņ","ncedil":"ņ","ncong":"≇","ncongdot":"⩭̸","ncup":"⩂","Ncy":"Н","ncy":"н","ndash":"–","nearhk":"⤤","nearr":"↗","neArr":"⇗","nearrow":"↗","ne":"≠","nedot":"≐̸","NegativeMediumSpace":"​","NegativeThickSpace":"​","NegativeThinSpace":"​","NegativeVeryThinSpace":"​","nequiv":"≢","nesear":"⤨","nesim":"≂̸","NestedGreaterGreater":"≫","NestedLessLess":"≪","NewLine":"\\n","nexist":"∄","nexists":"∄","Nfr":"𝔑","nfr":"𝔫","ngE":"≧̸","nge":"≱","ngeq":"≱","ngeqq":"≧̸","ngeqslant":"⩾̸","nges":"⩾̸","nGg":"⋙̸","ngsim":"≵","nGt":"≫⃒","ngt":"≯","ngtr":"≯","nGtv":"≫̸","nharr":"↮","nhArr":"⇎","nhpar":"⫲","ni":"∋","nis":"⋼","nisd":"⋺","niv":"∋","NJcy":"Њ","njcy":"њ","nlarr":"↚","nlArr":"⇍","nldr":"‥","nlE":"≦̸","nle":"≰","nleftarrow":"↚","nLeftarrow":"⇍","nleftrightarrow":"↮","nLeftrightarrow":"⇎","nleq":"≰","nleqq":"≦̸","nleqslant":"⩽̸","nles":"⩽̸","nless":"≮","nLl":"⋘̸","nlsim":"≴","nLt":"≪⃒","nlt":"≮","nltri":"⋪","nltrie":"⋬","nLtv":"≪̸","nmid":"∤","NoBreak":"⁠","NonBreakingSpace":" ","nopf":"𝕟","Nopf":"ℕ","Not":"⫬","not":"¬","NotCongruent":"≢","NotCupCap":"≭","NotDoubleVerticalBar":"∦","NotElement":"∉","NotEqual":"≠","NotEqualTilde":"≂̸","NotExists":"∄","NotGreater":"≯","NotGreaterEqual":"≱","NotGreaterFullEqual":"≧̸","NotGreaterGreater":"≫̸","NotGreaterLess":"≹","NotGreaterSlantEqual":"⩾̸","NotGreaterTilde":"≵","NotHumpDownHump":"≎̸","NotHumpEqual":"≏̸","notin":"∉","notindot":"⋵̸","notinE":"⋹̸","notinva":"∉","notinvb":"⋷","notinvc":"⋶","NotLeftTriangleBar":"⧏̸","NotLeftTriangle":"⋪","NotLeftTriangleEqual":"⋬","NotLess":"≮","NotLessEqual":"≰","NotLessGreater":"≸","NotLessLess":"≪̸","NotLessSlantEqual":"⩽̸","NotLessTilde":"≴","NotNestedGreaterGreater":"⪢̸","NotNestedLessLess":"⪡̸","notni":"∌","notniva":"∌","notnivb":"⋾","notnivc":"⋽","NotPrecedes":"⊀","NotPrecedesEqual":"⪯̸","NotPrecedesSlantEqual":"⋠","NotReverseElement":"∌","NotRightTriangleBar":"⧐̸","NotRightTriangle":"⋫","NotRightTriangleEqual":"⋭","NotSquareSubset":"⊏̸","NotSquareSubsetEqual":"⋢","NotSquareSuperset":"⊐̸","NotSquareSupersetEqual":"⋣","NotSubset":"⊂⃒","NotSubsetEqual":"⊈","NotSucceeds":"⊁","NotSucceedsEqual":"⪰̸","NotSucceedsSlantEqual":"⋡","NotSucceedsTilde":"≿̸","NotSuperset":"⊃⃒","NotSupersetEqual":"⊉","NotTilde":"≁","NotTildeEqual":"≄","NotTildeFullEqual":"≇","NotTildeTilde":"≉","NotVerticalBar":"∤","nparallel":"∦","npar":"∦","nparsl":"⫽⃥","npart":"∂̸","npolint":"⨔","npr":"⊀","nprcue":"⋠","nprec":"⊀","npreceq":"⪯̸","npre":"⪯̸","nrarrc":"⤳̸","nrarr":"↛","nrArr":"⇏","nrarrw":"↝̸","nrightarrow":"↛","nRightarrow":"⇏","nrtri":"⋫","nrtrie":"⋭","nsc":"⊁","nsccue":"⋡","nsce":"⪰̸","Nscr":"𝒩","nscr":"𝓃","nshortmid":"∤","nshortparallel":"∦","nsim":"≁","nsime":"≄","nsimeq":"≄","nsmid":"∤","nspar":"∦","nsqsube":"⋢","nsqsupe":"⋣","nsub":"⊄","nsubE":"⫅̸","nsube":"⊈","nsubset":"⊂⃒","nsubseteq":"⊈","nsubseteqq":"⫅̸","nsucc":"⊁","nsucceq":"⪰̸","nsup":"⊅","nsupE":"⫆̸","nsupe":"⊉","nsupset":"⊃⃒","nsupseteq":"⊉","nsupseteqq":"⫆̸","ntgl":"≹","Ntilde":"Ñ","ntilde":"ñ","ntlg":"≸","ntriangleleft":"⋪","ntrianglelefteq":"⋬","ntriangleright":"⋫","ntrianglerighteq":"⋭","Nu":"Ν","nu":"ν","num":"#","numero":"№","numsp":" ","nvap":"≍⃒","nvdash":"⊬","nvDash":"⊭","nVdash":"⊮","nVDash":"⊯","nvge":"≥⃒","nvgt":">⃒","nvHarr":"⤄","nvinfin":"⧞","nvlArr":"⤂","nvle":"≤⃒","nvlt":"<⃒","nvltrie":"⊴⃒","nvrArr":"⤃","nvrtrie":"⊵⃒","nvsim":"∼⃒","nwarhk":"⤣","nwarr":"↖","nwArr":"⇖","nwarrow":"↖","nwnear":"⤧","Oacute":"Ó","oacute":"ó","oast":"⊛","Ocirc":"Ô","ocirc":"ô","ocir":"⊚","Ocy":"О","ocy":"о","odash":"⊝","Odblac":"Ő","odblac":"ő","odiv":"⨸","odot":"⊙","odsold":"⦼","OElig":"Œ","oelig":"œ","ofcir":"⦿","Ofr":"𝔒","ofr":"𝔬","ogon":"˛","Ograve":"Ò","ograve":"ò","ogt":"⧁","ohbar":"⦵","ohm":"Ω","oint":"∮","olarr":"↺","olcir":"⦾","olcross":"⦻","oline":"‾","olt":"⧀","Omacr":"Ō","omacr":"ō","Omega":"Ω","omega":"ω","Omicron":"Ο","omicron":"ο","omid":"⦶","ominus":"⊖","Oopf":"𝕆","oopf":"𝕠","opar":"⦷","OpenCurlyDoubleQuote":"“","OpenCurlyQuote":"‘","operp":"⦹","oplus":"⊕","orarr":"↻","Or":"⩔","or":"∨","ord":"⩝","order":"ℴ","orderof":"ℴ","ordf":"ª","ordm":"º","origof":"⊶","oror":"⩖","orslope":"⩗","orv":"⩛","oS":"Ⓢ","Oscr":"𝒪","oscr":"ℴ","Oslash":"Ø","oslash":"ø","osol":"⊘","Otilde":"Õ","otilde":"õ","otimesas":"⨶","Otimes":"⨷","otimes":"⊗","Ouml":"Ö","ouml":"ö","ovbar":"⌽","OverBar":"‾","OverBrace":"⏞","OverBracket":"⎴","OverParenthesis":"⏜","para":"¶","parallel":"∥","par":"∥","parsim":"⫳","parsl":"⫽","part":"∂","PartialD":"∂","Pcy":"П","pcy":"п","percnt":"%","period":".","permil":"‰","perp":"⊥","pertenk":"‱","Pfr":"𝔓","pfr":"𝔭","Phi":"Φ","phi":"φ","phiv":"ϕ","phmmat":"ℳ","phone":"☎","Pi":"Π","pi":"π","pitchfork":"⋔","piv":"ϖ","planck":"ℏ","planckh":"ℎ","plankv":"ℏ","plusacir":"⨣","plusb":"⊞","pluscir":"⨢","plus":"+","plusdo":"∔","plusdu":"⨥","pluse":"⩲","PlusMinus":"±","plusmn":"±","plussim":"⨦","plustwo":"⨧","pm":"±","Poincareplane":"ℌ","pointint":"⨕","popf":"𝕡","Popf":"ℙ","pound":"£","prap":"⪷","Pr":"⪻","pr":"≺","prcue":"≼","precapprox":"⪷","prec":"≺","preccurlyeq":"≼","Precedes":"≺","PrecedesEqual":"⪯","PrecedesSlantEqual":"≼","PrecedesTilde":"≾","preceq":"⪯","precnapprox":"⪹","precneqq":"⪵","precnsim":"⋨","pre":"⪯","prE":"⪳","precsim":"≾","prime":"′","Prime":"″","primes":"ℙ","prnap":"⪹","prnE":"⪵","prnsim":"⋨","prod":"∏","Product":"∏","profalar":"⌮","profline":"⌒","profsurf":"⌓","prop":"∝","Proportional":"∝","Proportion":"∷","propto":"∝","prsim":"≾","prurel":"⊰","Pscr":"𝒫","pscr":"𝓅","Psi":"Ψ","psi":"ψ","puncsp":" ","Qfr":"𝔔","qfr":"𝔮","qint":"⨌","qopf":"𝕢","Qopf":"ℚ","qprime":"⁗","Qscr":"𝒬","qscr":"𝓆","quaternions":"ℍ","quatint":"⨖","quest":"?","questeq":"≟","quot":"\\"","QUOT":"\\"","rAarr":"⇛","race":"∽̱","Racute":"Ŕ","racute":"ŕ","radic":"√","raemptyv":"⦳","rang":"⟩","Rang":"⟫","rangd":"⦒","range":"⦥","rangle":"⟩","raquo":"»","rarrap":"⥵","rarrb":"⇥","rarrbfs":"⤠","rarrc":"⤳","rarr":"→","Rarr":"↠","rArr":"⇒","rarrfs":"⤞","rarrhk":"↪","rarrlp":"↬","rarrpl":"⥅","rarrsim":"⥴","Rarrtl":"⤖","rarrtl":"↣","rarrw":"↝","ratail":"⤚","rAtail":"⤜","ratio":"∶","rationals":"ℚ","rbarr":"⤍","rBarr":"⤏","RBarr":"⤐","rbbrk":"❳","rbrace":"}","rbrack":"]","rbrke":"⦌","rbrksld":"⦎","rbrkslu":"⦐","Rcaron":"Ř","rcaron":"ř","Rcedil":"Ŗ","rcedil":"ŗ","rceil":"⌉","rcub":"}","Rcy":"Р","rcy":"р","rdca":"⤷","rdldhar":"⥩","rdquo":"”","rdquor":"”","rdsh":"↳","real":"ℜ","realine":"ℛ","realpart":"ℜ","reals":"ℝ","Re":"ℜ","rect":"▭","reg":"®","REG":"®","ReverseElement":"∋","ReverseEquilibrium":"⇋","ReverseUpEquilibrium":"⥯","rfisht":"⥽","rfloor":"⌋","rfr":"𝔯","Rfr":"ℜ","rHar":"⥤","rhard":"⇁","rharu":"⇀","rharul":"⥬","Rho":"Ρ","rho":"ρ","rhov":"ϱ","RightAngleBracket":"⟩","RightArrowBar":"⇥","rightarrow":"→","RightArrow":"→","Rightarrow":"⇒","RightArrowLeftArrow":"⇄","rightarrowtail":"↣","RightCeiling":"⌉","RightDoubleBracket":"⟧","RightDownTeeVector":"⥝","RightDownVectorBar":"⥕","RightDownVector":"⇂","RightFloor":"⌋","rightharpoondown":"⇁","rightharpoonup":"⇀","rightleftarrows":"⇄","rightleftharpoons":"⇌","rightrightarrows":"⇉","rightsquigarrow":"↝","RightTeeArrow":"↦","RightTee":"⊢","RightTeeVector":"⥛","rightthreetimes":"⋌","RightTriangleBar":"⧐","RightTriangle":"⊳","RightTriangleEqual":"⊵","RightUpDownVector":"⥏","RightUpTeeVector":"⥜","RightUpVectorBar":"⥔","RightUpVector":"↾","RightVectorBar":"⥓","RightVector":"⇀","ring":"˚","risingdotseq":"≓","rlarr":"⇄","rlhar":"⇌","rlm":"‏","rmoustache":"⎱","rmoust":"⎱","rnmid":"⫮","roang":"⟭","roarr":"⇾","robrk":"⟧","ropar":"⦆","ropf":"𝕣","Ropf":"ℝ","roplus":"⨮","rotimes":"⨵","RoundImplies":"⥰","rpar":")","rpargt":"⦔","rppolint":"⨒","rrarr":"⇉","Rrightarrow":"⇛","rsaquo":"›","rscr":"𝓇","Rscr":"ℛ","rsh":"↱","Rsh":"↱","rsqb":"]","rsquo":"’","rsquor":"’","rthree":"⋌","rtimes":"⋊","rtri":"▹","rtrie":"⊵","rtrif":"▸","rtriltri":"⧎","RuleDelayed":"⧴","ruluhar":"⥨","rx":"℞","Sacute":"Ś","sacute":"ś","sbquo":"‚","scap":"⪸","Scaron":"Š","scaron":"š","Sc":"⪼","sc":"≻","sccue":"≽","sce":"⪰","scE":"⪴","Scedil":"Ş","scedil":"ş","Scirc":"Ŝ","scirc":"ŝ","scnap":"⪺","scnE":"⪶","scnsim":"⋩","scpolint":"⨓","scsim":"≿","Scy":"С","scy":"с","sdotb":"⊡","sdot":"⋅","sdote":"⩦","searhk":"⤥","searr":"↘","seArr":"⇘","searrow":"↘","sect":"§","semi":";","seswar":"⤩","setminus":"∖","setmn":"∖","sext":"✶","Sfr":"𝔖","sfr":"𝔰","sfrown":"⌢","sharp":"♯","SHCHcy":"Щ","shchcy":"щ","SHcy":"Ш","shcy":"ш","ShortDownArrow":"↓","ShortLeftArrow":"←","shortmid":"∣","shortparallel":"∥","ShortRightArrow":"→","ShortUpArrow":"↑","shy":"­","Sigma":"Σ","sigma":"σ","sigmaf":"ς","sigmav":"ς","sim":"∼","simdot":"⩪","sime":"≃","simeq":"≃","simg":"⪞","simgE":"⪠","siml":"⪝","simlE":"⪟","simne":"≆","simplus":"⨤","simrarr":"⥲","slarr":"←","SmallCircle":"∘","smallsetminus":"∖","smashp":"⨳","smeparsl":"⧤","smid":"∣","smile":"⌣","smt":"⪪","smte":"⪬","smtes":"⪬︀","SOFTcy":"Ь","softcy":"ь","solbar":"⌿","solb":"⧄","sol":"/","Sopf":"𝕊","sopf":"𝕤","spades":"♠","spadesuit":"♠","spar":"∥","sqcap":"⊓","sqcaps":"⊓︀","sqcup":"⊔","sqcups":"⊔︀","Sqrt":"√","sqsub":"⊏","sqsube":"⊑","sqsubset":"⊏","sqsubseteq":"⊑","sqsup":"⊐","sqsupe":"⊒","sqsupset":"⊐","sqsupseteq":"⊒","square":"□","Square":"□","SquareIntersection":"⊓","SquareSubset":"⊏","SquareSubsetEqual":"⊑","SquareSuperset":"⊐","SquareSupersetEqual":"⊒","SquareUnion":"⊔","squarf":"▪","squ":"□","squf":"▪","srarr":"→","Sscr":"𝒮","sscr":"𝓈","ssetmn":"∖","ssmile":"⌣","sstarf":"⋆","Star":"⋆","star":"☆","starf":"★","straightepsilon":"ϵ","straightphi":"ϕ","strns":"¯","sub":"⊂","Sub":"⋐","subdot":"⪽","subE":"⫅","sube":"⊆","subedot":"⫃","submult":"⫁","subnE":"⫋","subne":"⊊","subplus":"⪿","subrarr":"⥹","subset":"⊂","Subset":"⋐","subseteq":"⊆","subseteqq":"⫅","SubsetEqual":"⊆","subsetneq":"⊊","subsetneqq":"⫋","subsim":"⫇","subsub":"⫕","subsup":"⫓","succapprox":"⪸","succ":"≻","succcurlyeq":"≽","Succeeds":"≻","SucceedsEqual":"⪰","SucceedsSlantEqual":"≽","SucceedsTilde":"≿","succeq":"⪰","succnapprox":"⪺","succneqq":"⪶","succnsim":"⋩","succsim":"≿","SuchThat":"∋","sum":"∑","Sum":"∑","sung":"♪","sup1":"¹","sup2":"²","sup3":"³","sup":"⊃","Sup":"⋑","supdot":"⪾","supdsub":"⫘","supE":"⫆","supe":"⊇","supedot":"⫄","Superset":"⊃","SupersetEqual":"⊇","suphsol":"⟉","suphsub":"⫗","suplarr":"⥻","supmult":"⫂","supnE":"⫌","supne":"⊋","supplus":"⫀","supset":"⊃","Supset":"⋑","supseteq":"⊇","supseteqq":"⫆","supsetneq":"⊋","supsetneqq":"⫌","supsim":"⫈","supsub":"⫔","supsup":"⫖","swarhk":"⤦","swarr":"↙","swArr":"⇙","swarrow":"↙","swnwar":"⤪","szlig":"ß","Tab":"\\t","target":"⌖","Tau":"Τ","tau":"τ","tbrk":"⎴","Tcaron":"Ť","tcaron":"ť","Tcedil":"Ţ","tcedil":"ţ","Tcy":"Т","tcy":"т","tdot":"⃛","telrec":"⌕","Tfr":"𝔗","tfr":"𝔱","there4":"∴","therefore":"∴","Therefore":"∴","Theta":"Θ","theta":"θ","thetasym":"ϑ","thetav":"ϑ","thickapprox":"≈","thicksim":"∼","ThickSpace":"  ","ThinSpace":" ","thinsp":" ","thkap":"≈","thksim":"∼","THORN":"Þ","thorn":"þ","tilde":"˜","Tilde":"∼","TildeEqual":"≃","TildeFullEqual":"≅","TildeTilde":"≈","timesbar":"⨱","timesb":"⊠","times":"×","timesd":"⨰","tint":"∭","toea":"⤨","topbot":"⌶","topcir":"⫱","top":"⊤","Topf":"𝕋","topf":"𝕥","topfork":"⫚","tosa":"⤩","tprime":"‴","trade":"™","TRADE":"™","triangle":"▵","triangledown":"▿","triangleleft":"◃","trianglelefteq":"⊴","triangleq":"≜","triangleright":"▹","trianglerighteq":"⊵","tridot":"◬","trie":"≜","triminus":"⨺","TripleDot":"⃛","triplus":"⨹","trisb":"⧍","tritime":"⨻","trpezium":"⏢","Tscr":"𝒯","tscr":"𝓉","TScy":"Ц","tscy":"ц","TSHcy":"Ћ","tshcy":"ћ","Tstrok":"Ŧ","tstrok":"ŧ","twixt":"≬","twoheadleftarrow":"↞","twoheadrightarrow":"↠","Uacute":"Ú","uacute":"ú","uarr":"↑","Uarr":"↟","uArr":"⇑","Uarrocir":"⥉","Ubrcy":"Ў","ubrcy":"ў","Ubreve":"Ŭ","ubreve":"ŭ","Ucirc":"Û","ucirc":"û","Ucy":"У","ucy":"у","udarr":"⇅","Udblac":"Ű","udblac":"ű","udhar":"⥮","ufisht":"⥾","Ufr":"𝔘","ufr":"𝔲","Ugrave":"Ù","ugrave":"ù","uHar":"⥣","uharl":"↿","uharr":"↾","uhblk":"▀","ulcorn":"⌜","ulcorner":"⌜","ulcrop":"⌏","ultri":"◸","Umacr":"Ū","umacr":"ū","uml":"¨","UnderBar":"_","UnderBrace":"⏟","UnderBracket":"⎵","UnderParenthesis":"⏝","Union":"⋃","UnionPlus":"⊎","Uogon":"Ų","uogon":"ų","Uopf":"𝕌","uopf":"𝕦","UpArrowBar":"⤒","uparrow":"↑","UpArrow":"↑","Uparrow":"⇑","UpArrowDownArrow":"⇅","updownarrow":"↕","UpDownArrow":"↕","Updownarrow":"⇕","UpEquilibrium":"⥮","upharpoonleft":"↿","upharpoonright":"↾","uplus":"⊎","UpperLeftArrow":"↖","UpperRightArrow":"↗","upsi":"υ","Upsi":"ϒ","upsih":"ϒ","Upsilon":"Υ","upsilon":"υ","UpTeeArrow":"↥","UpTee":"⊥","upuparrows":"⇈","urcorn":"⌝","urcorner":"⌝","urcrop":"⌎","Uring":"Ů","uring":"ů","urtri":"◹","Uscr":"𝒰","uscr":"𝓊","utdot":"⋰","Utilde":"Ũ","utilde":"ũ","utri":"▵","utrif":"▴","uuarr":"⇈","Uuml":"Ü","uuml":"ü","uwangle":"⦧","vangrt":"⦜","varepsilon":"ϵ","varkappa":"ϰ","varnothing":"∅","varphi":"ϕ","varpi":"ϖ","varpropto":"∝","varr":"↕","vArr":"⇕","varrho":"ϱ","varsigma":"ς","varsubsetneq":"⊊︀","varsubsetneqq":"⫋︀","varsupsetneq":"⊋︀","varsupsetneqq":"⫌︀","vartheta":"ϑ","vartriangleleft":"⊲","vartriangleright":"⊳","vBar":"⫨","Vbar":"⫫","vBarv":"⫩","Vcy":"В","vcy":"в","vdash":"⊢","vDash":"⊨","Vdash":"⊩","VDash":"⊫","Vdashl":"⫦","veebar":"⊻","vee":"∨","Vee":"⋁","veeeq":"≚","vellip":"⋮","verbar":"|","Verbar":"‖","vert":"|","Vert":"‖","VerticalBar":"∣","VerticalLine":"|","VerticalSeparator":"❘","VerticalTilde":"≀","VeryThinSpace":" ","Vfr":"𝔙","vfr":"𝔳","vltri":"⊲","vnsub":"⊂⃒","vnsup":"⊃⃒","Vopf":"𝕍","vopf":"𝕧","vprop":"∝","vrtri":"⊳","Vscr":"𝒱","vscr":"𝓋","vsubnE":"⫋︀","vsubne":"⊊︀","vsupnE":"⫌︀","vsupne":"⊋︀","Vvdash":"⊪","vzigzag":"⦚","Wcirc":"Ŵ","wcirc":"ŵ","wedbar":"⩟","wedge":"∧","Wedge":"⋀","wedgeq":"≙","weierp":"℘","Wfr":"𝔚","wfr":"𝔴","Wopf":"𝕎","wopf":"𝕨","wp":"℘","wr":"≀","wreath":"≀","Wscr":"𝒲","wscr":"𝓌","xcap":"⋂","xcirc":"◯","xcup":"⋃","xdtri":"▽","Xfr":"𝔛","xfr":"𝔵","xharr":"⟷","xhArr":"⟺","Xi":"Ξ","xi":"ξ","xlarr":"⟵","xlArr":"⟸","xmap":"⟼","xnis":"⋻","xodot":"⨀","Xopf":"𝕏","xopf":"𝕩","xoplus":"⨁","xotime":"⨂","xrarr":"⟶","xrArr":"⟹","Xscr":"𝒳","xscr":"𝓍","xsqcup":"⨆","xuplus":"⨄","xutri":"△","xvee":"⋁","xwedge":"⋀","Yacute":"Ý","yacute":"ý","YAcy":"Я","yacy":"я","Ycirc":"Ŷ","ycirc":"ŷ","Ycy":"Ы","ycy":"ы","yen":"¥","Yfr":"𝔜","yfr":"𝔶","YIcy":"Ї","yicy":"ї","Yopf":"𝕐","yopf":"𝕪","Yscr":"𝒴","yscr":"𝓎","YUcy":"Ю","yucy":"ю","yuml":"ÿ","Yuml":"Ÿ","Zacute":"Ź","zacute":"ź","Zcaron":"Ž","zcaron":"ž","Zcy":"З","zcy":"з","Zdot":"Ż","zdot":"ż","zeetrf":"ℨ","ZeroWidthSpace":"​","Zeta":"Ζ","zeta":"ζ","zfr":"𝔷","Zfr":"ℨ","ZHcy":"Ж","zhcy":"ж","zigrarr":"⇝","zopf":"𝕫","Zopf":"ℤ","Zscr":"𝒵","zscr":"𝓏","zwj":"‍","zwnj":"‌"}')},function(e,t,r){"use strict";var n={};function s(e,t,r){var o,i,a,u,c,l="";for("string"!=typeof t&&(r=t,t=s.defaultChars),void 0===r&&(r=!0),c=function(e){var t,r,s=n[e];if(s)return s;for(s=n[e]=[],t=0;t<128;t++)r=String.fromCharCode(t),/^[0-9a-z]$/i.test(r)?s.push(r):s.push("%"+("0"+t.toString(16).toUpperCase()).slice(-2));for(t=0;t=55296&&a<=57343){if(a>=55296&&a<=56319&&o+1=56320&&u<=57343){l+=encodeURIComponent(e[o]+e[o+1]),o++;continue}l+="%EF%BF%BD"}else l+=encodeURIComponent(e[o]);return l}s.defaultChars=";/?:@&=+$,-_.!~*'()#",s.componentChars="-_.!~*'()",e.exports=s},function(e,t,r){"use strict";var n={};function s(e,t){var r;return"string"!=typeof t&&(t=s.defaultChars),r=function(e){var t,r,s=n[e];if(s)return s;for(s=n[e]=[],t=0;t<128;t++)r=String.fromCharCode(t),s.push(r);for(t=0;t=55296&&u<=57343?"���":String.fromCharCode(u),t+=6):240==(248&s)&&t+91114111?c+="����":(u-=65536,c+=String.fromCharCode(55296+(u>>10),56320+(1023&u))),t+=9):c+="�";return c}))}s.defaultChars=";/?:@&=+$,#",s.componentChars="",e.exports=s},function(e,t,r){"use strict";e.exports=function(e){var t="";return t+=e.protocol||"",t+=e.slashes?"//":"",t+=e.auth?e.auth+"@":"",e.hostname&&-1!==e.hostname.indexOf(":")?t+="["+e.hostname+"]":t+=e.hostname||"",t+=e.port?":"+e.port:"",t+=e.pathname||"",t+=e.search||"",t+=e.hash||""}},function(e,t,r){"use strict";function n(){this.protocol=null,this.slashes=null,this.auth=null,this.port=null,this.hostname=null,this.hash=null,this.search=null,this.pathname=null}var s=/^([a-z0-9.+-]+:)/i,o=/:[0-9]*$/,i=/^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,a=["{","}","|","\\","^","`"].concat(["<",">",'"',"`"," ","\r","\n","\t"]),u=["'"].concat(a),c=["%","/","?",";","#"].concat(u),l=["/","?","#"],p=/^[+a-z0-9A-Z_-]{0,63}$/,h=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,f={javascript:!0,"javascript:":!0},d={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0};n.prototype.parse=function(e,t){var r,n,o,a,u,m=e;if(m=m.trim(),!t&&1===e.split("#").length){var g=i.exec(m);if(g)return this.pathname=g[1],g[2]&&(this.search=g[2]),this}var _=s.exec(m);if(_&&(o=(_=_[0]).toLowerCase(),this.protocol=_,m=m.substr(_.length)),(t||_||m.match(/^\/\/[^@\/]+@[^@\/]+/))&&(!(u="//"===m.substr(0,2))||_&&f[_]||(m=m.substr(2),this.slashes=!0)),!f[_]&&(u||_&&!d[_])){var b,k,C=-1;for(r=0;r127?x+="x":x+=y[F];if(!x.match(p)){var w=A.slice(0,r),q=A.slice(r+1),S=y.match(h);S&&(w.push(S[1]),q.unshift(S[2])),q.length&&(m=q.join(".")+m),this.hostname=w.join(".");break}}}}this.hostname.length>255&&(this.hostname=""),D&&(this.hostname=this.hostname.substr(1,this.hostname.length-2))}var L=m.indexOf("#");-1!==L&&(this.hash=m.substr(L),m=m.slice(0,L));var z=m.indexOf("?");return-1!==z&&(this.search=m.substr(z),m=m.slice(0,z)),m&&(this.pathname=m),d[o]&&this.hostname&&!this.pathname&&(this.pathname=""),this},n.prototype.parseHost=function(e){var t=o.exec(e);t&&(":"!==(t=t[0])&&(this.port=t.substr(1)),e=e.substr(0,e.length-t.length)),e&&(this.hostname=e)},e.exports=function(e,t){if(e&&e instanceof n)return e;var r=new n;return r.parse(e,t),r}},function(e,t,r){"use strict";t.Any=r(18),t.Cc=r(19),t.Cf=r(20),t.P=r(4),t.Z=r(21)},function(e,t){e.exports=/[\0-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/},function(e,t){e.exports=/[\0-\x1F\x7F-\x9F]/},function(e,t){e.exports=/[\xAD\u0600-\u0605\u061C\u06DD\u070F\u08E2\u180E\u200B-\u200F\u202A-\u202E\u2060-\u2064\u2066-\u206F\uFEFF\uFFF9-\uFFFB]|\uD804\uDCBD|\uD82F[\uDCA0-\uDCA3]|\uD834[\uDD73-\uDD7A]|\uDB40[\uDC01\uDC20-\uDC7F]/},function(e,t){e.exports=/[ \xA0\u1680\u2000-\u200A\u202F\u205F\u3000]/},function(e,t,r){"use strict";t.parseLinkLabel=r(23),t.parseLinkDestination=r(24),t.parseLinkTitle=r(25)},function(e,t,r){"use strict";e.exports=function(e,t,r){var n,s,o,i,a=-1,u=e.posMax,c=e.pos;for(e.pos=t+1,n=1;e.pos32)return a;if(41===s){if(0===o)break;o--}t++}return i===t||0!==o||(a.str=n(e.slice(i,t)),a.lines=0,a.pos=t,a.ok=!0),a}},function(e,t,r){"use strict";var n=r(0).unescapeAll;e.exports=function(e,t,r){var s,o,i=0,a=t,u={ok:!1,pos:0,lines:0,str:""};if(t>=r)return u;if(34!==(o=e.charCodeAt(t))&&39!==o&&40!==o)return u;for(t++,40===o&&(o=41);t"+o(e[t].content)+""},i.code_block=function(e,t,r,n,s){var i=e[t];return""+o(e[t].content)+"\n"},i.fence=function(e,t,r,n,i){var a,u,c,l,p,h=e[t],f=h.info?s(h.info).trim():"",d="",m="";return f&&(d=(c=f.split(/(\s+)/g))[0],m=c.slice(2).join("")),0===(a=r.highlight&&r.highlight(h.content,d,m)||o(h.content)).indexOf(""+a+"\n"):"
"+a+"
\n"},i.image=function(e,t,r,n,s){var o=e[t];return o.attrs[o.attrIndex("alt")][1]=s.renderInlineAsText(o.children,r,n),s.renderToken(e,t,r)},i.hardbreak=function(e,t,r){return r.xhtmlOut?"
\n":"
\n"},i.softbreak=function(e,t,r){return r.breaks?r.xhtmlOut?"
\n":"
\n":"\n"},i.text=function(e,t){return o(e[t].content)},i.html_block=function(e,t){return e[t].content},i.html_inline=function(e,t){return e[t].content},a.prototype.renderAttrs=function(e){var t,r,n;if(!e.attrs)return"";for(n="",t=0,r=e.attrs.length;t\n":">")},a.prototype.renderInline=function(e,t,r){for(var n,s="",o=this.rules,i=0,a=e.length;i/i.test(e)}e.exports=function(e){var t,r,o,i,a,u,c,l,p,h,f,d,m,g,_,b,k,C,v=e.tokens;if(e.md.options.linkify)for(r=0,o=v.length;r=0;t--)if("link_close"!==(u=i[t]).type){if("html_inline"===u.type&&(C=u.content,/^\s]/i.test(C)&&m>0&&m--,s(u.content)&&m++),!(m>0)&&"text"===u.type&&e.md.linkify.test(u.content)){for(p=u.content,k=e.md.linkify.match(p),c=[],d=u.level,f=0,l=0;lf&&((a=new e.Token("text","",0)).content=p.slice(f,h),a.level=d,c.push(a)),(a=new e.Token("link_open","a",1)).attrs=[["href",_]],a.level=d++,a.markup="linkify",a.info="auto",c.push(a),(a=new e.Token("text","",0)).content=b,a.level=d,c.push(a),(a=new e.Token("link_close","a",-1)).level=--d,a.markup="linkify",a.info="auto",c.push(a),f=k[l].lastIndex);f=0;t--)"text"!==(r=e[t]).type||n||(r.content=r.content.replace(o,a)),"link_open"===r.type&&"auto"===r.info&&n--,"link_close"===r.type&&"auto"===r.info&&n++}function c(e){var t,r,s=0;for(t=e.length-1;t>=0;t--)"text"!==(r=e[t]).type||s||n.test(r.content)&&(r.content=r.content.replace(/\+-/g,"±").replace(/\.{2,}/g,"…").replace(/([?!])…/g,"$1..").replace(/([?!]){4,}/g,"$1$1$1").replace(/,{2,}/g,",").replace(/(^|[^-])---(?=[^-]|$)/gm,"$1—").replace(/(^|\s)--(?=\s|$)/gm,"$1–").replace(/(^|[^-\s])--(?=[^-\s]|$)/gm,"$1–")),"link_open"===r.type&&"auto"===r.info&&s--,"link_close"===r.type&&"auto"===r.info&&s++}e.exports=function(e){var t;if(e.md.options.typographer)for(t=e.tokens.length-1;t>=0;t--)"inline"===e.tokens[t].type&&(s.test(e.tokens[t].content)&&u(e.tokens[t].children),n.test(e.tokens[t].content)&&c(e.tokens[t].children))}},function(e,t,r){"use strict";var n=r(0).isWhiteSpace,s=r(0).isPunctChar,o=r(0).isMdAsciiPunct,i=/['"]/,a=/['"]/g;function u(e,t,r){return e.substr(0,t)+r+e.substr(t+1)}function c(e,t){var r,i,c,l,p,h,f,d,m,g,_,b,k,C,v,D,A,y,x,F,E;for(x=[],r=0;r=0&&!(x[A].level<=f);A--);if(x.length=A+1,"text"===i.type){p=0,h=(c=i.content).length;e:for(;p=0)m=c.charCodeAt(l.index-1);else for(A=r-1;A>=0&&("softbreak"!==e[A].type&&"hardbreak"!==e[A].type);A--)if(e[A].content){m=e[A].content.charCodeAt(e[A].content.length-1);break}if(g=32,p=48&&m<=57&&(D=v=!1),v&&D&&(v=_,D=b),v||D){if(D)for(A=x.length-1;A>=0&&(d=x[A],!(x[A].level=0;t--)"inline"===e.tokens[t].type&&i.test(e.tokens[t].content)&&c(e.tokens[t].children,e)}},function(e,t,r){"use strict";var n=r(2);function s(e,t,r){this.src=e,this.env=r,this.tokens=[],this.inlineMode=!1,this.md=t}s.prototype.Token=n,e.exports=s},function(e,t,r){"use strict";var n=r(1),s=[["table",r(36),["paragraph","reference"]],["code",r(37)],["fence",r(38),["paragraph","reference","blockquote","list"]],["blockquote",r(39),["paragraph","reference","blockquote","list"]],["hr",r(40),["paragraph","reference","blockquote","list"]],["list",r(41),["paragraph","reference","blockquote"]],["reference",r(42)],["heading",r(43),["paragraph","reference","blockquote"]],["lheading",r(44)],["html_block",r(45),["paragraph","reference","blockquote"]],["paragraph",r(47)]];function o(){this.ruler=new n;for(var e=0;e=r))&&!(e.sCount[i]=u){e.line=r;break}for(n=0;nr)return!1;if(h=t+1,e.sCount[h]=4)return!1;if((c=e.bMarks[h]+e.tShift[h])>=e.eMarks[h])return!1;if(124!==(a=e.src.charCodeAt(c++))&&45!==a&&58!==a)return!1;for(;c=4)return!1;if((f=o(u)).length&&""===f[0]&&f.shift(),f.length&&""===f[f.length-1]&&f.pop(),0===(d=f.length)||d!==g.length)return!1;if(i)return!0;for(C=e.parentType,e.parentType="table",D=e.md.block.ruler.getRules("blockquote"),(m=e.push("table_open","table",1)).map=b=[t,0],(m=e.push("thead_open","thead",1)).map=[t,t+1],(m=e.push("tr_open","tr",1)).map=[t,t+1],l=0;l=4)break;for((f=o(u)).length&&""===f[0]&&f.shift(),f.length&&""===f[f.length-1]&&f.pop(),h===t+2&&((m=e.push("tbody_open","tbody",1)).map=k=[t+2,0]),(m=e.push("tr_open","tr",1)).map=[h,h+1],l=0;l=4))break;s=++n}return e.line=s,(o=e.push("code_block","code",0)).content=e.getLines(t,s,4+e.blkIndent,!0),o.map=[t,e.line],!0}},function(e,t,r){"use strict";e.exports=function(e,t,r,n){var s,o,i,a,u,c,l,p=!1,h=e.bMarks[t]+e.tShift[t],f=e.eMarks[t];if(e.sCount[t]-e.blkIndent>=4)return!1;if(h+3>f)return!1;if(126!==(s=e.src.charCodeAt(h))&&96!==s)return!1;if(u=h,(o=(h=e.skipChars(h,s))-u)<3)return!1;if(l=e.src.slice(u,h),i=e.src.slice(h,f),96===s&&i.indexOf(String.fromCharCode(s))>=0)return!1;if(n)return!0;for(a=t;!(++a>=r)&&!((h=u=e.bMarks[a]+e.tShift[a])<(f=e.eMarks[a])&&e.sCount[a]=4||(h=e.skipChars(h,s))-u=4)return!1;if(62!==e.src.charCodeAt(F++))return!1;if(s)return!0;for(u=f=e.sCount[t]+1,32===e.src.charCodeAt(F)?(F++,u++,f++,o=!1,C=!0):9===e.src.charCodeAt(F)?(C=!0,(e.bsCount[t]+f)%4==3?(F++,u++,f++,o=!1):o=!0):C=!1,d=[e.bMarks[t]],e.bMarks[t]=F;F=E,b=[e.sCount[t]],e.sCount[t]=f-u,k=[e.tShift[t]],e.tShift[t]=F-e.bMarks[t],D=e.md.block.ruler.getRules("blockquote"),_=e.parentType,e.parentType="blockquote",h=t+1;h=(E=e.eMarks[h])));h++)if(62!==e.src.charCodeAt(F++)||y){if(l)break;for(v=!1,a=0,c=D.length;a=E,m.push(e.bsCount[h]),e.bsCount[h]=e.sCount[h]+1+(C?1:0),b.push(e.sCount[h]),e.sCount[h]=f-u,k.push(e.tShift[h]),e.tShift[h]=F-e.bMarks[h]}for(g=e.blkIndent,e.blkIndent=0,(A=e.push("blockquote_open","blockquote",1)).markup=">",A.map=p=[t,0],e.md.block.tokenize(e,t,h),(A=e.push("blockquote_close","blockquote",-1)).markup=">",e.lineMax=x,e.parentType=_,p[1]=e.line,a=0;a=4)return!1;if(42!==(o=e.src.charCodeAt(c++))&&45!==o&&95!==o)return!1;for(i=1;c=i)return-1;if((r=e.src.charCodeAt(o++))<48||r>57)return-1;for(;;){if(o>=i)return-1;if(!((r=e.src.charCodeAt(o++))>=48&&r<=57)){if(41===r||46===r)break;return-1}if(o-s>=10)return-1}return o=4)return!1;if(e.listIndent>=0&&e.sCount[t]-e.listIndent>=4&&e.sCount[t]=e.blkIndent&&(B=!0),(w=o(e,t))>=0){if(h=!0,S=e.bMarks[t]+e.tShift[t],b=Number(e.src.substr(S,w-S-1)),B&&1!==b)return!1}else{if(!((w=s(e,t))>=0))return!1;h=!1}if(B&&e.skipSpaces(w)>=e.eMarks[t])return!1;if(_=e.src.charCodeAt(w-1),n)return!0;for(g=e.tokens.length,h?(T=e.push("ordered_list_open","ol",1),1!==b&&(T.attrs=[["start",b]])):T=e.push("bullet_list_open","ul",1),T.map=m=[t,0],T.markup=String.fromCharCode(_),C=t,q=!1,z=e.md.block.ruler.getRules("list"),A=e.parentType,e.parentType="list";C=k?1:v-p)>4&&(l=1),c=p+l,(T=e.push("list_item_open","li",1)).markup=String.fromCharCode(_),T.map=f=[t,0],F=e.tight,x=e.tShift[t],y=e.sCount[t],D=e.listIndent,e.listIndent=e.blkIndent,e.blkIndent=c,e.tight=!0,e.tShift[t]=a-e.bMarks[t],e.sCount[t]=v,a>=k&&e.isEmpty(t+1)?e.line=Math.min(e.line+2,r):e.md.block.tokenize(e,t,r,!0),e.tight&&!q||(I=!1),q=e.line-t>1&&e.isEmpty(e.line-1),e.blkIndent=e.listIndent,e.listIndent=D,e.tShift[t]=x,e.sCount[t]=y,e.tight=F,(T=e.push("list_item_close","li",-1)).markup=String.fromCharCode(_),C=t=e.line,f[1]=C,a=e.bMarks[t],C>=r)break;if(e.sCount[C]=4)break;for(L=!1,u=0,d=z.length;u=4)return!1;if(91!==e.src.charCodeAt(A))return!1;for(;++A3||e.sCount[x]<0)){for(k=!1,p=0,h=C.length;p=4)return!1;if(35!==(o=e.src.charCodeAt(c))||c>=l)return!1;for(i=1,o=e.src.charCodeAt(++c);35===o&&c6||cc&&n(e.src.charCodeAt(a-1))&&(l=a),e.line=t+1,(u=e.push("heading_open","h"+String(i),1)).markup="########".slice(0,i),u.map=[t,e.line],(u=e.push("inline","",0)).content=e.src.slice(c,l).trim(),u.map=[t,e.line],u.children=[],(u=e.push("heading_close","h"+String(i),-1)).markup="########".slice(0,i)),!0)}},function(e,t,r){"use strict";e.exports=function(e,t,r){var n,s,o,i,a,u,c,l,p,h,f=t+1,d=e.md.block.ruler.getRules("paragraph");if(e.sCount[t]-e.blkIndent>=4)return!1;for(h=e.parentType,e.parentType="paragraph";f3)){if(e.sCount[f]>=e.blkIndent&&(u=e.bMarks[f]+e.tShift[f])<(c=e.eMarks[f])&&(45===(p=e.src.charCodeAt(u))||61===p)&&(u=e.skipChars(u,p),(u=e.skipSpaces(u))>=c)){l=61===p?1:2;break}if(!(e.sCount[f]<0)){for(s=!1,o=0,i=d.length;o|$))/i,/<\/(script|pre|style)>/i,!0],[/^/,!0],[/^<\?/,/\?>/,!0],[/^/,!0],[/^/,!0],[new RegExp("^|$))","i"),/^$/,!0],[new RegExp(s.source+"\\s*$"),/^$/,!1]];e.exports=function(e,t,r,n){var s,i,a,u,c=e.bMarks[t]+e.tShift[t],l=e.eMarks[t];if(e.sCount[t]-e.blkIndent>=4)return!1;if(!e.md.options.html)return!1;if(60!==e.src.charCodeAt(c))return!1;for(u=e.src.slice(c,l),s=0;s3||e.sCount[u]<0)){for(n=!1,s=0,o=c.length;s0&&this.level++,this.tokens.push(s),s},o.prototype.isEmpty=function(e){return this.bMarks[e]+this.tShift[e]>=this.eMarks[e]},o.prototype.skipEmptyLines=function(e){for(var t=this.lineMax;et;)if(!s(this.src.charCodeAt(--e)))return e+1;return e},o.prototype.skipChars=function(e,t){for(var r=this.src.length;er;)if(t!==this.src.charCodeAt(--e))return e+1;return e},o.prototype.getLines=function(e,t,r,n){var o,i,a,u,c,l,p,h=e;if(e>=t)return"";for(l=new Array(t-e),o=0;hr?new Array(i-r+1).join(" ")+this.src.slice(u,c):this.src.slice(u,c)}return l.join("")},o.prototype.Token=n,e.exports=o},function(e,t,r){"use strict";var n=r(1),s=[["text",r(50)],["newline",r(51)],["escape",r(52)],["backticks",r(53)],["strikethrough",r(7).tokenize],["emphasis",r(8).tokenize],["link",r(54)],["image",r(55)],["autolink",r(56)],["html_inline",r(57)],["entity",r(58)]],o=[["balance_pairs",r(59)],["strikethrough",r(7).postProcess],["emphasis",r(8).postProcess],["text_collapse",r(60)]];function i(){var e;for(this.ruler=new n,e=0;e=o)break}else e.pending+=e.src[e.pos++]}e.pending&&e.pushPending()},i.prototype.parse=function(e,t,r,n){var s,o,i,a=new this.State(e,t,r,n);for(this.tokenize(a),i=(o=this.ruler2.getRules("")).length,s=0;s=0&&32===e.pending.charCodeAt(r)?r>=1&&32===e.pending.charCodeAt(r-1)?(e.pending=e.pending.replace(/ +$/,""),e.push("hardbreak","br",0)):(e.pending=e.pending.slice(0,-1),e.push("softbreak","br",0)):e.push("softbreak","br",0)),o++;o?@[]^_`{|}~-".split("").forEach((function(e){s[e.charCodeAt(0)]=1})),e.exports=function(e,t){var r,o=e.pos,i=e.posMax;if(92!==e.src.charCodeAt(o))return!1;if(++o=m)return!1;if(g=c,(l=e.md.helpers.parseLinkDestination(e.src,c,e.posMax)).ok){for(h=e.md.normalizeLink(l.str),e.md.validateLink(h)?c=l.pos:h="",g=c;c=m||41!==e.src.charCodeAt(c))&&(_=!0),c++}if(_){if(void 0===e.env.references)return!1;if(c=0?i=e.src.slice(g,c++):c=a+1):c=a+1,i||(i=e.src.slice(u,a)),!(p=e.env.references[n(i)]))return e.pos=d,!1;h=p.href,f=p.title}return t||(e.pos=u,e.posMax=a,e.push("link_open","a",1).attrs=r=[["href",h]],f&&r.push(["title",f]),e.md.inline.tokenize(e),e.push("link_close","a",-1)),e.pos=c,e.posMax=m,!0}},function(e,t,r){"use strict";var n=r(0).normalizeReference,s=r(0).isSpace;e.exports=function(e,t){var r,o,i,a,u,c,l,p,h,f,d,m,g,_="",b=e.pos,k=e.posMax;if(33!==e.src.charCodeAt(e.pos))return!1;if(91!==e.src.charCodeAt(e.pos+1))return!1;if(c=e.pos+2,(u=e.md.helpers.parseLinkLabel(e,e.pos+1,!1))<0)return!1;if((l=u+1)=k)return!1;for(g=l,(h=e.md.helpers.parseLinkDestination(e.src,l,e.posMax)).ok&&(_=e.md.normalizeLink(h.str),e.md.validateLink(_)?l=h.pos:_=""),g=l;l=k||41!==e.src.charCodeAt(l))return e.pos=b,!1;l++}else{if(void 0===e.env.references)return!1;if(l=0?a=e.src.slice(g,l++):l=u+1):l=u+1,a||(a=e.src.slice(c,u)),!(p=e.env.references[n(a)]))return e.pos=b,!1;_=p.href,f=p.title}return t||(i=e.src.slice(c,u),e.md.inline.parse(i,e.md,e.env,m=[]),(d=e.push("image","img",0)).attrs=r=[["src",_],["alt",""]],d.children=m,d.content=i,f&&r.push(["title",f])),e.pos=l,e.posMax=k,!0}},function(e,t,r){"use strict";var n=/^([a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)$/,s=/^([a-zA-Z][a-zA-Z0-9+.\-]{1,31}):([^<>\x00-\x20]*)$/;e.exports=function(e,t){var r,o,i,a,u,c,l=e.pos;if(60!==e.src.charCodeAt(l))return!1;for(u=e.pos,c=e.posMax;;){if(++l>=c)return!1;if(60===(a=e.src.charCodeAt(l)))return!1;if(62===a)break}return r=e.src.slice(u+1,l),s.test(r)?(o=e.md.normalizeLink(r),!!e.md.validateLink(o)&&(t||((i=e.push("link_open","a",1)).attrs=[["href",o]],i.markup="autolink",i.info="auto",(i=e.push("text","",0)).content=e.md.normalizeLinkText(r),(i=e.push("link_close","a",-1)).markup="autolink",i.info="auto"),e.pos+=r.length+2,!0)):!!n.test(r)&&(o=e.md.normalizeLink("mailto:"+r),!!e.md.validateLink(o)&&(t||((i=e.push("link_open","a",1)).attrs=[["href",o]],i.markup="autolink",i.info="auto",(i=e.push("text","",0)).content=e.md.normalizeLinkText(r),(i=e.push("link_close","a",-1)).markup="autolink",i.info="auto"),e.pos+=r.length+2,!0))}},function(e,t,r){"use strict";var n=r(6).HTML_TAG_RE;e.exports=function(e,t){var r,s,o,i=e.pos;return!!e.md.options.html&&(o=e.posMax,!(60!==e.src.charCodeAt(i)||i+2>=o)&&(!(33!==(r=e.src.charCodeAt(i+1))&&63!==r&&47!==r&&!function(e){var t=32|e;return t>=97&&t<=122}(r))&&(!!(s=e.src.slice(i).match(n))&&(t||(e.push("html_inline","",0).content=e.src.slice(i,i+s[0].length)),e.pos+=s[0].length,!0))))}},function(e,t,r){"use strict";var n=r(3),s=r(0).has,o=r(0).isValidEntityCode,i=r(0).fromCodePoint,a=/^&#((?:x[a-f0-9]{1,6}|[0-9]{1,7}));/i,u=/^&([a-z][a-z0-9]{1,31});/i;e.exports=function(e,t){var r,c,l=e.pos,p=e.posMax;if(38!==e.src.charCodeAt(l))return!1;if(l+1i;n-=o.jump+1)if((o=t[n]).marker===s.marker&&o.open&&o.end<0&&(u=!1,(o.close||s.open)&&(o.length+s.length)%3==0&&(o.length%3==0&&s.length%3==0||(u=!0)),!u)){c=n>0&&!t[n-1].open?t[n-1].jump+1:0,s.jump=r-n+c,s.open=!1,o.end=r,o.jump=c,o.close=!1,a=-1;break}-1!==a&&(l[s.marker][(s.length||0)%3]=a)}}e.exports=function(e){var t,r=e.tokens_meta,s=e.tokens_meta.length;for(n(0,e.delimiters),t=0;t0&&n++,"text"===s[t].type&&t+10&&(this.level++,this._prev_delimiters.push(this.delimiters),this.delimiters=[],o={delimiters:this.delimiters}),this.pendingLevel=this.level,this.tokens.push(s),this.tokens_meta.push(o),s},a.prototype.scanDelims=function(e,t){var r,n,a,u,c,l,p,h,f,d=e,m=!0,g=!0,_=this.posMax,b=this.src.charCodeAt(e);for(r=e>0?this.src.charCodeAt(e-1):32;d<_&&this.src.charCodeAt(d)===b;)d++;return a=d-e,n=d<_?this.src.charCodeAt(d):32,p=i(r)||o(String.fromCharCode(r)),f=i(n)||o(String.fromCharCode(n)),l=s(r),(h=s(n))?m=!1:f&&(l||p||(m=!1)),l?g=!1:p&&(h||f||(g=!1)),t?(u=m,c=g):(u=m&&(!g||p),c=g&&(!m||f)),{can_open:u,can_close:c,length:a}},a.prototype.Token=n,e.exports=a},function(e,t,r){"use strict";function n(e){var t=Array.prototype.slice.call(arguments,1);return t.forEach((function(t){t&&Object.keys(t).forEach((function(r){e[r]=t[r]}))})),e}function s(e){return Object.prototype.toString.call(e)}function o(e){return"[object Function]"===s(e)}function i(e){return e.replace(/[.?*+^$[\]\\(){}|-]/g,"\\$&")}var a={fuzzyLink:!0,fuzzyEmail:!0,fuzzyIP:!1};var u={"http:":{validate:function(e,t,r){var n=e.slice(t);return r.re.http||(r.re.http=new RegExp("^\\/\\/"+r.re.src_auth+r.re.src_host_port_strict+r.re.src_path,"i")),r.re.http.test(n)?n.match(r.re.http)[0].length:0}},"https:":"http:","ftp:":"http:","//":{validate:function(e,t,r){var n=e.slice(t);return r.re.no_http||(r.re.no_http=new RegExp("^"+r.re.src_auth+"(?:localhost|(?:(?:"+r.re.src_domain+")\\.)+"+r.re.src_domain_root+")"+r.re.src_port+r.re.src_host_terminator+r.re.src_path,"i")),r.re.no_http.test(n)?t>=3&&":"===e[t-3]||t>=3&&"/"===e[t-3]?0:n.match(r.re.no_http)[0].length:0}},"mailto:":{validate:function(e,t,r){var n=e.slice(t);return r.re.mailto||(r.re.mailto=new RegExp("^"+r.re.src_email_name+"@"+r.re.src_host_strict,"i")),r.re.mailto.test(n)?n.match(r.re.mailto)[0].length:0}}},c="biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф".split("|");function l(e){var t=e.re=r(63)(e.__opts__),n=e.__tlds__.slice();function a(e){return e.replace("%TLDS%",t.src_tlds)}e.onCompile(),e.__tlds_replaced__||n.push("a[cdefgilmnoqrstuwxz]|b[abdefghijmnorstvwyz]|c[acdfghiklmnoruvwxyz]|d[ejkmoz]|e[cegrstu]|f[ijkmor]|g[abdefghilmnpqrstuwy]|h[kmnrtu]|i[delmnoqrst]|j[emop]|k[eghimnprwyz]|l[abcikrstuvy]|m[acdeghklmnopqrstuvwxyz]|n[acefgilopruz]|om|p[aefghklmnrstwy]|qa|r[eosuw]|s[abcdeghijklmnortuvxyz]|t[cdfghjklmnortvwz]|u[agksyz]|v[aceginu]|w[fs]|y[et]|z[amw]"),n.push(t.src_xn),t.src_tlds=n.join("|"),t.email_fuzzy=RegExp(a(t.tpl_email_fuzzy),"i"),t.link_fuzzy=RegExp(a(t.tpl_link_fuzzy),"i"),t.link_no_ip_fuzzy=RegExp(a(t.tpl_link_no_ip_fuzzy),"i"),t.host_fuzzy_test=RegExp(a(t.tpl_host_fuzzy_test),"i");var u=[];function c(e,t){throw new Error('(LinkifyIt) Invalid schema "'+e+'": '+t)}e.__compiled__={},Object.keys(e.__schemas__).forEach((function(t){var r=e.__schemas__[t];if(null!==r){var n={validate:null,link:null};if(e.__compiled__[t]=n,"[object Object]"===s(r))return!function(e){return"[object RegExp]"===s(e)}(r.validate)?o(r.validate)?n.validate=r.validate:c(t,r):n.validate=function(e){return function(t,r){var n=t.slice(r);return e.test(n)?n.match(e)[0].length:0}}(r.validate),void(o(r.normalize)?n.normalize=r.normalize:r.normalize?c(t,r):n.normalize=function(e,t){t.normalize(e)});!function(e){return"[object String]"===s(e)}(r)?c(t,r):u.push(t)}})),u.forEach((function(t){e.__compiled__[e.__schemas__[t]]&&(e.__compiled__[t].validate=e.__compiled__[e.__schemas__[t]].validate,e.__compiled__[t].normalize=e.__compiled__[e.__schemas__[t]].normalize)})),e.__compiled__[""]={validate:null,normalize:function(e,t){t.normalize(e)}};var l=Object.keys(e.__compiled__).filter((function(t){return t.length>0&&e.__compiled__[t]})).map(i).join("|");e.re.schema_test=RegExp("(^|(?!_)(?:[><|]|"+t.src_ZPCc+"))("+l+")","i"),e.re.schema_search=RegExp("(^|(?!_)(?:[><|]|"+t.src_ZPCc+"))("+l+")","ig"),e.re.pretest=RegExp("("+e.re.schema_test.source+")|("+e.re.host_fuzzy_test.source+")|@","i"),function(e){e.__index__=-1,e.__text_cache__=""}(e)}function p(e,t){var r=e.__index__,n=e.__last_index__,s=e.__text_cache__.slice(r,n);this.schema=e.__schema__.toLowerCase(),this.index=r+t,this.lastIndex=n+t,this.raw=s,this.text=s,this.url=s}function h(e,t){var r=new p(e,t);return e.__compiled__[r.schema].normalize(r,e),r}function f(e,t){if(!(this instanceof f))return new f(e,t);var r;t||(r=e,Object.keys(r||{}).reduce((function(e,t){return e||a.hasOwnProperty(t)}),!1)&&(t=e,e={})),this.__opts__=n({},a,t),this.__index__=-1,this.__last_index__=-1,this.__schema__="",this.__text_cache__="",this.__schemas__=n({},u,e),this.__compiled__={},this.__tlds__=c,this.__tlds_replaced__=!1,this.re={},l(this)}f.prototype.add=function(e,t){return this.__schemas__[e]=t,l(this),this},f.prototype.set=function(e){return this.__opts__=n(this.__opts__,e),this},f.prototype.test=function(e){if(this.__text_cache__=e,this.__index__=-1,!e.length)return!1;var t,r,n,s,o,i,a,u;if(this.re.schema_test.test(e))for((a=this.re.schema_search).lastIndex=0;null!==(t=a.exec(e));)if(s=this.testSchemaAt(e,t[2],a.lastIndex)){this.__schema__=t[2],this.__index__=t.index+t[1].length,this.__last_index__=t.index+t[0].length+s;break}return this.__opts__.fuzzyLink&&this.__compiled__["http:"]&&(u=e.search(this.re.host_fuzzy_test))>=0&&(this.__index__<0||u=0&&null!==(n=e.match(this.re.email_fuzzy))&&(o=n.index+n[1].length,i=n.index+n[0].length,(this.__index__<0||othis.__last_index__)&&(this.__schema__="mailto:",this.__index__=o,this.__last_index__=i)),this.__index__>=0},f.prototype.pretest=function(e){return this.re.pretest.test(e)},f.prototype.testSchemaAt=function(e,t,r){return this.__compiled__[t.toLowerCase()]?this.__compiled__[t.toLowerCase()].validate(e,r,this):0},f.prototype.match=function(e){var t=0,r=[];this.__index__>=0&&this.__text_cache__===e&&(r.push(h(this,t)),t=this.__last_index__);for(var n=t?e.slice(t):e;this.test(n);)r.push(h(this,t)),n=n.slice(this.__last_index__),t+=this.__last_index__;return r.length?r:null},f.prototype.tlds=function(e,t){return e=Array.isArray(e)?e:[e],t?(this.__tlds__=this.__tlds__.concat(e).sort().filter((function(e,t,r){return e!==r[t-1]})).reverse(),l(this),this):(this.__tlds__=e.slice(),this.__tlds_replaced__=!0,l(this),this)},f.prototype.normalize=function(e){e.schema||(e.url="http://"+e.url),"mailto:"!==e.schema||/^mailto:/i.test(e.url)||(e.url="mailto:"+e.url)},f.prototype.onCompile=function(){},e.exports=f},function(e,t,r){"use strict";e.exports=function(e){var t={};t.src_Any=r(64).source,t.src_Cc=r(65).source,t.src_Z=r(66).source,t.src_P=r(67).source,t.src_ZPCc=[t.src_Z,t.src_P,t.src_Cc].join("|"),t.src_ZCc=[t.src_Z,t.src_Cc].join("|");return t.src_pseudo_letter="(?:(?![><|]|"+t.src_ZPCc+")"+t.src_Any+")",t.src_ip4="(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)",t.src_auth="(?:(?:(?!"+t.src_ZCc+"|[@/\\[\\]()]).)+@)?",t.src_port="(?::(?:6(?:[0-4]\\d{3}|5(?:[0-4]\\d{2}|5(?:[0-2]\\d|3[0-5])))|[1-5]?\\d{1,4}))?",t.src_host_terminator="(?=$|[><|]|"+t.src_ZPCc+")(?!-|_|:\\d|\\.-|\\.(?!$|"+t.src_ZPCc+"))",t.src_path="(?:[/?#](?:(?!"+t.src_ZCc+"|[><|]|[()[\\]{}.,\"'?!\\-]).|\\[(?:(?!"+t.src_ZCc+"|\\]).)*\\]|\\((?:(?!"+t.src_ZCc+"|[)]).)*\\)|\\{(?:(?!"+t.src_ZCc+'|[}]).)*\\}|\\"(?:(?!'+t.src_ZCc+'|["]).)+\\"|\\\'(?:(?!'+t.src_ZCc+"|[']).)+\\'|\\'(?="+t.src_pseudo_letter+"|[-]).|\\.{2,}[a-zA-Z0-9%/&]|\\.(?!"+t.src_ZCc+"|[.]).|"+(e&&e["---"]?"\\-(?!--(?:[^-]|$))(?:-*)|":"\\-+|")+"\\,(?!"+t.src_ZCc+").|\\!+(?!"+t.src_ZCc+"|[!]).|\\?(?!"+t.src_ZCc+"|[?]).)+|\\/)?",t.src_email_name='[\\-;:&=\\+\\$,\\.a-zA-Z0-9_][\\-;:&=\\+\\$,\\"\\.a-zA-Z0-9_]*',t.src_xn="xn--[a-z0-9\\-]{1,59}",t.src_domain_root="(?:"+t.src_xn+"|"+t.src_pseudo_letter+"{1,63})",t.src_domain="(?:"+t.src_xn+"|(?:"+t.src_pseudo_letter+")|(?:"+t.src_pseudo_letter+"(?:-|"+t.src_pseudo_letter+"){0,61}"+t.src_pseudo_letter+"))",t.src_host="(?:(?:(?:(?:"+t.src_domain+")\\.)*"+t.src_domain+"))",t.tpl_host_fuzzy="(?:"+t.src_ip4+"|(?:(?:(?:"+t.src_domain+")\\.)+(?:%TLDS%)))",t.tpl_host_no_ip_fuzzy="(?:(?:(?:"+t.src_domain+")\\.)+(?:%TLDS%))",t.src_host_strict=t.src_host+t.src_host_terminator,t.tpl_host_fuzzy_strict=t.tpl_host_fuzzy+t.src_host_terminator,t.src_host_port_strict=t.src_host+t.src_port+t.src_host_terminator,t.tpl_host_port_fuzzy_strict=t.tpl_host_fuzzy+t.src_port+t.src_host_terminator,t.tpl_host_port_no_ip_fuzzy_strict=t.tpl_host_no_ip_fuzzy+t.src_port+t.src_host_terminator,t.tpl_host_fuzzy_test="localhost|www\\.|\\.\\d{1,3}\\.|(?:\\.(?:%TLDS%)(?:"+t.src_ZPCc+"|>|$))",t.tpl_email_fuzzy='(^|[><|]|"|\\(|'+t.src_ZCc+")("+t.src_email_name+"@"+t.tpl_host_fuzzy_strict+")",t.tpl_link_fuzzy="(^|(?![.:/\\-_@])(?:[$+<=>^`||]|"+t.src_ZPCc+"))((?![$+<=>^`||])"+t.tpl_host_port_fuzzy_strict+t.src_path+")",t.tpl_link_no_ip_fuzzy="(^|(?![.:/\\-_@])(?:[$+<=>^`||]|"+t.src_ZPCc+"))((?![$+<=>^`||])"+t.tpl_host_port_no_ip_fuzzy_strict+t.src_path+")",t}},function(e,t){e.exports=/[\0-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/},function(e,t){e.exports=/[\0-\x1F\x7F-\x9F]/},function(e,t){e.exports=/[ \xA0\u1680\u2000-\u200A\u202F\u205F\u3000]/},function(e,t){e.exports=/[!-#%-\*,-/:;\?@\[-\]_\{\}\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u0AF0\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E44\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC9\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD805[\uDC4B-\uDC4F\uDC5B\uDC5D\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDE60-\uDE6C\uDF3C-\uDF3E]|\uD807[\uDC41-\uDC45\uDC70\uDC71]|\uD809[\uDC70-\uDC74]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]|\uD83A[\uDD5E\uDD5F]/},function(e,t,r){(function(e,n){var s;/*! https://mths.be/punycode v1.4.1 by @mathias */!function(o){t&&t.nodeType,e&&e.nodeType;var i="object"==typeof n&&n;i.global!==i&&i.window!==i&&i.self;var a,u=2147483647,c=/^xn--/,l=/[^\x20-\x7E]/,p=/[\x2E\u3002\uFF0E\uFF61]/g,h={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},f=Math.floor,d=String.fromCharCode;function m(e){throw new RangeError(h[e])}function g(e,t){for(var r=e.length,n=[];r--;)n[r]=t(e[r]);return n}function _(e,t){var r=e.split("@"),n="";return r.length>1&&(n=r[0]+"@",e=r[1]),n+g((e=e.replace(p,".")).split("."),t).join(".")}function b(e){for(var t,r,n=[],s=0,o=e.length;s=55296&&t<=56319&&s65535&&(t+=d((e-=65536)>>>10&1023|55296),e=56320|1023&e),t+=d(e)})).join("")}function C(e,t){return e+22+75*(e<26)-((0!=t)<<5)}function v(e,t,r){var n=0;for(e=r?f(e/700):e>>1,e+=f(e/t);e>455;n+=36)e=f(e/35);return f(n+36*e/(e+38))}function D(e){var t,r,n,s,o,i,a,c,l,p,h,d=[],g=e.length,_=0,b=128,C=72;for((r=e.lastIndexOf("-"))<0&&(r=0),n=0;n=128&&m("not-basic"),d.push(e.charCodeAt(n));for(s=r>0?r+1:0;s=g&&m("invalid-input"),((c=(h=e.charCodeAt(s++))-48<10?h-22:h-65<26?h-65:h-97<26?h-97:36)>=36||c>f((u-_)/i))&&m("overflow"),_+=c*i,!(c<(l=a<=C?1:a>=C+26?26:a-C));a+=36)i>f(u/(p=36-l))&&m("overflow"),i*=p;C=v(_-o,t=d.length+1,0==o),f(_/t)>u-b&&m("overflow"),b+=f(_/t),_%=t,d.splice(_++,0,b)}return k(d)}function A(e){var t,r,n,s,o,i,a,c,l,p,h,g,_,k,D,A=[];for(g=(e=b(e)).length,t=128,r=0,o=72,i=0;i=t&&hf((u-r)/(_=n+1))&&m("overflow"),r+=(a-t)*_,t=a,i=0;iu&&m("overflow"),h==t){for(c=r,l=36;!(c<(p=l<=o?1:l>=o+26?26:l-o));l+=36)D=c-p,k=36-p,A.push(d(C(p+D%k,0))),c=f(D/k);A.push(d(C(c,0))),o=v(r,_,n==s),r=0,++n}++r,++t}return A.join("")}a={version:"1.4.1",ucs2:{decode:b,encode:k},decode:D,encode:A,toASCII:function(e){return _(e,(function(e){return l.test(e)?"xn--"+A(e):e}))},toUnicode:function(e){return _(e,(function(e){return c.test(e)?D(e.slice(4).toLowerCase()):e}))}},void 0===(s=function(){return a}.call(t,r,t,e))||(e.exports=s)}()}).call(this,r(69)(e),r(70))},function(e,t){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children||(e.children=[]),Object.defineProperty(e,"loaded",{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,"id",{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}},function(e,t){var r;r=function(){return this}();try{r=r||new Function("return this")()}catch(e){"object"==typeof window&&(r=window)}e.exports=r},function(e,t,r){"use strict";e.exports={options:{html:!1,xhtmlOut:!1,breaks:!1,langPrefix:"language-",linkify:!1,typographer:!1,quotes:"“”‘’",highlight:null,maxNesting:100},components:{core:{},block:{},inline:{}}}},function(e,t,r){"use strict";e.exports={options:{html:!1,xhtmlOut:!1,breaks:!1,langPrefix:"language-",linkify:!1,typographer:!1,quotes:"“”‘’",highlight:null,maxNesting:20},components:{core:{rules:["normalize","block","inline"]},block:{rules:["paragraph"]},inline:{rules:["text"],rules2:["balance_pairs","text_collapse"]}}}},function(e,t,r){"use strict";e.exports={options:{html:!0,xhtmlOut:!0,breaks:!1,langPrefix:"language-",linkify:!1,typographer:!1,quotes:"“”‘’",highlight:null,maxNesting:20},components:{core:{rules:["normalize","block","inline"]},block:{rules:["blockquote","code","fence","heading","hr","html_block","lheading","list","reference","paragraph"]},inline:{rules:["autolink","backticks","emphasis","entity","escape","html_inline","image","link","newline","text"],rules2:["balance_pairs","emphasis","text_collapse"]}}}}]); \ No newline at end of file diff --git a/extensions/markdown-language-features/notebook/index.ts b/extensions/markdown-language-features/notebook/index.ts index 035ff83ae15..1dcb131ae64 100644 --- a/extensions/markdown-language-features/notebook/index.ts +++ b/extensions/markdown-language-features/notebook/index.ts @@ -11,7 +11,9 @@ type extendMarkdownItFnType = ( ); (function () { - const markdownIt = new MarkdownIt(); + const markdownIt = new MarkdownIt({ + html: true + }); (globalThis as any).extendMarkdownIt = ((f: (md: MarkdownIt.MarkdownIt) => void) => { f(markdownIt); diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index cf7c8b291a0..8da6a42f808 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -322,11 +322,12 @@ ] }, "scripts": { - "compile": "gulp compile-extension:markdown-language-features && npm run build-preview", + "compile": "gulp compile-extension:markdown-language-features && npm run build-preview && npm run build-notebook", "watch": "npm run build-preview && gulp watch-extension:markdown-language-features", "vscode:prepublish": "npm run build-ext && npm run build-preview", "build-ext": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:markdown-language-features ./tsconfig.json", "build-preview": "npx webpack-cli --mode production", + "build-notebook": "npx webpack-cli --config webpack.notebook --mode production", "compile-web": "npx webpack-cli --config extension-browser.webpack.config --mode none", "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, 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 c36b49cb8d0..b0631344346 100644 --- a/extensions/microsoft-authentication/src/AADHelper.ts +++ b/extensions/microsoft-authentication/src/AADHelper.ts @@ -144,7 +144,7 @@ export class AzureActiveDirectoryService { this.pollForReconnect(session.id, session.refreshToken, session.scope); } } else { - await this.logout(session.id); + await this.removeSession(session.id); } } }); @@ -193,7 +193,7 @@ export class AzureActiveDirectoryService { if (e.message === REFRESH_NETWORK_FAILURE) { // Ignore, will automatically retry on next poll. } else { - await this.logout(session.id); + await this.removeSession(session.id); } } } @@ -202,7 +202,7 @@ export class AzureActiveDirectoryService { promises = promises.concat(this._tokens.map(async token => { const matchesExisting = sessions.some(session => token.scope === session.scope && token.sessionId === session.id); if (!matchesExisting) { - await this.logout(token.sessionId); + await this.removeSession(token.sessionId); removed.push(this.convertToSessionSync(token)); } })); @@ -300,7 +300,17 @@ export class AzureActiveDirectoryService { return Promise.all(this._tokens.map(token => this.convertToSession(token))); } - public async login(scope: 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))); + } + + public async createSession(scope: string): Promise { Logger.info('Logging in...'); if (!scope.includes('offline_access')) { Logger.info('Warning: The \'offline_access\' scope was not included, so the generated token will not be able to be refreshed.'); @@ -501,7 +511,7 @@ export class AzureActiveDirectoryService { this.pollForReconnect(token.sessionId, token.refreshToken, token.scope); } } else { - await this.logout(token.sessionId); + await this.removeSession(token.sessionId); onDidChangeSessions.fire({ added: [], removed: [this.convertToSessionSync(token)], changed: [] }); } } @@ -681,7 +691,7 @@ export class AzureActiveDirectoryService { }); } - public async logout(sessionId: string): Promise { + public async removeSession(sessionId: string): Promise { Logger.info(`Logging out of session '${sessionId}'`); const token = this.removeInMemorySessionData(sessionId); let session: vscode.AuthenticationSession | undefined; diff --git a/extensions/microsoft-authentication/src/extension.ts b/extensions/microsoft-authentication/src/extension.ts index dbf93db8bda..41b8690f344 100644 --- a/extensions/microsoft-authentication/src/extension.ts +++ b/extensions/microsoft-authentication/src/extension.ts @@ -20,15 +20,15 @@ export async function activate(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.authentication.registerAuthenticationProvider('microsoft', 'Microsoft', { onDidChangeSessions: onDidChangeSessions.event, - getSessions: () => Promise.resolve(loginService.sessions), - login: async (scopes: string[]) => { + getSessions: (scopes: string[]) => loginService.getSessions(scopes), + createSession: async (scopes: string[]) => { try { /* __GDPR__ "login" : { } */ telemetryReporter.sendTelemetryEvent('login'); - const session = await loginService.login(scopes.sort().join(' ')); + const session = await loginService.createSession(scopes.sort().join(' ')); onDidChangeSessions.fire({ added: [session], removed: [], changed: [] }); return session; } catch (e) { @@ -40,14 +40,14 @@ export async function activate(context: vscode.ExtensionContext) { throw e; } }, - logout: async (id: string) => { + removeSession: async (id: string) => { try { /* __GDPR__ "logout" : { } */ telemetryReporter.sendTelemetryEvent('logout'); - const session = await loginService.logout(id); + const session = await loginService.removeSession(id); if (session) { onDidChangeSessions.fire({ added: [], removed: [session], changed: [] }); } 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..e0a8912e0f6 --- /dev/null +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts @@ -0,0 +1,296 @@ +/*--------------------------------------------------------------------------------------------- + * 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]); + }); + + test('workspace edit API (appendNotebookCellOutput, replaceCellOutput, event)', async function () { + const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest'); + const document = await vscode.notebook.openNotebookDocument(uri); + + const outputChangeEvent = utils.asPromise(vscode.notebook.onDidChangeCellOutputs); + const edit = new vscode.WorkspaceEdit(); + const firstCellOutput = new vscode.NotebookCellOutput([new vscode.NotebookCellOutputItem('foo', 'bar')]); + edit.appendNotebookCellOutput(document.uri, 0, [firstCellOutput]); + const success = await vscode.workspace.applyEdit(edit); + const data = await outputChangeEvent; + + assert.strictEqual(success, true); + assert.strictEqual(document.cells.length, 1); + assert.strictEqual(document.cells[0].outputs.length, 1); + assert.deepStrictEqual(document.cells[0].outputs, [firstCellOutput]); + + assert.strictEqual(data.document === document, true); + assert.strictEqual(data.cells.length, 1); + assert.strictEqual(data.cells[0].outputs.length, 1); + assert.deepStrictEqual(data.cells[0].outputs, [firstCellOutput]); + + + { + const outputChangeEvent = utils.asPromise(vscode.notebook.onDidChangeCellOutputs); + const edit = new vscode.WorkspaceEdit(); + const secondCellOutput = new vscode.NotebookCellOutput([new vscode.NotebookCellOutputItem('foo', 'baz')]); + edit.appendNotebookCellOutput(document.uri, 0, [secondCellOutput]); + const success = await vscode.workspace.applyEdit(edit); + const data = await outputChangeEvent; + + assert.strictEqual(success, true); + assert.strictEqual(document.cells.length, 1); + assert.strictEqual(document.cells[0].outputs.length, 2); + assert.deepStrictEqual(document.cells[0].outputs, [firstCellOutput, secondCellOutput]); + + assert.strictEqual(data.document === document, true); + assert.strictEqual(data.cells.length, 1); + assert.strictEqual(data.cells[0].outputs.length, 2); + assert.deepStrictEqual(data.cells[0].outputs, [firstCellOutput, secondCellOutput]); + } + + { + const outputChangeEvent = utils.asPromise(vscode.notebook.onDidChangeCellOutputs); + const edit = new vscode.WorkspaceEdit(); + const thirdOutput = new vscode.NotebookCellOutput([new vscode.NotebookCellOutputItem('foo', 'baz1')]); + edit.replaceNotebookCellOutput(document.uri, 0, [thirdOutput]); + const success = await vscode.workspace.applyEdit(edit); + const data = await outputChangeEvent; + + assert.strictEqual(success, true); + assert.strictEqual(document.cells.length, 1); + assert.strictEqual(document.cells[0].outputs.length, 1); + assert.deepStrictEqual(document.cells[0].outputs, [thirdOutput]); + + assert.strictEqual(data.document === document, true); + assert.strictEqual(data.cells.length, 1); + assert.strictEqual(data.cells[0].outputs.length, 1); + assert.deepStrictEqual(data.cells[0].outputs, [thirdOutput]); + } + }); +}); diff --git a/extensions/vscode-notebook-tests/src/notebook.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts similarity index 58% rename from extensions/vscode-notebook-tests/src/notebook.test.ts rename to extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts index ca66acd63b6..06fff1fafb0 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, saveAllEditors } 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; } @@ -66,7 +27,7 @@ async function saveFileAndCloseAll(resource: vscode.Uri) { }); }); await vscode.commands.executeCommand('workbench.action.files.save'); - await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + await closeAllEditors(); await documentClosed; } @@ -83,7 +44,7 @@ async function saveAllFilesAndCloseAll(resource: vscode.Uri | undefined) { }); }); await vscode.commands.executeCommand('workbench.action.files.saveAll'); - await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + await closeAllEditors(); await documentClosed; } @@ -100,101 +61,148 @@ 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); } -function assertInitalState() { - // no-op unless we figure out why some documents are opened after the editor is closed +suite('Notebook API tests', function () { - // assert.strictEqual(vscode.window.activeNotebookEditor, undefined); - // assert.strictEqual(vscode.notebook.notebookDocuments.length, 0); - // assert.strictEqual(vscode.notebook.visibleNotebookEditors.length, 0); -} + const disposables: vscode.Disposable[] = []; -suite('Notebook API tests', () => { - // test.only('crash', async function () { - // for (let i = 0; i < 200; i++) { - // let resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb')); - // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - // await vscode.commands.executeCommand('workbench.action.revertAndCloseActiveEditor'); + suiteTeardown(async function () { + await revertAllDirty(); + await closeAllEditors(); - // resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './empty.vsctestnb')); - // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - // await vscode.commands.executeCommand('workbench.action.revertAndCloseActiveEditor'); - // } - // }); - - // test.only('crash', async function () { - // for (let i = 0; i < 200; i++) { - // let resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb')); - // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - // await vscode.commands.executeCommand('workbench.action.files.save'); - // await vscode.commands.executeCommand('workbench.action.closeAllEditors'); - // resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './empty.vsctestnb')); - // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - // await vscode.commands.executeCommand('workbench.action.files.save'); - // await vscode.commands.executeCommand('workbench.action.closeAllEditors'); - // } - // }); - - test('document open/close event', async function () { - assertInitalState(); - - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); - const firstDocumentOpen = getEventOncePromise(vscode.notebook.onDidOpenNotebookDocument); - await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - await firstDocumentOpen; - - const firstDocumentClose = getEventOncePromise(vscode.notebook.onDidCloseNotebookDocument); - await vscode.commands.executeCommand('workbench.action.closeAllEditors'); - await firstDocumentClose; + disposeAll(disposables); + disposables.length = 0; }); - test('notebook open/close, all cell-documents are ready', async function () { - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + 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 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') { + 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 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'); + + 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('shared document in notebook editors', async function () { - assertInitalState(); - - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile(undefined, undefined, '.vsctestnb'); let counter = 0; const disposables: vscode.Disposable[] = []; disposables.push(vscode.notebook.onDidOpenNotebookDocument(() => { @@ -208,29 +216,25 @@ suite('Notebook API tests', () => { await splitEditor(); assert.strictEqual(counter, 1); - await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + await closeAllEditors(); assert.strictEqual(counter, 0); disposables.forEach(d => d.dispose()); }); 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); - await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + const firstEditorClose = asPromise(vscode.window.onDidChangeVisibleNotebookEditors); + await closeAllEditors(); await firstEditorClose; }); 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(() => { @@ -243,17 +247,15 @@ suite('Notebook API tests', () => { await splitEditor(); assert.strictEqual(count, 2); - await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + await closeAllEditors(); assert.strictEqual(count, 0); }); 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 +271,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 +292,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 +301,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, { @@ -317,13 +319,11 @@ suite('Notebook API tests', () => { // language: 'markdown' // }); - await vscode.commands.executeCommand('workbench.action.files.save'); - await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + await saveAllFilesAndCloseAll(undefined); }); 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 +331,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, { @@ -352,20 +352,17 @@ suite('Notebook API tests', () => { ] }); - await vscode.commands.executeCommand('workbench.action.files.save'); - await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + await saveAllEditors(); + await closeAllEditors(); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); const firstEditor = vscode.window.activeNotebookEditor; assert.strictEqual(firstEditor?.document.cells.length, 1); - - await vscode.commands.executeCommand('workbench.action.files.save'); - await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + await saveAllFilesAndCloseAll(undefined); }); 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 +374,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,136 +383,58 @@ 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); assert.strictEqual(vscode.window.visibleNotebookEditors.length, 2); assert.strictEqual(secondEditor && vscode.window.visibleNotebookEditors.indexOf(secondEditor) >= 0, true); - await vscode.commands.executeCommand('workbench.action.files.save'); - await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + await saveAllFilesAndCloseAll(undefined); }); 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 () { - assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + test('change cell language', async function () { + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - const cellsChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); - await vscode.window.activeNotebookEditor!.edit(editBuilder => { - editBuilder.replaceCells(1, 0, [{ cellKind: vscode.CellKind.Code, language: 'javascript', source: 'test 2', outputs: [], metadata: undefined }]); + 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'); }); - const cellChangeEventRet = await cellsChangeEvent; - assert.strictEqual(cellChangeEventRet.document === vscode.window.activeNotebookEditor?.document, true); - assert.strictEqual(cellChangeEventRet.document.isDirty, true); - assert.strictEqual(cellChangeEventRet.changes.length, 1); - assert.strictEqual(cellChangeEventRet.changes[0].start, 1); - assert.strictEqual(cellChangeEventRet.changes[0].deletedCount, 0); - assert.strictEqual(cellChangeEventRet.changes[0].items[0] === vscode.window.activeNotebookEditor!.document.cells[1], true); + // 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 (replaceOutput, USE NotebookCellOutput-type)', async function () { - assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); - await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - - await vscode.window.activeNotebookEditor!.edit(editBuilder => { - editBuilder.replaceCellOutput(0, [new vscode.NotebookCellOutput([ - new vscode.NotebookCellOutputItem('application/foo', 'bar'), - new vscode.NotebookCellOutputItem('application/json', { data: true }, { metadata: true }), - ])]); - }); - - const document = vscode.window.activeNotebookEditor?.document!; - assert.strictEqual(document.isDirty, true); - assert.strictEqual(document.cells.length, 1); - assert.strictEqual(document.cells[0].outputs.length, 1); - - // consuming is OLD api (for now) - const [output] = document.cells[0].outputs; - - assert.strictEqual(output.outputs.length, 2); - assert.strictEqual(output.outputs[0].mime, 'application/foo'); - assert.strictEqual(output.outputs[0].value, 'bar'); - assert.strictEqual(output.outputs[1].mime, 'application/json'); - assert.deepStrictEqual(output.outputs[1].value, { data: true }); - - await saveAllFilesAndCloseAll(undefined); - }); - - test('edit API (replaceOutput)', async function () { - assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); - await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - - await vscode.window.activeNotebookEditor!.edit(editBuilder => { - editBuilder.replaceCellOutput(0, [new vscode.NotebookCellOutput([ - new vscode.NotebookCellOutputItem('foo', 'bar') - ])]); - }); - - const document = vscode.window.activeNotebookEditor?.document!; - assert.strictEqual(document.isDirty, true); - assert.strictEqual(document.cells.length, 1); - assert.strictEqual(document.cells[0].outputs.length, 1); - assert.strictEqual(document.cells[0].outputs[0].outputs.length, 1); - assert.strictEqual(document.cells[0].outputs[0].outputs[0].mime, 'foo'); - assert.strictEqual(document.cells[0].outputs[0].outputs[0].value, 'bar'); - - await saveAllFilesAndCloseAll(undefined); - }); - - test('edit API (replaceOutput, event)', async function () { - assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); - await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - - const outputChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeCellOutputs); - await vscode.window.activeNotebookEditor!.edit(editBuilder => { - editBuilder.replaceCellOutput(0, [new vscode.NotebookCellOutput([ - new vscode.NotebookCellOutputItem('foo', 'bar') - ])]); - }); - - const value = await outputChangeEvent; - assert.strictEqual(value.document === vscode.window.activeNotebookEditor?.document, true); - assert.strictEqual(value.document.isDirty, true); - assert.strictEqual(value.cells.length, 1); - assert.strictEqual(value.document.cells.length, 1); - assert.strictEqual(value.document.cells[0].outputs.length, 1); - assert.strictEqual(value.document.cells[0].outputs[0].outputs.length, 1); - assert.strictEqual(value.document.cells[0].outputs[0].outputs[0].mime, 'foo'); - assert.strictEqual(value.document.cells[0].outputs[0].outputs[0].value, 'bar'); - - await saveAllFilesAndCloseAll(undefined); - }); - 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!; @@ -528,15 +447,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 +465,16 @@ 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.CellKind.Markdown, - language: 'markdown', - metadata: undefined, - outputs: [], - source: 'new_markdown' - }, { - cellKind: vscode.CellKind.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.CellKind.Markdown, - language: 'markdown', - metadata: undefined, - outputs: [], - source: 'new2_markdown' - }, { - cellKind: vscode.CellKind.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.CellKind.Markdown, - language: 'markdown', - metadata: undefined, - outputs: [], - source: 'new_markdown' - }, { - cellKind: vscode.CellKind.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.CellKind.Code, language: 'javascript', source: 'test 2', outputs: [], metadata: undefined }]); - editBuilder.replaceCellMetadata(0, { runnable: false }); + editBuilder.replaceCells(1, 0, [{ cellKind: vscode.NotebookCellKind.Code, language: 'javascript', source: 'test 2', outputs: [], metadata: undefined }]); + editBuilder.replaceCellMetadata(0, new vscode.NotebookCellMetadata().with({ runnable: false })); }); await cellsChangeEvent; @@ -696,16 +484,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.CellKind.Code, language: 'javascript', source: 'test 2', outputs: [], metadata: undefined }]); - editBuilder.replaceCellMetadata(0, { runnable: false }); + editBuilder.replaceCells(1, 0, [{ cellKind: vscode.NotebookCellKind.Code, language: 'javascript', source: 'test 2', outputs: [], metadata: undefined }]); + editBuilder.replaceCellMetadata(0, new vscode.NotebookCellMetadata().with({ runnable: false })); }); await cellsChangeEvent; @@ -723,8 +510,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 +525,12 @@ 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'); @@ -765,8 +551,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'); @@ -839,8 +624,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 +636,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; @@ -863,8 +647,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'); @@ -879,37 +662,11 @@ suite('notebook workflow', () => { assert.deepStrictEqual(activeCell, newActiveCell); await saveFileAndCloseAll(resource); - // TODO@rebornix, there are still some events order issue. - // assert.strictEqual(vscode.window.activeNotebookEditor!.document.cells.indexOf(newActiveCell!), 2); }); - // test.only('document metadata is respected', async function () { - // const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); - // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - - // assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); - // const editor = vscode.window.activeNotebookEditor!; - - // assert.strictEqual(editor.document.cells.length, 1); - // editor.document.metadata.editable = false; - // await editor.edit(builder => builder.delete(0)); - // assert.strictEqual(editor.document.cells.length, 1, 'should not delete cell'); // Not editable, no effect - // await editor.edit(builder => builder.insert(0, 'test', 'python', vscode.CellKind.Code, [], undefined)); - // assert.strictEqual(editor.document.cells.length, 1, 'should not insert cell'); // Not editable, no effect - - // editor.document.metadata.editable = true; - // await editor.edit(builder => builder.delete(0)); - // assert.strictEqual(editor.document.cells.length, 0, 'should delete cell'); // Editable, it worked - // await editor.edit(builder => builder.insert(0, 'test', 'python', vscode.CellKind.Code, [], undefined)); - // assert.strictEqual(editor.document.cells.length, 1, 'should insert cell'); // Editable, it worked - - // // await vscode.commands.executeCommand('workbench.action.files.save'); - // await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); - // }); 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 +675,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'); @@ -937,8 +694,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 +703,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 +711,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; }); @@ -965,15 +721,13 @@ suite('notebook workflow', () => { assert.strictEqual(cell.outputs.length, 1, 'should execute'); // runnable, it worked }); - await vscode.commands.executeCommand('workbench.action.files.save'); - await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + await saveAllFilesAndCloseAll(undefined); }); // 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!; @@ -982,20 +736,18 @@ suite('notebook workflow', () => { await vscode.commands.executeCommand('notebook.execute'); assert.strictEqual(cell.outputs.length, 0, 'should not execute'); // not runnable, didn't work - await vscode.commands.executeCommand('workbench.action.files.save'); - await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + await saveAllFilesAndCloseAll(undefined); }); 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 +763,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) => { @@ -1021,22 +773,18 @@ suite('notebook workflow', () => { assert.strictEqual(vscode.window.activeNotebookEditor?.document.uri.fsPath, secondResource.fsPath); }); - await vscode.commands.executeCommand('workbench.action.files.save'); - await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); - await vscode.commands.executeCommand('workbench.action.files.save'); - await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + await saveAllFilesAndCloseAll(undefined); }); 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 +794,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) => { @@ -1061,22 +809,18 @@ suite('notebook workflow', () => { assert.strictEqual(vscode.window.activeNotebookEditor?.document.uri.fsPath, secondResource.fsPath); }); - await vscode.commands.executeCommand('workbench.action.files.save'); - await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); - await vscode.commands.executeCommand('workbench.action.files.save'); - await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + await saveAllFilesAndCloseAll(undefined); }); 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 +831,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); @@ -1096,15 +840,13 @@ suite('notebook workflow', () => { 'my second output' ]); - await vscode.commands.executeCommand('workbench.action.files.save'); - await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + await saveAllFilesAndCloseAll(undefined); }); -}); + // }); -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 +875,11 @@ 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'); @@ -1179,116 +920,8 @@ suite('notebook undo redo', () => { await saveFileAndCloseAll(resource); }); - // test.skip('execute and then undo redo', async function () { - // assertInitalState(); - // const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); - // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - - // const cellsChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); - // await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); - // const cellChangeEventRet = await cellsChangeEvent; - // assert.strictEqual(cellChangeEventRet.document, vscode.window.activeNotebookEditor?.document); - // assert.strictEqual(cellChangeEventRet.changes.length, 1); - // assert.deepStrictEqual(cellChangeEventRet.changes[0], { - // start: 1, - // deletedCount: 0, - // deletedItems: [], - // items: [ - // vscode.window.activeNotebookEditor!.document.cells[1] - // ] - // }); - - // const secondCell = vscode.window.activeNotebookEditor!.document.cells[1]; - - // const moveCellEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); - // await vscode.commands.executeCommand('notebook.cell.moveUp'); - // const moveCellEventRet = await moveCellEvent; - // assert.deepStrictEqual(moveCellEventRet, { - // document: vscode.window.activeNotebookEditor!.document, - // changes: [ - // { - // start: 1, - // deletedCount: 1, - // deletedItems: [secondCell], - // items: [] - // }, - // { - // start: 0, - // deletedCount: 0, - // deletedItems: [], - // items: [vscode.window.activeNotebookEditor?.document.cells[0]] - // } - // ] - // }); - - // const cellOutputChange = getEventOncePromise(vscode.notebook.onDidChangeCellOutputs); - // await vscode.commands.executeCommand('notebook.cell.execute'); - // const cellOutputsAddedRet = await cellOutputChange; - // assert.deepStrictEqual(cellOutputsAddedRet, { - // document: vscode.window.activeNotebookEditor!.document, - // cells: [vscode.window.activeNotebookEditor!.document.cells[0]] - // }); - // assert.strictEqual(cellOutputsAddedRet.cells[0].outputs.length, 1); - - // const cellOutputClear = getEventOncePromise(vscode.notebook.onDidChangeCellOutputs); - // await vscode.commands.executeCommand('undo'); - // const cellOutputsCleardRet = await cellOutputClear; - // assert.deepStrictEqual(cellOutputsCleardRet, { - // document: vscode.window.activeNotebookEditor!.document, - // cells: [vscode.window.activeNotebookEditor!.document.cells[0]] - // }); - // assert.strictEqual(cellOutputsAddedRet.cells[0].outputs.length, 0); - - // await saveFileAndCloseAll(resource); - // }); - -}); - -suite('notebook working copy', () => { - // test('notebook revert on close', async function () { - // const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); - // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - // await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); - // assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), ''); - - // await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove'); - // await vscode.commands.executeCommand('default:type', { text: 'var abc = 0;' }); - - // // close active editor from command will revert the file - // await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); - // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - // assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true); - // assert.strictEqual(vscode.window.activeNotebookEditor?.selection !== undefined, true); - // assert.deepStrictEqual(vscode.window.activeNotebookEditor?.document.cells[0], vscode.window.activeNotebookEditor?.selection); - // assert.strictEqual(vscode.window.activeNotebookEditor?.selection?.document.getText(), 'test'); - - // await vscode.commands.executeCommand('workbench.action.files.save'); - // await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); - // }); - - // test('notebook revert', async function () { - // const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); - // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - // await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); - // assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), ''); - - // await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove'); - // await vscode.commands.executeCommand('default:type', { text: 'var abc = 0;' }); - // await vscode.commands.executeCommand('workbench.action.files.revert'); - - // assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true); - // assert.strictEqual(vscode.window.activeNotebookEditor?.selection !== undefined, true); - // assert.deepStrictEqual(vscode.window.activeNotebookEditor?.document.cells[0], vscode.window.activeNotebookEditor?.selection); - // assert.deepStrictEqual(vscode.window.activeNotebookEditor?.document.cells.length, 1); - // assert.strictEqual(vscode.window.activeNotebookEditor?.selection?.document.getText(), 'test'); - - // await vscode.commands.executeCommand('workbench.action.files.saveAll'); - // await vscode.commands.executeCommand('workbench.action.closeAllEditors'); - // }); - 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 +931,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'); @@ -1313,8 +946,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 +956,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(), ''); @@ -1346,14 +978,10 @@ suite('notebook working copy', () => { assert.strictEqual(vscode.window.activeNotebookEditor?.selection?.document.getText(), ''); await saveAllFilesAndCloseAll(secondResource); - // await vscode.commands.executeCommand('workbench.action.files.saveAll'); - // await vscode.commands.executeCommand('workbench.action.closeAllEditors'); }); 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'); @@ -1368,19 +996,12 @@ suite('notebook working copy', () => { assert.notEqual(firstNotebookEditor, secondNotebookEditor); assert.strictEqual(firstNotebookEditor?.document, secondNotebookEditor?.document, 'split notebook editors share the same document'); - // assert.notEqual(firstNotebookEditor?.asWebviewUri(vscode.Uri.file('./hello.png')), secondNotebookEditor?.asWebviewUri(vscode.Uri.file('./hello.png'))); await saveAllFilesAndCloseAll(resource); - - // await vscode.commands.executeCommand('workbench.action.files.saveAll'); - // await vscode.commands.executeCommand('workbench.action.closeAllEditors'); }); -}); -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); @@ -1393,8 +1014,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,21 +1029,10 @@ suite('metadata', () => { await saveFileAndCloseAll(resource); }); -}); -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" }); - // assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); - // assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), ''); - // assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.language, 'typescript'); - // await vscode.commands.executeCommand('workbench.action.closeAllEditors'); - // }); 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!; @@ -1440,8 +1049,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!; @@ -1458,8 +1066,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(); @@ -1468,18 +1075,20 @@ suite('regression', () => { assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), 'var abc = 0;'); + + // no kernel -> no default language + // 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); - await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + await closeAllEditors(); }); // 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;'); @@ -1490,20 +1099,18 @@ suite('regression', () => { assert.notEqual(vscode.window.activeNotebookEditor, undefined, 'notebook first'); // assert.notEqual(vscode.window.activeTextEditor, undefined); - await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + await closeAllEditors(); }); test('#102411 - untitled notebook creation failed', async function () { - assertInitalState(); await vscode.commands.executeCommand('workbench.action.files.newUntitledFile', { viewType: 'notebookCoreTest' }); assert.notEqual(vscode.window.activeNotebookEditor, undefined, 'untitled notebook editor is not undefined'); - await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + await closeAllEditors(); }); 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; @@ -1522,23 +1129,111 @@ suite('regression', () => { assert.strictEqual(vscode.window.activeNotebookEditor!.document.cells.length, 2); assert.notEqual(vscode.window.activeNotebookEditor!.document.cells[0].document.getText(), vscode.window.activeNotebookEditor!.document.cells[1].document.getText()); - await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + await closeAllEditors(); }); -}); -suite('webview', () => { + test('#116598, output items change event.', async function () { + + 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 () { + 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 () { + 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); + }); + + test('Numeric metadata should get updated correctly', async function () { + const resource = await createRandomFile('', undefined, '.vsctestnb'); + const document = await vscode.notebook.openNotebookDocument(resource); + + const edit = new vscode.WorkspaceEdit(); + const runStartTime = Date.now(); + const lastRunDuration = Date.now() + 1000; + const runState = vscode.NotebookCellRunState.Success; + const executionOrder = 1234; + const metadata = document.cells[0].metadata.with({ + ...document.cells[0].metadata, + runStartTime, + runState, + lastRunDuration, + executionOrder + }); + edit.replaceNotebookCellMetadata(document.uri, 0, metadata); + await vscode.workspace.applyEdit(edit); + + assert.strictEqual(document.cells[0].metadata.runStartTime, runStartTime); + assert.strictEqual(document.cells[0].metadata.lastRunDuration, lastRunDuration); + assert.strictEqual(document.cells[0].metadata.executionOrder, executionOrder); + assert.strictEqual(document.cells[0].metadata.runState, vscode.NotebookCellRunState.Success); + }); + + // }); + + // 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')); // assert.strictEqual(uri.scheme, 'vscode-webview-resource'); - // await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + // await closeAllEditors(); // }); @@ -1563,6 +1258,6 @@ suite('webview', () => { // await vscode.commands.executeCommand('notebook.cell.execute'); // await promise; - // await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + // await closeAllEditors(); // }); }); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts index a24019b21fe..5e7a544291d 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { window, Pseudoterminal, EventEmitter, TerminalDimensions, workspace, ConfigurationTarget, Disposable, UIKind, env, EnvironmentVariableMutatorType, EnvironmentVariableMutator, extensions, ExtensionContext, TerminalOptions, ExtensionTerminalOptions, Terminal } from 'vscode'; -import { doesNotThrow, equal, deepEqual, throws } from 'assert'; +import { doesNotThrow, equal, deepEqual, throws, strictEqual } from 'assert'; import { assertNoRpc } from '../utils'; // Disable terminal tests: @@ -19,8 +19,6 @@ import { assertNoRpc } from '../utils'; extensionContext = (global as any).testExtensionContext; const config = workspace.getConfiguration('terminal.integrated'); - // Disable conpty in integration tests because of https://github.com/microsoft/vscode/issues/76548 - await config.update('windowsEnableConpty', false, ConfigurationTarget.Global); // Disable exit alerts as tests may trigger then and we're not testing the notifications await config.update('showExitAlert', false, ConfigurationTarget.Global); // Canvas may cause problems when running in a container @@ -57,42 +55,28 @@ import { assertNoRpc } from '../utils'; }); }); - (process.platform === 'linux' ? test.skip : test)('echo works in the default shell', (done) => { - disposables.push(window.onDidOpenTerminal(term => { - try { - equal(terminal, term); - } catch (e) { - done(e); - return; - } - let data = ''; - const dataDisposable = window.onDidWriteTerminalData(e => { - try { - equal(terminal, e.terminal); - } catch (e) { - done(e); - return; - } - data += e.data; - if (data.indexOf(expected) !== 0) { - dataDisposable.dispose(); - terminal.dispose(); - disposables.push(window.onDidCloseTerminal(() => { - done(); - })); - } + test('echo works in the default shell', async () => { + const terminal = await new Promise(r => { + disposables.push(window.onDidOpenTerminal(t => { + strictEqual(terminal, t); + r(terminal); + })); + // Use a single character to avoid winpty/conpty issues with injected sequences + const terminal = window.createTerminal({ + env: { TEST: '`' } }); - disposables.push(dataDisposable); - })); - // Use a single character to avoid winpty/conpty issues with injected sequences - const expected = '`'; - const terminal = window.createTerminal({ - env: { - TEST: '`' - } + terminal.show(); }); - terminal.show(); - doesNotThrow(() => { + + let data = ''; + await new Promise(r => { + disposables.push(window.onDidWriteTerminalData(e => { + strictEqual(terminal, e.terminal); + data += e.data; + if (data.indexOf('`') !== 0) { + r(); + } + })); // Print an environment variable value so the echo statement doesn't get matched if (process.platform === 'win32') { terminal.sendText(`$env:TEST`); @@ -100,6 +84,14 @@ import { assertNoRpc } from '../utils'; terminal.sendText(`echo $TEST`); } }); + + await new Promise(r => { + terminal.dispose(); + disposables.push(window.onDidCloseTerminal(t => { + strictEqual(terminal, t); + r(); + })); + }); }); test('onDidCloseTerminal event fires when terminal is disposed', async () => { 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 90% rename from extensions/vscode-notebook-tests/src/notebookSmokeTestMain.ts rename to extensions/vscode-notebook-tests/src/extension.ts index 686397a0f87..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,26 +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 = { - languages: ['typescript'], - metadata: {}, + metadata: new vscode.NotebookDocumentMetadata(), cells: [ { source: 'code()', language: 'typescript', - cellKind: vscode.CellKind.Code, + cellKind: vscode.NotebookCellKind.Code, outputs: [], - metadata: { - custom: { testCellMetadata: 123 } - } + metadata: new vscode.NotebookCellMetadata().with({ custom: { testCellMetadata: 123 } }) }, { source: 'Markdown Cell', language: 'markdown', - cellKind: vscode.CellKind.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 32d25475aa9..00000000000 --- a/extensions/vscode-notebook-tests/src/notebookTestMain.ts +++ /dev/null @@ -1,137 +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) => { - if (/.*empty\-.*\.vsctestnb$/.test(_resource.path)) { - return { - languages: ['typescript'], - metadata: {}, - cells: [] - }; - } - - const dto: vscode.NotebookData = { - languages: ['typescript'], - metadata: { - custom: { testMetadata: false } - }, - cells: [ - { - source: 'test', - language: 'typescript', - cellKind: vscode.CellKind.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, - 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, - 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 89879b12199..59bf1158a51 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.54.0", - "distro": "667da01a8d816dd448d4cafe83857414de969d80", + "distro": "8abc3b69c9a71c8da5a17f3e06068485a0a90c4c", "author": { "name": "Microsoft Corporation" }, @@ -70,7 +70,7 @@ "native-is-elevated": "0.4.1", "native-keymap": "2.2.1", "native-watchdog": "1.3.0", - "node-pty": "0.11.0-beta1", + "node-pty": "0.10.0-beta19", "spdlog": "^0.11.1", "sudo-prompt": "9.1.1", "tas-client-umd": "0.1.2", @@ -189,8 +189,8 @@ "source-map-support": "^0.3.2", "style-loader": "^1.0.0", "ts-loader": "^6.2.1", - "tsec": "0.1.1", - "typescript": "4.2.0-dev.20201207", + "tsec": "0.1.3", + "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/remote/package.json b/remote/package.json index 86a8a64cb84..3d36902888e 100644 --- a/remote/package.json +++ b/remote/package.json @@ -13,7 +13,7 @@ "jschardet": "2.2.1", "minimist": "^1.2.5", "native-watchdog": "1.3.0", - "node-pty": "0.11.0-beta1", + "node-pty": "0.10.0-beta19", "spdlog": "^0.11.1", "tas-client-umd": "0.1.2", "vscode-nsfw": "1.2.9", diff --git a/remote/web/package.json b/remote/web/package.json index 040d0a926fe..6084e4460cc 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -5,7 +5,6 @@ "dependencies": { "iconv-lite-umd": "0.6.8", "jschardet": "2.2.1", - "node-pty": "0.11.0-beta1", "tas-client-umd": "0.1.2", "vscode-oniguruma": "1.3.1", "vscode-textmate": "5.2.0", diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 21482b82927..9bcfb3b1e72 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -12,18 +12,6 @@ jschardet@2.2.1: resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.2.1.tgz#03b0264669a90c7a5c436a68c5a7d4e4cb0c9823" integrity sha512-Ks2JNuUJoc7PGaZ7bVFtSEvOcr0rBq6Q1J5/7+zKWLT+g+4zziL63O0jg7y2jxhzIa1LVsHUbPXrbaWmz9iwDw== -nan@^2.14.0: - version "2.14.2" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" - integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== - -node-pty@0.11.0-beta1: - version "0.11.0-beta1" - resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.11.0-beta1.tgz#961cf7ab56e0a689b71a19a46371ea090050f312" - integrity sha512-nHMB0K3LZTqjWv3X11XFdy/L4V2eMEk0RbmbVN02GPOkXdMy2NITI0px/x0JtNeIolRPq6r5hf5NUcNc2LJizw== - dependencies: - nan "^2.14.0" - tas-client-umd@0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/tas-client-umd/-/tas-client-umd-0.1.2.tgz#fe93ae085f65424292ac79feff4f1add3e50e624" diff --git a/remote/yarn.lock b/remote/yarn.lock index 35efe72cbb4..a264b79dde8 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -304,10 +304,10 @@ node-addon-api@^3.0.2: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.0.2.tgz#04bc7b83fd845ba785bb6eae25bc857e1ef75681" integrity sha512-+D4s2HCnxPd5PjjI0STKwncjXTUKKqm74MDMz9OPXavjsGmjkvwgLtA5yoxJUdmpj52+2u+RrXgPipahKczMKg== -node-pty@0.11.0-beta1: - version "0.11.0-beta1" - resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.11.0-beta1.tgz#961cf7ab56e0a689b71a19a46371ea090050f312" - integrity sha512-nHMB0K3LZTqjWv3X11XFdy/L4V2eMEk0RbmbVN02GPOkXdMy2NITI0px/x0JtNeIolRPq6r5hf5NUcNc2LJizw== +node-pty@0.10.0-beta19: + version "0.10.0-beta19" + resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.10.0-beta19.tgz#b7cbfba53f7b2a816efe8c9302dd083cc5874458" + integrity sha512-4UIOGMvpofUbe+ZniBUtY8zc/psMURSzbMonQgIhK7JlMQsUwcbkDIrKzStVLJX0FkeZpUNlsVtK7qqzHvrUZA== dependencies: nan "^2.14.0" 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..97961a64821 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 @@ -371,7 +371,8 @@ function configureCrashReporter() { companyName: companyName, productName: process.env['VSCODE_DEV'] ? `${productName} Dev` : productName, submitURL, - uploadToServer: !crashReporterDirectory + uploadToServer: !crashReporterDirectory, + compress: true }); } @@ -405,7 +406,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 +561,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/tsec.exemptions.json b/src/tsec.exemptions.json index dc1e805868f..34fb2f4d8d2 100644 --- a/src/tsec.exemptions.json +++ b/src/tsec.exemptions.json @@ -1,5 +1,31 @@ { + "ban-eval-calls": [ + "vs/workbench/api/worker/extHostExtensionService.ts" + ], + "ban-function-calls": [ + "vs/workbench/api/worker/extHostExtensionService.ts", + "vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts", + "vs/workbench/services/keybinding/test/electron-browser/keyboardMapperTestUtils.ts" + ], "ban-trustedtypes-createpolicy": [ - "**/*.ts" + "vs/base/browser/dom.ts", + "vs/base/browser/markdownRenderer.ts", + "vs/base/worker/defaultWorkerFactory.ts", + "vs/base/worker/workerMain.ts", + "vs/editor/browser/core/markdownRenderer.ts", + "vs/editor/browser/view/domLineBreaksComputer.ts", + "vs/editor/browser/view/viewLayer.ts", + "vs/editor/browser/widget/diffEditorWidget.ts", + "vs/editor/browser/widget/diffReview.ts", + "vs/editor/standalone/browser/colorizer.ts", + "vs/workbench/api/worker/extHostExtensionService.ts", + "vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts", + "vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts", + "vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts", + "vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts" + ], + "ban-worker-calls": [ + "vs/base/worker/defaultWorkerFactory.ts", + "vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts" ] } diff --git a/src/vs/base/browser/contextmenu.ts b/src/vs/base/browser/contextmenu.ts index 2a28dace3c2..49667d70eef 100644 --- a/src/vs/base/browser/contextmenu.ts +++ b/src/vs/base/browser/contextmenu.ts @@ -3,9 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IAction, IActionRunner, IActionViewItem } from 'vs/base/common/actions'; +import { IAction, IActionRunner } from 'vs/base/common/actions'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { AnchorAlignment, AnchorAxisAlignment } from 'vs/base/browser/ui/contextview/contextview'; +import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; export interface IContextMenuEvent { readonly shiftKey?: boolean; 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 b4dafee88bf..f8cde279f9d 100644 --- a/src/vs/base/browser/ui/actionbar/actionViewItems.ts +++ b/src/vs/base/browser/ui/actionbar/actionViewItems.ts @@ -8,13 +8,14 @@ import * as platform from 'vs/base/common/platform'; import * as nls from 'vs/nls'; import { Disposable } from 'vs/base/common/lifecycle'; import { SelectBox, ISelectOptionItem, ISelectBoxOptions } from 'vs/base/browser/ui/selectBox/selectBox'; -import { IAction, IActionRunner, Action, IActionChangeEvent, ActionRunner, Separator, IActionViewItem } from 'vs/base/common/actions'; +import { IAction, IActionRunner, Action, IActionChangeEvent, ActionRunner, Separator } from 'vs/base/common/actions'; import * as types from 'vs/base/common/types'; 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'; +import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; export interface IBaseActionViewItemOptions { draggable?: boolean; @@ -181,9 +182,9 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem { } } - setFocusable(): void { + setFocusable(focusable: boolean): void { if (this.element) { - this.element.tabIndex = 0; + this.element.tabIndex = focusable ? 0 : -1; } } @@ -288,9 +289,9 @@ export class ActionViewItem extends BaseActionViewItem { } } - setFocusable(): void { + setFocusable(focusable: boolean): void { if (this.label) { - this.label.tabIndex = 0; + this.label.tabIndex = focusable ? 0 : -1; } } @@ -356,7 +357,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 b4450cc869f..9f41732cd84 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./actionbar'; -import { Disposable, dispose } from 'vs/base/common/lifecycle'; -import { IAction, IActionRunner, ActionRunner, IRunEvent, Separator, IActionViewItem, IActionViewItemProvider } from 'vs/base/common/actions'; +import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { IAction, IActionRunner, ActionRunner, IRunEvent, Separator } from 'vs/base/common/actions'; import * as DOM from 'vs/base/browser/dom'; import * as types from 'vs/base/common/types'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -13,6 +13,19 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Emitter } from 'vs/base/common/event'; import { IActionViewItemOptions, ActionViewItem, BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +export interface IActionViewItem extends IDisposable { + actionRunner: IActionRunner; + setActionContext(context: any): void; + render(element: HTMLElement): void; + isEnabled(): boolean; + focus(fromRight?: boolean): void; // TODO@isidorn what is this? + blur(): void; +} + +export interface IActionViewItemProvider { + (action: IAction): IActionViewItem | undefined; +} + export const enum ActionsOrientation { HORIZONTAL, HORIZONTAL_REVERSE, @@ -35,7 +48,6 @@ export interface IActionBarOptions { readonly triggerKeys?: ActionTrigger; readonly allowContextMenu?: boolean; readonly preventLoopNavigation?: boolean; - readonly ignoreOrientationForPreviousAndNextKey?: boolean; } export interface IActionOptions extends IActionViewItemOptions { @@ -63,6 +75,8 @@ export class ActionBar extends Disposable implements IActionRunner { // Trigger Key Tracking private triggerKeyDown: boolean = false; + private focusable: boolean = true; + // Elements domNode: HTMLElement; protected actionsList: HTMLElement; @@ -117,22 +131,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; } @@ -148,12 +162,8 @@ export class ActionBar extends Disposable implements IActionRunner { eventHandled = this.focusNext(); } else if (event.equals(KeyCode.Escape) && this.cancelHasListener) { this._onDidCancel.fire(); - } else if ((event.equals(KeyCode.Tab) || event.equals(KeyMod.Shift | KeyCode.Tab)) && focusedItem instanceof BaseActionViewItem && focusedItem.trapsArrowNavigation) { - if (event.equals(KeyCode.Tab)) { - this.focusNext(); - } else { - this.focusPrevious(); - } + } else if (event.equals(KeyCode.Tab) && focusedItem instanceof BaseActionViewItem && focusedItem.trapsArrowNavigation) { + this.focusNext(); } else if (this.isTriggerKeyEvent(event)) { // Staying out of the else branch even if not triggered if (this._triggerKeys.keyDown) { @@ -223,6 +233,25 @@ 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 item can be focused + setFocusable(focusable: boolean): void { + this.focusable = focusable; + if (this.focusable) { + const first = this.viewItems.find(vi => vi instanceof BaseActionViewItem); + if (first instanceof BaseActionViewItem) { + first.setFocusable(true); + } + } else { + this.viewItems.forEach(vi => { + if (vi instanceof BaseActionViewItem) { + vi.setFocusable(false); + } + }); + } + } + private isTriggerKeyEvent(event: StandardKeyboardEvent): boolean { let ret = false; this._triggerKeys.keys.forEach(keyCode => { @@ -301,9 +330,9 @@ export class ActionBar extends Disposable implements IActionRunner { item.setActionContext(this.context); item.render(actionViewItemElement); - if (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(); + item.setFocusable(true); } if (index === null || index < 0 || index >= this.actionsList.children.length) { 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/codicons/codicon/codicon-modifiers.css b/src/vs/base/browser/ui/codicons/codicon/codicon-modifiers.css index bae18d887f4..4b5b5141489 100644 --- a/src/vs/base/browser/ui/codicons/codicon/codicon-modifiers.css +++ b/src/vs/base/browser/ui/codicons/codicon/codicon-modifiers.css @@ -13,7 +13,7 @@ } } -.codicon-sync.codicon-modifier-spin, .codicon-loading.codicon-modifier-spin .codicon-gear.codicon-modifier-spin { +.codicon-sync.codicon-modifier-spin, .codicon-loading.codicon-modifier-spin, .codicon-gear.codicon-modifier-spin { /* Use steps to throttle FPS to reduce CPU usage */ animation: codicon-spin 1.5s steps(30) infinite; } diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf index 01abb46b868..b1a42c0999c 100644 Binary files a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf and b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf differ diff --git a/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts index 353b766ef1d..67e2e9bb7c1 100644 --- a/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts +++ b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./dropdown'; -import { Action, IAction, IActionRunner, IActionViewItemProvider } from 'vs/base/common/actions'; +import { Action, IAction, IActionRunner } from 'vs/base/common/actions'; import { IDisposable } from 'vs/base/common/lifecycle'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; @@ -14,6 +14,7 @@ import { ActionViewItem, BaseActionViewItem, IActionViewItemOptions, IBaseAction import { IActionProvider, DropdownMenu, IDropdownMenuOptions, ILabelRenderer } from 'vs/base/browser/ui/dropdown/dropdown'; import { IContextMenuProvider } from 'vs/base/browser/contextmenu'; import { Codicon } from 'vs/base/common/codicons'; +import { IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; export interface IKeybindingProvider { (action: IAction): ResolvedKeybinding | undefined; @@ -172,12 +173,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..958d5f12868 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -5,10 +5,10 @@ import * as nls from 'vs/nls'; 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 { IActionRunner, IAction, SubmenuAction, Separator, EmptySubmenuAction } from 'vs/base/common/actions'; +import { ActionBar, ActionsOrientation, IActionViewItemProvider } 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/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index c1348a93ddc..8d1c5b6b17a 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -5,8 +5,8 @@ import 'vs/css!./toolbar'; import * as nls from 'vs/nls'; -import { Action, IActionRunner, IAction, IActionViewItemProvider, SubmenuAction } from 'vs/base/common/actions'; -import { ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Action, IActionRunner, IAction, SubmenuAction } from 'vs/base/common/actions'; +import { ActionBar, ActionsOrientation, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; 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/actions.ts b/src/vs/base/common/actions.ts index 74db8a99f21..466be3c5cae 100644 --- a/src/vs/base/common/actions.ts +++ b/src/vs/base/common/actions.ts @@ -39,19 +39,6 @@ export interface IActionRunner extends IDisposable { readonly onBeforeRun: Event; } -export interface IActionViewItem extends IDisposable { - actionRunner: IActionRunner; - setActionContext(context: any): void; - render(element: any /* HTMLElement */): void; - isEnabled(): boolean; - focus(fromRight?: boolean): void; // TODO@isidorn what is this? - blur(): void; -} - -export interface IActionViewItemProvider { - (action: IAction): IActionViewItem | undefined; -} - export interface IActionChangeEvent { readonly label?: string; readonly tooltip?: string; diff --git a/src/vs/base/common/codicons.ts b/src/vs/base/common/codicons.ts index 8682ac8158a..95601fc7ac0 100644 --- a/src/vs/base/common/codicons.ts +++ b/src/vs/base/common/codicons.ts @@ -543,6 +543,9 @@ export namespace Codicon { export const typeHierarchySub = new Codicon('type-hierarchy-sub', { character: '\\ebba' }); export const typeHierarchySuper = new Codicon('type-hierarchy-super', { character: '\\ebbb' }); export const gitPullRequestCreate = new Codicon('git-pull-request-create', { character: '\\ebbc' }); + export const runAbove = new Codicon('run-above', { character: '\\ebbd' }); + export const runBelow = new Codicon('run-below', { character: '\\ebbe' }); + export const notebookTemplate = new Codicon('notebook-template', { character: '\\ebbf' }); export const dropDownButton = new Codicon('drop-down-button', Codicon.chevronDown.definition); } diff --git a/src/vs/base/common/iterator.ts b/src/vs/base/common/iterator.ts index 7d622c76c53..adfbdc906ab 100644 --- a/src/vs/base/common/iterator.ts +++ b/src/vs/base/common/iterator.ts @@ -39,6 +39,18 @@ export namespace Iterable { return false; } + export function find(iterable: Iterable, predicate: (t: T) => t is R): T | undefined; + export function find(iterable: Iterable, predicate: (t: T) => boolean): T | undefined; + export function find(iterable: Iterable, predicate: (t: T) => boolean): T | undefined { + for (const element of iterable) { + if (predicate(element)) { + return element; + } + } + + return undefined; + } + export function filter(iterable: Iterable, predicate: (t: T) => t is R): Iterable; export function filter(iterable: Iterable, predicate: (t: T) => boolean): Iterable; export function* filter(iterable: Iterable, predicate: (t: T) => boolean): Iterable { 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 71a8c714bec..4ebb2bf0fec 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -53,13 +53,12 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration as registerUserDataSyncConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncResourceEnablementService, IUserDataSyncBackupStoreService, IUserDataSyncStoreManagementService, IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; import { UserDataSyncStoreService, UserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; -import { UserDataSyncChannel, UserDataSyncUtilServiceClient, UserDataAutoSyncChannel, UserDataSyncMachinesServiceChannel, UserDataSyncAccountServiceChannel, UserDataSyncStoreManagementServiceChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; +import { UserDataSyncUtilServiceClient, UserDataAutoSyncChannel, UserDataSyncMachinesServiceChannel, UserDataSyncAccountServiceChannel, UserDataSyncStoreManagementServiceChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; 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'; @@ -84,6 +83,7 @@ import { join } from 'vs/base/common/path'; import { TerminalIpcChannels } from 'vs/platform/terminal/common/terminal'; import { LocalPtyService } from 'vs/platform/terminal/electron-browser/localPtyService'; import { ILocalPtyService } from 'vs/platform/terminal/electron-sandbox/terminal'; +import { UserDataSyncChannel } from 'vs/platform/userDataSync/common/userDataSyncServiceIpc'; class SharedProcessMain extends Disposable { @@ -174,8 +174,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(); @@ -265,7 +265,6 @@ class SharedProcessMain extends Disposable { // Terminal const localPtyService = this._register(new LocalPtyService(logService)); services.set(ILocalPtyService, localPtyService); - this._register(toDisposable(() => localPtyService.dispose())); return new InstantiationService(services); } @@ -298,7 +297,7 @@ class SharedProcessMain extends Disposable { const userDataSyncStoreManagementChannel = new UserDataSyncStoreManagementServiceChannel(accessor.get(IUserDataSyncStoreManagementService)); this.server.registerChannel('userDataSyncStoreManagement', userDataSyncStoreManagementChannel); - const userDataSyncChannel = new UserDataSyncChannel(this.server, accessor.get(IUserDataSyncService), accessor.get(ILogService)); + const userDataSyncChannel = new UserDataSyncChannel(accessor.get(IUserDataSyncService), accessor.get(ILogService)); this.server.registerChannel('userDataSync', userDataSyncChannel); const userDataAutoSync = this._register(accessor.get(IInstantiationService).createInstance(UserDataAutoSyncService)); 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..6d03ad233a4 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); }; @@ -225,11 +225,19 @@ export class CodeApplication extends Disposable { this.nativeHostMainService?.openExternal(undefined, url); }); - session.defaultSession.setPermissionRequestHandler((webContents, permission /* 'media' | 'geolocation' | 'notifications' | 'midiSysex' | 'pointerLock' | 'fullscreen' | 'openExternal' */, callback) => { + const webviewFrameUrl = 'about:blank?webviewFrame'; + + session.defaultSession.setPermissionRequestHandler((_webContents, permission /* 'media' | 'geolocation' | 'notifications' | 'midiSysex' | 'pointerLock' | 'fullscreen' | 'openExternal' */, callback, details) => { + if (details.requestingUrl === webviewFrameUrl) { + return callback(permission === 'clipboard-read'); + } return callback(false); }); - session.defaultSession.setPermissionCheckHandler((webContents, permission /* 'media' */) => { + session.defaultSession.setPermissionCheckHandler((_webContents, permission /* 'media' */, _origin, details) => { + if (details.requestingUrl === webviewFrameUrl) { + return permission === 'clipboard-read'; + } return false; }); }); @@ -255,7 +263,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 +336,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 +384,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 +396,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 +412,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 +456,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 +473,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 +517,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 +588,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 +672,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 +705,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 +742,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 +797,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 +870,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 +934,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 +983,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 +1007,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 +1027,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 2201f7c0e50..7cbdd078f09 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -10,7 +10,7 @@ import { localize } from 'vs/nls'; import { isWindows, IProcessEnvironment, isMacintosh } from 'vs/base/common/platform'; import product from 'vs/platform/product/common/product'; import { parseMainProcessArgv, addArg } from 'vs/platform/environment/node/argvHelper'; -import { createWaitMarkerFile } from 'vs/platform/environment/node/waitMarkerFile'; +import { createWaitMarkerFile } from 'vs/platform/environment/node/wait'; import { LifecycleMainService, ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc'; import { Server as NodeIPCServer, serve as nodeIPCServe, connect as nodeIPCConnect, XDG_RUNTIME_DIR } from 'vs/base/parts/ipc/node/ipc.net'; @@ -66,43 +66,22 @@ import { LoggerService } from 'vs/platform/log/node/loggerService'; class CodeMain { main(): void { + try { + this.startup(); + } catch (error) { + console.error(error.message); + app.exit(1); + } + } + + private async startup(): Promise { // Set the error handler early enough so that we are not getting the // default electron error dialog popping up setUnexpectedErrorHandler(err => console.error(err)); - // Parse arguments - let args: NativeParsedArgs; - try { - args = parseMainProcessArgv(process.argv); - args = this.validatePaths(args); - } catch (err) { - console.error(err.message); - app.exit(1); - - return; - } - - // If we are started with --wait create a random temporary file - // and pass it over to the starting instance. We can use this file - // to wait for it to be deleted to monitor that the edited file - // is closed and then exit the waiting process. - // - // Note: we are not doing this if the wait marker has been already - // added as argument. This can happen if Code was started from CLI. - if (args.wait && !args.waitMarkerFilePath) { - const waitMarkerFilePath = createWaitMarkerFile(args.verbose); - if (waitMarkerFilePath) { - addArg(process.argv, '--waitMarkerFilePath', waitMarkerFilePath); - args.waitMarkerFilePath = waitMarkerFilePath; - } - } - - // Launch - this.startup(args); - } - - private async startup(args: NativeParsedArgs): Promise { + // Resolve command line arguments + const args = this.resolveArgs(); // Create services const [instantiationService, instanceEnvironment, environmentService, configurationService, stateService, bufferLogService] = this.createServices(args); @@ -129,7 +108,7 @@ class CodeMain { // Create the main IPC server by trying to be the server // If this throws an error it means we are not the first // instance of VS Code running and so we would quit. - const mainIpcServer = await this.doStartup(args, logService, environmentService, lifecycleMainService, instantiationService, true); + const mainProcessNodeIpcServer = await this.doStartup(args, logService, environmentService, lifecycleMainService, instantiationService, true); // Delay creation of spdlog for perf reasons (https://github.com/microsoft/vscode/issues/72906) bufferLogService.logger = new SpdLogLogger('main', join(environmentService.logsPath, 'main.log'), true, bufferLogService.getLevel()); @@ -140,7 +119,7 @@ class CodeMain { configurationService.dispose(); }); - return instantiationService.createInstance(CodeApplication, mainIpcServer, instanceEnvironment).startup(); + return instantiationService.createInstance(CodeApplication, mainProcessNodeIpcServer, instanceEnvironment).startup(); }); } catch (error) { instantiationService.invokeFunction(this.quit, error); @@ -202,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 => { @@ -219,17 +198,17 @@ 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 - ].map((path): undefined | Promise => path ? promises.mkdir(path, { recursive: true }) : undefined)); + 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 const configurationServiceInitialization = configurationService.initialize(); @@ -240,15 +219,15 @@ 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 server: NodeIPCServer; + let mainProcessNodeIpcServer: NodeIPCServer; try { - server = await nodeIPCServe(environmentService.mainIPCHandle); - once(lifecycleMainService.onWillShutdown)(() => server.dispose()); + mainProcessNodeIpcServer = await nodeIPCServe(environmentMainService.mainIPCHandle); + once(lifecycleMainService.onWillShutdown)(() => mainProcessNodeIpcServer.dispose()); } catch (error) { // Handle unexpected errors (the only expected error is EADDRINUSE that @@ -256,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; @@ -265,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 @@ -284,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(); @@ -362,12 +341,12 @@ class CodeMain { // instance to startup. Otherwise we would wrongly overwrite the PID process.env['VSCODE_PID'] = String(process.pid); - return server; + 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."), @@ -390,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); @@ -429,7 +408,30 @@ class CodeMain { lifecycleMainService.kill(exitCode); } - //#region Path Helpers + //#region Command line arguments utilities + + private resolveArgs(): NativeParsedArgs { + + // Parse arguments + const args = this.validatePaths(parseMainProcessArgv(process.argv)); + + // If we are started with --wait create a random temporary file + // and pass it over to the starting instance. We can use this file + // to wait for it to be deleted to monitor that the edited file + // is closed and then exit the waiting process. + // + // Note: we are not doing this if the wait marker has been already + // added as argument. This can happen if Code was started from CLI. + if (args.wait && !args.waitMarkerFilePath) { + const waitMarkerFilePath = createWaitMarkerFile(args.verbose); + if (waitMarkerFilePath) { + addArg(process.argv, '--waitMarkerFilePath', waitMarkerFilePath); + args.waitMarkerFilePath = waitMarkerFilePath; + } + } + + return args; + } private validatePaths(args: NativeParsedArgs): NativeParsedArgs { diff --git a/src/vs/code/electron-main/protocol.ts b/src/vs/code/electron-main/protocol.ts index 5bfbebb680e..298bee5c07b 100644 --- a/src/vs/code/electron-main/protocol.ts +++ b/src/vs/code/electron-main/protocol.ts @@ -31,33 +31,30 @@ export class FileProtocolHandler extends Disposable { // Define an initial set of roots we allow loading from // - appRoot : all files installed as part of the app // - extensions : all files shipped from extensions + // - storage : all files in global and workspace storage (https://github.com/microsoft/vscode/issues/116735) this.validRoots.set(URI.file(environmentService.appRoot), true); this.validRoots.set(URI.file(environmentService.extensionsPath), true); + this.validRoots.set(environmentService.globalStorageHome, true); + this.validRoots.set(environmentService.workspaceStorageHome, true); // Register vscode-file:// handler defaultSession.protocol.registerFileProtocol(Schemas.vscodeFileResource, (request, callback) => this.handleResourceRequest(request, callback as unknown as ProtocolCallback)); - // Block any file:// access (explicitly enabled only) - if (isPreferringBrowserCodeLoad) { - this.logService.info(`Intercepting ${Schemas.file}: protocol and blocking it`); - - defaultSession.protocol.interceptFileProtocol(Schemas.file, (request, callback) => this.handleFileRequest(request, callback as unknown as ProtocolCallback)); - } + // Intercept any file:// access + defaultSession.protocol.interceptFileProtocol(Schemas.file, (request, callback) => this.handleFileRequest(request, callback as unknown as ProtocolCallback)); // Cleanup this._register(toDisposable(() => { defaultSession.protocol.unregisterProtocol(Schemas.vscodeFileResource); - if (isPreferringBrowserCodeLoad) { - defaultSession.protocol.uninterceptProtocol(Schemas.file); - } + defaultSession.protocol.uninterceptProtocol(Schemas.file); })); } 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) { @@ -85,10 +82,29 @@ export class FileProtocolHandler extends Disposable { } private async handleFileRequest(request: Electron.ProtocolRequest, callback: ProtocolCallback) { - const uri = URI.parse(request.url); + const fileUri = URI.parse(request.url); - this.logService.error(`Refused to load resource ${uri.fsPath} from ${Schemas.file}: protocol`); - callback({ error: -3 /* ABORTED */ }); + // isPreferringBrowserCodeLoad: false + // => ensure the file path is in our expected roots + if (!isPreferringBrowserCodeLoad) { + if (this.validRoots.findSubstr(fileUri)) { + return callback({ + path: fileUri.fsPath + }); + } + + this.logService.error(`${Schemas.file}: Refused to load resource ${fileUri.fsPath} from ${Schemas.file}: protocol (original URL: ${request.url})`); + + return callback({ error: -3 /* ABORTED */ }); + } + + // isPreferringBrowserCodeLoad: true + // => block any file request + else { + this.logService.error(`Refused to load resource ${fileUri.fsPath} from ${Schemas.file}: protocol (original URL: ${request.url})`); + + return callback({ error: -3 /* ABORTED */ }); + } } private async handleResourceRequest(request: Electron.ProtocolRequest, callback: ProtocolCallback) { @@ -102,9 +118,10 @@ export class FileProtocolHandler extends Disposable { return callback({ path: fileUri.fsPath }); - } + } else { + this.logService.error(`${Schemas.vscodeFileResource}: Refused to load resource ${fileUri.fsPath} from ${Schemas.vscodeFileResource}: protocol (original URL: ${request.url})`); - this.logService.error(`${Schemas.vscodeFileResource}: Refused to load resource ${fileUri.fsPath}}`); - callback({ error: -3 /* ABORTED */ }); + return callback({ error: -3 /* ABORTED */ }); + } } } 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/code/node/cli.ts b/src/vs/code/node/cli.ts index ac557018989..43f8ea01331 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -9,7 +9,7 @@ import { spawn, ChildProcess, SpawnOptions } from 'child_process'; import { buildHelpMessage, buildVersionMessage, OPTIONS } from 'vs/platform/environment/node/argv'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { parseCLIProcessArgv, addArg } from 'vs/platform/environment/node/argvHelper'; -import { createWaitMarkerFile } from 'vs/platform/environment/node/waitMarkerFile'; +import { createWaitMarkerFile } from 'vs/platform/environment/node/wait'; import product from 'vs/platform/product/common/product'; import { isAbsolute, join } from 'vs/base/common/path'; import { whenDeleted, writeFileSync } from 'vs/base/node/pfs'; 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 53615b318ac..6256e77b6ec 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -222,7 +222,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE private readonly _updateDecorationsRunner: RunOnceScheduler; private readonly _editorWorkerService: IEditorWorkerService; - protected _contextKeyService: IContextKeyService; + private readonly _contextKeyService: IContextKeyService; + private readonly _instantiationService: IInstantiationService; private readonly _codeEditorService: ICodeEditorService; private readonly _themeService: IThemeService; private readonly _notificationService: INotificationService; @@ -248,6 +249,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._editorWorkerService = editorWorkerService; this._codeEditorService = codeEditorService; this._contextKeyService = this._register(contextKeyService.createScoped(domElement)); + this._instantiationService = instantiationService.createChild(new ServiceCollection([IContextKeyService, this._contextKeyService])); this._contextKeyService.createKey('isInDiffEditor', true); this._themeService = themeService; this._notificationService = notificationService; @@ -354,20 +356,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._diffComputationResult = null; - const leftContextKeyService = this._contextKeyService.createScoped(); - - const leftServices = new ServiceCollection(); - leftServices.set(IContextKeyService, leftContextKeyService); - const leftScopedInstantiationService = instantiationService.createChild(leftServices); - - const rightContextKeyService = this._contextKeyService.createScoped(); - - const rightServices = new ServiceCollection(); - rightServices.set(IContextKeyService, rightContextKeyService); - const rightScopedInstantiationService = instantiationService.createChild(rightServices); - - this._originalEditor = this._createLeftHandSideEditor(options, codeEditorWidgetOptions.originalEditor || {}, leftScopedInstantiationService, leftContextKeyService); - this._modifiedEditor = this._createRightHandSideEditor(options, codeEditorWidgetOptions.modifiedEditor || {}, rightScopedInstantiationService, rightContextKeyService); + this._originalEditor = this._createLeftHandSideEditor(options, codeEditorWidgetOptions.originalEditor || {}); + this._modifiedEditor = this._createRightHandSideEditor(options, codeEditorWidgetOptions.modifiedEditor || {}); this._originalOverviewRuler = null; this._modifiedOverviewRuler = null; @@ -495,8 +485,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._layoutOverviewRulers(); } - private _createLeftHandSideEditor(options: Readonly, codeEditorWidgetOptions: ICodeEditorWidgetOptions, instantiationService: IInstantiationService, contextKeyService: IContextKeyService): CodeEditorWidget { - const editor = this._createInnerEditor(instantiationService, this._originalDomNode, this._adjustOptionsForLeftHandSide(options), codeEditorWidgetOptions); + private _createLeftHandSideEditor(options: Readonly, codeEditorWidgetOptions: ICodeEditorWidgetOptions): CodeEditorWidget { + const editor = this._createInnerEditor(this._instantiationService, this._originalDomNode, this._adjustOptionsForLeftHandSide(options), codeEditorWidgetOptions); this._register(editor.onDidScrollChange((e) => { if (this._isHandlingScrollEvent) { @@ -538,7 +528,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } })); - const isInDiffLeftEditorKey = contextKeyService.createKey('isInDiffLeftEditor', undefined); + const isInDiffLeftEditorKey = this._contextKeyService.createKey('isInDiffLeftEditor', editor.hasWidgetFocus()); this._register(editor.onDidFocusEditorWidget(() => isInDiffLeftEditorKey.set(true))); this._register(editor.onDidBlurEditorWidget(() => isInDiffLeftEditorKey.set(false))); @@ -557,8 +547,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE return editor; } - private _createRightHandSideEditor(options: Readonly, codeEditorWidgetOptions: ICodeEditorWidgetOptions, instantiationService: IInstantiationService, contextKeyService: IContextKeyService): CodeEditorWidget { - const editor = this._createInnerEditor(instantiationService, this._modifiedDomNode, this._adjustOptionsForRightHandSide(options), codeEditorWidgetOptions); + private _createRightHandSideEditor(options: Readonly, codeEditorWidgetOptions: ICodeEditorWidgetOptions): CodeEditorWidget { + const editor = this._createInnerEditor(this._instantiationService, this._modifiedDomNode, this._adjustOptionsForRightHandSide(options), codeEditorWidgetOptions); this._register(editor.onDidScrollChange((e) => { if (this._isHandlingScrollEvent) { @@ -606,7 +596,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } })); - const isInDiffRightEditorKey = contextKeyService.createKey('isInDiffRightEditor', undefined); + const isInDiffRightEditorKey = this._contextKeyService.createKey('isInDiffRightEditor', editor.hasWidgetFocus()); this._register(editor.onDidFocusEditorWidget(() => isInDiffRightEditorKey.set(true))); this._register(editor.onDidBlurEditorWidget(() => isInDiffRightEditorKey.set(false))); @@ -1161,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 { @@ -1174,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/codeAction/codeAction.ts b/src/vs/editor/contrib/codeAction/codeAction.ts index 25b7687bc4a..3fc3033b397 100644 --- a/src/vs/editor/contrib/codeAction/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/codeAction.ts @@ -223,8 +223,7 @@ function getDocumentation( return undefined; } -CommandsRegistry.registerCommand('_executeCodeActionProvider', async function (accessor, ...args): Promise> { - const [resource, rangeOrSelection, kind, itemResolveCount] = args; +CommandsRegistry.registerCommand('_executeCodeActionProvider', async function (accessor, resource: URI, rangeOrSelection: Range | Selection, kind?: string, itemResolveCount?: number): Promise> { if (!(resource instanceof URI)) { throw illegalArgument(); } @@ -244,14 +243,14 @@ CommandsRegistry.registerCommand('_executeCodeActionProvider', async function (a throw illegalArgument(); } + const include = typeof kind === 'string' ? new CodeActionKind(kind) : undefined; const codeActionSet = await getCodeActions( model, validatedRangeOrSelection, - { type: modes.CodeActionTriggerType.Manual, filter: { includeSourceActions: true, include: kind && kind.value ? new CodeActionKind(kind.value) : undefined } }, + { type: modes.CodeActionTriggerType.Manual, filter: { includeSourceActions: true, include } }, Progress.None, CancellationToken.None); - const resolving: Promise[] = []; const resolveCount = Math.min(codeActionSet.validActions.length, typeof itemResolveCount === 'number' ? itemResolveCount : 0); for (let i = 0; i < resolveCount; i++) { 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/contrib/suggest/suggestWidgetDetails.ts b/src/vs/editor/contrib/suggest/suggestWidgetDetails.ts index 9eed3c9900c..5adcbc39882 100644 --- a/src/vs/editor/contrib/suggest/suggestWidgetDetails.ts +++ b/src/vs/editor/contrib/suggest/suggestWidgetDetails.ts @@ -131,7 +131,7 @@ export class SuggestDetailsWidget { md += `score: ${item.score[0]}${item.word ? `, compared '${item.completion.filterText && (item.completion.filterText + ' (filterText)') || typeof item.completion.label === 'string' ? item.completion.label : item.completion.label.name}' with '${item.word}'` : ' (no prefix)'}\n`; md += `distance: ${item.distance}, see localityBonus-setting\n`; md += `index: ${item.idx}, based on ${item.completion.sortText && `sortText: "${item.completion.sortText}"` || 'label'}\n`; - md += `commit characters: ${item.completion.commitCharacters}\n`; + md += `commit characters: ${item.completion.commitCharacters?.join('')}\n`; documentation = new MarkdownString().appendCodeblock('empty', md); detail = `Provider: ${item.provider._debugDisplayName}`; } diff --git a/src/vs/editor/contrib/suggest/suggestWidgetStatus.ts b/src/vs/editor/contrib/suggest/suggestWidgetStatus.ts index 3aa92bca3f4..5739f38f198 100644 --- a/src/vs/editor/contrib/suggest/suggestWidgetStatus.ts +++ b/src/vs/editor/contrib/suggest/suggestWidgetStatus.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; -import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IActionViewItemProvider, IAction } from 'vs/base/common/actions'; +import { ActionBar, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IAction } from 'vs/base/common/actions'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { suggestWidgetStatusbarMenu } from 'vs/editor/contrib/suggest/suggest'; diff --git a/src/vs/editor/contrib/suggest/wordContextKey.ts b/src/vs/editor/contrib/suggest/wordContextKey.ts index a9558304bac..81e80cf74f2 100644 --- a/src/vs/editor/contrib/suggest/wordContextKey.ts +++ b/src/vs/editor/contrib/suggest/wordContextKey.ts @@ -4,15 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; -export class WordContextKey extends Disposable { +export class WordContextKey { static readonly AtEnd = new RawContextKey('atEndOfWord', false); private readonly _ckAtEnd: IContextKey; + private readonly _configListener: IDisposable; private _enabled: boolean = false; private _selectionListener?: IDisposable; @@ -21,14 +22,14 @@ export class WordContextKey extends Disposable { private readonly _editor: ICodeEditor, @IContextKeyService contextKeyService: IContextKeyService, ) { - super(); + this._ckAtEnd = WordContextKey.AtEnd.bindTo(contextKeyService); - this._register(this._editor.onDidChangeConfiguration(e => e.hasChanged(EditorOption.tabCompletion) && this._update())); + this._configListener = this._editor.onDidChangeConfiguration(e => e.hasChanged(EditorOption.tabCompletion) && this._update()); this._update(); } dispose(): void { - super.dispose(); + this._configListener.dispose(); this._selectionListener?.dispose(); this._ckAtEnd.reset(); } 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 d55aea9bb41..e40016315b3 100644 --- a/src/vs/platform/contextkey/browser/contextKeyService.ts +++ b/src/vs/platform/contextkey/browser/contextKeyService.ts @@ -3,11 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Emitter, PauseableEmitter } from 'vs/base/common/event'; +import { Emitter, Event, PauseableEmitter } from 'vs/base/common/event'; 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'; @@ -287,6 +288,13 @@ export abstract class AbstractContextKeyService implements IContextKeyService { return new ScopedContextKeyService(this, domNode); } + createOverlay(overlay: Iterable<[string, any]> = Iterable.empty()): IContextKeyService { + if (this._isDisposed) { + throw new Error(`AbstractContextKeyService has been disposed`); + } + return new OverlayContextKeyService(this, overlay); + } + public contextMatchesRules(rules: ContextKeyExpression | undefined): boolean { if (this._isDisposed) { throw new Error(`AbstractContextKeyService has been disposed`); @@ -405,27 +413,25 @@ export class ContextKeyService extends AbstractContextKeyService implements ICon class ScopedContextKeyService extends AbstractContextKeyService { private _parent: AbstractContextKeyService; - private _domNode: IContextKeyServiceTarget | undefined; + private _domNode: IContextKeyServiceTarget; private readonly _parentChangeListener = new MutableDisposable(); - constructor(parent: AbstractContextKeyService, domNode?: IContextKeyServiceTarget) { + constructor(parent: AbstractContextKeyService, domNode: IContextKeyServiceTarget) { super(parent.createChildContext()); this._parent = parent; this._updateParentChangeListener(); - if (domNode) { - this._domNode = domNode; - if (this._domNode.hasAttribute(KEYBINDING_CONTEXT_ATTR)) { - let extraInfo = ''; - if ((this._domNode as HTMLElement).classList) { - extraInfo = Array.from((this._domNode as HTMLElement).classList.values()).join(', '); - } - - console.error(`Element already has context attribute${extraInfo ? ': ' + extraInfo : ''}`); + this._domNode = domNode; + if (this._domNode.hasAttribute(KEYBINDING_CONTEXT_ATTR)) { + let extraInfo = ''; + if ((this._domNode as HTMLElement).classList) { + extraInfo = Array.from((this._domNode as HTMLElement).classList.values()).join(', '); } - this._domNode.setAttribute(KEYBINDING_CONTEXT_ATTR, String(this._myContextId)); + + console.error(`Element already has context attribute${extraInfo ? ': ' + extraInfo : ''}`); } + this._domNode.setAttribute(KEYBINDING_CONTEXT_ATTR, String(this._myContextId)); } private _updateParentChangeListener(): void { @@ -434,14 +440,15 @@ class ScopedContextKeyService extends AbstractContextKeyService { } public dispose(): void { - this._onDidChangeContext.dispose(); - this._isDisposed = true; - this._parent.disposeContext(this._myContextId); - this._parentChangeListener?.dispose(); - if (this._domNode) { - this._domNode.removeAttribute(KEYBINDING_CONTEXT_ATTR); - this._domNode = undefined; + if (this._isDisposed) { + return; } + + this._onDidChangeContext.dispose(); + this._parent.disposeContext(this._myContextId); + this._parentChangeListener.dispose(); + this._domNode.removeAttribute(KEYBINDING_CONTEXT_ATTR); + this._isDisposed = true; } public getContextValuesContainer(contextId: number): Context { @@ -484,6 +491,76 @@ class ScopedContextKeyService extends AbstractContextKeyService { } } +class OverlayContext implements IContext { + + constructor(private parent: IContext, private overlay: ReadonlyMap) { } + + getValue(key: string): T | undefined { + return this.overlay.has(key) ? this.overlay.get(key) : this.parent.getValue(key); + } +} + +class OverlayContextKeyService implements IContextKeyService { + + declare _serviceBrand: undefined; + private overlay: Map; + + get contextId(): number { + return this.parent.contextId; + } + + get onDidChangeContext(): Event { + return this.parent.onDidChangeContext; + } + + constructor(private parent: AbstractContextKeyService | OverlayContextKeyService, overlay: Iterable<[string, any]>) { + this.overlay = new Map(overlay); + } + + bufferChangeEvents(callback: Function): void { + this.parent.bufferChangeEvents(callback); + } + + createKey(): IContextKey { + throw new Error('Not supported.'); + } + + getContext(target: IContextKeyServiceTarget | null): IContext { + return new OverlayContext(this.parent.getContext(target), this.overlay); + } + + getContextValuesContainer(contextId: number): IContext { + const parentContext = this.parent.getContextValuesContainer(contextId); + return new OverlayContext(parentContext, this.overlay); + } + + contextMatchesRules(rules: ContextKeyExpression | undefined): boolean { + const context = this.getContextValuesContainer(this.contextId); + const result = KeybindingResolver.contextMatchesRules(context, rules); + return result; + } + + getContextKeyValue(key: string): T | undefined { + return this.overlay.has(key) ? this.overlay.get(key) : this.parent.getContextKeyValue(key); + } + + createScoped(): IContextKeyService { + throw new Error('Not supported.'); + } + + createOverlay(overlay: Iterable<[string, any]> = Iterable.empty()): IContextKeyService { + return new OverlayContextKeyService(this, overlay); + } + + updateParent(): void { + throw new Error('Not supported.'); + } + + dispose(): void { + // noop + } +} + function findContextAttr(domNode: IContextKeyServiceTarget | null): number { while (domNode) { if (domNode.hasAttribute(KEYBINDING_CONTEXT_ATTR)) { @@ -502,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 56e09f911fa..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(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 { @@ -1341,7 +1345,8 @@ export interface IContextKeyService { contextMatchesRules(rules: ContextKeyExpression | undefined): boolean; getContextKeyValue(key: string): T | undefined; - createScoped(target?: IContextKeyServiceTarget): IContextKeyService; + createScoped(target: IContextKeyServiceTarget): IContextKeyService; + createOverlay(overlay: Iterable<[string, any]>): IContextKeyService; getContext(target: IContextKeyServiceTarget | null): IContext; updateParent(parentContextKeyService: IContextKeyService): void; 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/environment/node/waitMarkerFile.ts b/src/vs/platform/environment/node/wait.ts similarity index 72% rename from src/vs/platform/environment/node/waitMarkerFile.ts rename to src/vs/platform/environment/node/wait.ts index 8916ef40732..9441f808590 100644 --- a/src/vs/platform/environment/node/waitMarkerFile.ts +++ b/src/vs/platform/environment/node/wait.ts @@ -3,15 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/** - * This code is also used by standalone cli's. Avoid adding dependencies to keep the size of the cli small. - */ -import * as path from 'vs/base/common/path'; -import * as os from 'os'; import * as fs from 'fs'; +import { tmpdir } from 'os'; +import { join } from 'vs/base/common/path'; export function createWaitMarkerFile(verbose?: boolean): string | undefined { - const randomWaitMarkerPath = path.join(os.tmpdir(), Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10)); + const randomWaitMarkerPath = join(tmpdir(), Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10)); try { fs.writeFileSync(randomWaitMarkerPath, ''); // use built-in fs to avoid dragging in more dependencies diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index 8f0a3b204c3..a3b24d7c4a7 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -113,6 +113,15 @@ export interface IAuthenticationContribution { readonly label: string; } +export interface IGettingStartedContent { + readonly id: string; + readonly title: string; + readonly description: string; + readonly button: { title: string } & ({ command?: never, link: string } | { command: string, link?: never }), + readonly media: { path: string | { hc: string, light: string, dark: string }, altText: string }, + readonly when?: string; +} + export interface IExtensionContributions { commands?: ICommand[]; configuration?: IConfiguration | IConfiguration[]; @@ -132,6 +141,7 @@ export interface IExtensionContributions { readonly customEditors?: readonly IWebviewEditor[]; readonly codeActions?: readonly ICodeActionContribution[]; authentication?: IAuthenticationContribution[]; + gettingStarted?: IGettingStartedContent[]; } export type ExtensionKind = 'ui' | 'workspace' | 'web'; 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/keybinding/test/common/abstractKeybindingService.test.ts b/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts index d627e662606..ec890db1260 100644 --- a/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts +++ b/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts @@ -118,6 +118,7 @@ suite('AbstractKeybindingService', () => { contextMatchesRules: undefined!, getContextKeyValue: undefined!, createScoped: undefined!, + createOverlay: undefined!, getContext: (target: IContextKeyServiceTarget): any => { return currentContextValue; }, diff --git a/src/vs/platform/keybinding/test/common/mockKeybindingService.ts b/src/vs/platform/keybinding/test/common/mockKeybindingService.ts index bc03b1feabc..2fe7bbabca9 100644 --- a/src/vs/platform/keybinding/test/common/mockKeybindingService.ts +++ b/src/vs/platform/keybinding/test/common/mockKeybindingService.ts @@ -66,6 +66,9 @@ export class MockContextKeyService implements IContextKeyService { public createScoped(domNode: HTMLElement): IContextKeyService { return this; } + public createOverlay(): IContextKeyService { + return this; + } updateParent(_parentContextKeyService: IContextKeyService): void { // no-op } 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/log/browser/log.ts b/src/vs/platform/log/browser/log.ts index 8b1d44c4b91..8fe0f14599d 100644 --- a/src/vs/platform/log/browser/log.ts +++ b/src/vs/platform/log/browser/log.ts @@ -9,6 +9,18 @@ interface IAutomatedWindow { codeAutomationLog(type: string, args: any[]): void; } +function logLevelToString(level: LogLevel): string { + switch (level) { + case LogLevel.Trace: return 'trace'; + case LogLevel.Debug: return 'debug'; + case LogLevel.Info: return 'info'; + case LogLevel.Warning: return 'warn'; + case LogLevel.Error: return 'error'; + case LogLevel.Critical: return 'critical'; + } + return 'info'; +} + /** * A logger that is used when VSCode is running in the web with * an automation such as playwright. We expect a global codeAutomationLog @@ -19,7 +31,7 @@ export class ConsoleLogInAutomationLogger extends AdapterLogger implements ILogg declare codeAutomationLog: any; constructor(logLevel: LogLevel = DEFAULT_LOG_LEVEL) { - super({ log: (type, args) => this.consoleLog(type, args) }, logLevel); + super({ log: (level, args) => this.consoleLog(logLevelToString(level), args) }, logLevel); } private consoleLog(type: string, args: any[]): void { diff --git a/src/vs/platform/log/common/log.ts b/src/vs/platform/log/common/log.ts index 524381951d1..8a2764bc6c4 100644 --- a/src/vs/platform/log/common/log.ts +++ b/src/vs/platform/log/common/log.ts @@ -314,44 +314,44 @@ export class ConsoleLogger extends AbstractLogger implements ILogger { export class AdapterLogger extends AbstractLogger implements ILogger { - constructor(private readonly adapter: { log: (type: string, args: any[]) => void }, logLevel: LogLevel = DEFAULT_LOG_LEVEL) { + constructor(private readonly adapter: { log: (logLevel: LogLevel, args: any[]) => void }, logLevel: LogLevel = DEFAULT_LOG_LEVEL) { super(); this.setLevel(logLevel); } trace(message: string, ...args: any[]): void { if (this.getLevel() <= LogLevel.Trace) { - this.adapter.log('trace', [this.extractMessage(message), ...args]); + this.adapter.log(LogLevel.Trace, [this.extractMessage(message), ...args]); } } debug(message: string, ...args: any[]): void { if (this.getLevel() <= LogLevel.Debug) { - this.adapter.log('debug', [this.extractMessage(message), ...args]); + this.adapter.log(LogLevel.Debug, [this.extractMessage(message), ...args]); } } info(message: string, ...args: any[]): void { if (this.getLevel() <= LogLevel.Info) { - this.adapter.log('info', [this.extractMessage(message), ...args]); + this.adapter.log(LogLevel.Info, [this.extractMessage(message), ...args]); } } warn(message: string | Error, ...args: any[]): void { if (this.getLevel() <= LogLevel.Warning) { - this.adapter.log('warn', [this.extractMessage(message), ...args]); + this.adapter.log(LogLevel.Warning, [this.extractMessage(message), ...args]); } } error(message: string | Error, ...args: any[]): void { if (this.getLevel() <= LogLevel.Error) { - this.adapter.log('error', [this.extractMessage(message), ...args]); + this.adapter.log(LogLevel.Error, [this.extractMessage(message), ...args]); } } critical(message: string | Error, ...args: any[]): void { if (this.getLevel() <= LogLevel.Critical) { - this.adapter.log('critical', [this.extractMessage(message), ...args]); + this.adapter.log(LogLevel.Critical, [this.extractMessage(message), ...args]); } } diff --git a/src/vs/platform/log/common/logIpc.ts b/src/vs/platform/log/common/logIpc.ts index 4c18165d9b4..4b8ac5fab13 100644 --- a/src/vs/platform/log/common/logIpc.ts +++ b/src/vs/platform/log/common/logIpc.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { LogLevel, ILogService, LogService, ILoggerService, ILogger, AbstractMessageLogger, ILoggerOptions } from 'vs/platform/log/common/log'; +import { LogLevel, ILogService, LogService, ILoggerService, ILogger, AbstractMessageLogger, ILoggerOptions, AdapterLogger } from 'vs/platform/log/common/log'; import { Event } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; @@ -34,72 +34,6 @@ export class LogLevelChannel implements IServerChannel { } -export class LoggerChannel implements IServerChannel { - - private consoleLogger: ILogger | undefined; - private readonly loggers = new Map(); - - constructor(private readonly loggerService: ILoggerService) { } - - listen(_: unknown, event: string): Event { - throw new Error(`Event not found: ${event}`); - } - - async call(_: unknown, command: string, arg?: any): Promise { - switch (command) { - case 'createConsoleLogger': return this.createConsoleLogger(); - case 'createLogger': this.createLogger(URI.revive(arg[0]), arg[1]); return; - case 'log': return this.log(URI.revive(arg[0]), arg[1]); - } - - throw new Error(`Call not found: ${command}`); - } - - private createConsoleLogger(): void { - this.consoleLogger = new class extends AbstractMessageLogger { - protected log(level: LogLevel, message: string) { - let consoleFn = console.log; - - switch (level) { - case LogLevel.Error: - consoleFn = console.error; - break; - case LogLevel.Warning: - consoleFn = console.warn; - break; - case LogLevel.Info: - consoleFn = console.info; - break; - } - - consoleFn.call(console, message); - } - }(); - } - - private createLogger(file: URI, options: ILoggerOptions): void { - this.loggers.set(file.toString(), this.loggerService.createLogger(file, options)); - } - - private log(file: URI | undefined, messages: [LogLevel, string][]): void { - const logger = file ? this.loggers.get(file.toString()) : this.consoleLogger; - if (!logger) { - throw new Error('Create the logger before logging'); - } - for (const [level, message] of messages) { - switch (level) { - case LogLevel.Trace: logger.trace(message); break; - case LogLevel.Debug: logger.debug(message); break; - case LogLevel.Info: logger.info(message); break; - case LogLevel.Warning: logger.warn(message); break; - case LogLevel.Error: logger.error(message); break; - case LogLevel.Critical: logger.critical(message); break; - default: throw new Error('Invalid log level'); - } - } - } -} - export class LogLevelChannelClient { constructor(private channel: IChannel) { } @@ -118,6 +52,118 @@ export class LogLevelChannelClient { } +export class LoggerChannel implements IServerChannel { + + private readonly loggers = new Map(); + + constructor(private readonly loggerService: ILoggerService) { } + + listen(_: unknown, event: string): Event { + throw new Error(`Event not found: ${event}`); + } + + async call(_: unknown, command: string, arg?: any): Promise { + switch (command) { + case 'createLogger': this.createLogger(URI.revive(arg[0]), arg[1]); return; + case 'log': return this.log(URI.revive(arg[0]), arg[1]); + case 'consoleLog': return this.consoleLog(arg[0], arg[1]); + } + + throw new Error(`Call not found: ${command}`); + } + + private createLogger(file: URI, options: ILoggerOptions): void { + this.loggers.set(file.toString(), this.loggerService.createLogger(file, options)); + } + + private consoleLog(level: LogLevel, args: any[]): void { + let consoleFn = console.log; + + switch (level) { + case LogLevel.Error: + consoleFn = console.error; + break; + case LogLevel.Warning: + consoleFn = console.warn; + break; + case LogLevel.Info: + consoleFn = console.info; + break; + } + + consoleFn.call(console, ...args); + } + + private log(file: URI, messages: [LogLevel, string][]): void { + const logger = this.loggers.get(file.toString()); + if (!logger) { + throw new Error('Create the logger before logging'); + } + for (const [level, message] of messages) { + switch (level) { + case LogLevel.Trace: logger.trace(message); break; + case LogLevel.Debug: logger.debug(message); break; + case LogLevel.Info: logger.info(message); break; + case LogLevel.Warning: logger.warn(message); break; + case LogLevel.Error: logger.error(message); break; + case LogLevel.Critical: logger.critical(message); break; + default: throw new Error('Invalid log level'); + } + } + } +} + +export class LoggerChannelClient implements ILoggerService { + + declare readonly _serviceBrand: undefined; + + constructor(private readonly channel: IChannel) { } + + createConsoleMainLogger(): ILogger { + return new AdapterLogger({ + log: (level: LogLevel, args: any[]) => { + this.channel.call('consoleLog', [level, args]); + } + }); + } + + createLogger(file: URI, options?: ILoggerOptions): ILogger { + return new Logger(this.channel, file, options); + } + +} + +class Logger extends AbstractMessageLogger { + + private isLoggerCreated: boolean = false; + private buffer: [LogLevel, string][] = []; + + constructor( + private readonly channel: IChannel, + private readonly file: URI, + loggerOptions?: ILoggerOptions, + ) { + super(loggerOptions?.always); + this.channel.call('createLogger', [file, loggerOptions]) + .then(() => { + this._log(this.buffer); + this.isLoggerCreated = true; + }); + } + + protected log(level: LogLevel, message: string) { + this._log([[level, message]]); + } + + private _log(messages: [LogLevel, string][]) { + if (this.isLoggerCreated) { + this.channel.call('log', [this.file, messages]); + } else { + this.buffer.push(...messages); + } + } +} + export class FollowerLogService extends LogService implements ILogService { declare readonly _serviceBrand: undefined; 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 92b8c1a6d52..06f3213babb 100644 --- a/src/vs/platform/product/common/productService.ts +++ b/src/vs/platform/product/common/productService.ts @@ -23,7 +23,6 @@ export interface IBuiltInExtension { } export type ConfigurationSyncStore = { - web?: Partial>, url: string, insidersUrl: string, stableUrl: string, @@ -45,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/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 565a694166a..bc1b3715a9f 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -142,7 +142,11 @@ export enum TerminalIpcChannels { /** * Deals with logging from the pty host process. */ - Log = 'log' + Log = 'log', + /** + * Enables the detection of unresponsive pty hosts. + */ + Heartbeat = 'heartbeat' } export interface IPtyService { @@ -150,6 +154,7 @@ export interface IPtyService { readonly onPtyHostExit?: Event; readonly onPtyHostStart?: Event; + readonly onPtyHostUnresponsive?: Event; readonly onProcessData: Event<{ id: number, event: IProcessDataEvent | string }>; readonly onProcessExit: Event<{ id: number, event: number | undefined }>; @@ -159,6 +164,10 @@ export interface IPtyService { readonly onProcessResolvedShellLaunchConfig: Event<{ id: number, event: IShellLaunchConfig }>; readonly onProcessReplay: Event<{ id: number, event: IPtyHostProcessReplayEvent }>; + restartPtyHost?(): Promise; + + shutdownAll?(): Promise; + createProcess( shellLaunchConfig: IShellLaunchConfig, cwd: string, @@ -190,11 +199,40 @@ export interface IPtyService { getLatency(id: number): Promise; setTerminalLayoutInfo(args: ISetTerminalLayoutInfoArgs): void; + getTerminalLayoutInfo(args: IGetTerminalLayoutInfoArgs): Promise; orphanQuestionReply(args: IOrphanQuestionReplyArgs): void; } +export enum HeartbeatConstants { + /** + * The duration between heartbeats + */ + BeatInterval = 5000, + /** + * Defines a multiplier for BeatInterval for how long to wait before starting the second wait + * timer. + */ + FirstWaitMultiplier = 1.2, + /** + * Defines a multiplier for BeatInterval for how long to wait before telling the user about + * non-responsiveness. The second timer is to avoid informing the user incorrectly when waking + * the computer up from sleep + */ + SecondWaitMultiplier = 1, + /** + * How long to wait before telling the user about non-responsiveness when they try to create a + * process. This short circuits the standard wait timeouts to tell the user sooner and only + * create process is handled to avoid additional perf overhead. + */ + CreateProcessTimeout = 2000 +} + +export interface IHeartbeatService { + readonly onBeat: Event; +} + export interface IShellLaunchConfig { /** * The name of the terminal, if this is not set the name of the process will be used. diff --git a/src/vs/platform/terminal/electron-browser/localPtyService.ts b/src/vs/platform/terminal/electron-browser/localPtyService.ts index 038e8ca2593..c38b0ff8ab1 100644 --- a/src/vs/platform/terminal/electron-browser/localPtyService.ts +++ b/src/vs/platform/terminal/electron-browser/localPtyService.ts @@ -5,7 +5,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; -import { IProcessDataEvent, IPtyService, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, ITerminalsLayoutInfo, TerminalIpcChannels } from 'vs/platform/terminal/common/terminal'; +import { IPtyService, IProcessDataEvent, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, ITerminalsLayoutInfo, TerminalIpcChannels, IHeartbeatService, HeartbeatConstants } from 'vs/platform/terminal/common/terminal'; import { Client } from 'vs/base/parts/ipc/node/ipc.cp'; import { FileAccess } from 'vs/base/common/network'; import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc'; @@ -21,24 +21,31 @@ enum Constants { export class LocalPtyService extends Disposable implements IPtyService { declare readonly _serviceBrand: undefined; + private _client: Client; // ProxyChannel is not used here because events get lost when forwarding across multiple proxies private _proxy: IPtyService; private _restartCount = 0; private _isDisposed = false; + private _heartbeatFirstTimeout?: NodeJS.Timeout; + private _heartbeatSecondTimeout?: NodeJS.Timeout; + private readonly _onPtyHostExit = this._register(new Emitter()); readonly onPtyHostExit = this._onPtyHostExit.event; private readonly _onPtyHostStart = this._register(new Emitter()); readonly onPtyHostStart = this._onPtyHostStart.event; - private readonly _onProcessReplay = this._register(new Emitter<{ id: number, event: IPtyHostProcessReplayEvent }>()); - readonly onProcessReplay = this._onProcessReplay.event; + private readonly _onPtyHostUnresponsive = this._register(new Emitter()); + readonly onPtyHostUnresponsive = this._onPtyHostUnresponsive.event; + private readonly _onProcessData = this._register(new Emitter<{ id: number, event: IProcessDataEvent | string }>()); readonly onProcessData = this._onProcessData.event; private readonly _onProcessExit = this._register(new Emitter<{ id: number, event: number | undefined }>()); readonly onProcessExit = this._onProcessExit.event; private readonly _onProcessReady = this._register(new Emitter<{ id: number, event: { pid: number, cwd: string } }>()); readonly onProcessReady = this._onProcessReady.event; + private readonly _onProcessReplay = this._register(new Emitter<{ id: number, event: IPtyHostProcessReplayEvent }>()); + readonly onProcessReplay = this._onProcessReplay.event; private readonly _onProcessTitleChanged = this._register(new Emitter<{ id: number, event: string }>()); readonly onProcessTitleChanged = this._onProcessTitleChanged.event; private readonly _onProcessOverrideDimensions = this._register(new Emitter<{ id: number, event: ITerminalDimensionsOverride | undefined }>()); @@ -51,10 +58,10 @@ export class LocalPtyService extends Disposable implements IPtyService { ) { super(); - this._proxy = this._startPtyHost(); + [this._client, this._proxy] = this._startPtyHost(); } - private _startPtyHost(): IPtyService { + private _startPtyHost(): [Client, IPtyService] { const client = this._register(new Client( FileAccess.asFileUri('bootstrap-fork', require).fsPath, { @@ -69,15 +76,22 @@ export class LocalPtyService extends Disposable implements IPtyService { )); this._onPtyHostStart.fire(); + const heartbeatService = ProxyChannel.toService(client.getChannel(TerminalIpcChannels.Heartbeat)); + heartbeatService.onBeat(() => this._handleHeartbeat()); + // Handle exit - this._register({ dispose: () => client.dispose() }); + this._register({ + dispose: () => { + this._disposePtyHost(); + } + }); this._register(client.onDidProcessExit(e => { this._onPtyHostExit.fire(e.code); if (!this._isDisposed) { if (this._restartCount <= Constants.MaxRestarts) { this._logService.error(`ptyHost terminated unexpectedly with code ${e.code}`); this._restartCount++; - this._proxy = this._startPtyHost(); + this.restartPtyHost(); } else { this._logService.error(`ptyHost terminated unexpectedly with code ${e.code}, giving up`); } @@ -102,21 +116,26 @@ export class LocalPtyService extends Disposable implements IPtyService { this._logService.info(`Replaying ${e}`); this._onProcessReplay.fire(e); })); - return proxy; + return [client, proxy]; } dispose() { this._isDisposed = true; super.dispose(); } - createProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, executableEnv: IProcessEnvironment, windowsEnableConpty: boolean, workspaceId: string, workspaceName: string): Promise { - this._logService.info('LocalPtyService.createProcess'); - return this._proxy.createProcess(shellLaunchConfig, cwd, cols, rows, env, executableEnv, windowsEnableConpty, workspaceId, workspaceName); - } + fetchPersistentTerminalProcess(id: number): Promise { this._logService.info('LocalPtyService.fetchPersistentTerminalProcess'); return this._proxy.fetchPersistentTerminalProcess(id); } + + async createProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, executableEnv: IProcessEnvironment, windowsEnableConpty: boolean, workspaceId: string, workspaceName: string): Promise { + const timeout = setTimeout(() => this._handleUnresponsiveCreateProcess(), HeartbeatConstants.CreateProcessTimeout); + const result = await this._proxy.createProcess(shellLaunchConfig, cwd, cols, rows, env, executableEnv, windowsEnableConpty, workspaceId, workspaceName); + clearTimeout(timeout); + return result; + } + start(id: number): Promise { return this._proxy.start(id); } @@ -150,4 +169,50 @@ export class LocalPtyService extends Disposable implements IPtyService { public async getTerminalLayoutInfo(args: IGetTerminalLayoutInfoArgs): Promise { return await this._proxy.getTerminalLayoutInfo(args); } + + async restartPtyHost(): Promise { + this._disposePtyHost(); + [this._client, this._proxy] = this._startPtyHost(); + } + + private _disposePtyHost(): void { + if (this._proxy.shutdownAll) { + this._proxy.shutdownAll(); + } + this._client.dispose(); + } + + private _handleHeartbeat() { + this._clearHeartbeatTimeouts(); + this._heartbeatFirstTimeout = setTimeout(() => this._handleHeartbeatFirstTimeout(), HeartbeatConstants.BeatInterval * HeartbeatConstants.FirstWaitMultiplier); + } + + private _handleHeartbeatFirstTimeout() { + this._logService.warn(`No ptyHost heartbeat after ${HeartbeatConstants.BeatInterval * HeartbeatConstants.FirstWaitMultiplier}ms`); + this._heartbeatFirstTimeout = undefined; + this._heartbeatSecondTimeout = setTimeout(() => this._handleHeartbeatSecondTimeout(), HeartbeatConstants.BeatInterval * HeartbeatConstants.SecondWaitMultiplier); + } + + private _handleHeartbeatSecondTimeout() { + this._logService.error(`No ptyHost heartbeat after ${HeartbeatConstants.BeatInterval * HeartbeatConstants.FirstWaitMultiplier}ms!`); + this._heartbeatSecondTimeout = undefined; + this._onPtyHostUnresponsive.fire(); + } + + private _handleUnresponsiveCreateProcess() { + this._clearHeartbeatTimeouts(); + this._logService.error(`No ptyHost response to createProcess after ${HeartbeatConstants.CreateProcessTimeout}ms`); + this._onPtyHostUnresponsive.fire(); + } + + private _clearHeartbeatTimeouts() { + if (this._heartbeatFirstTimeout) { + clearTimeout(this._heartbeatFirstTimeout); + this._heartbeatFirstTimeout = undefined; + } + if (this._heartbeatSecondTimeout) { + clearTimeout(this._heartbeatSecondTimeout); + this._heartbeatSecondTimeout = undefined; + } + } } diff --git a/src/vs/platform/terminal/node/heartbeatService.ts b/src/vs/platform/terminal/node/heartbeatService.ts new file mode 100644 index 00000000000..3a414958afd --- /dev/null +++ b/src/vs/platform/terminal/node/heartbeatService.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter } from 'vs/base/common/event'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { HeartbeatConstants, IHeartbeatService } from 'vs/platform/terminal/common/terminal'; + +export class HeartbeatService extends Disposable implements IHeartbeatService { + private readonly _onBeat = this._register(new Emitter()); + readonly onBeat = this._onBeat.event; + + constructor() { + super(); + + const interval = setInterval(() => { + this._onBeat.fire(); + }, HeartbeatConstants.BeatInterval); + this._register(toDisposable(() => clearInterval(interval))); + } +} diff --git a/src/vs/platform/terminal/node/ptyHostMain.ts b/src/vs/platform/terminal/node/ptyHostMain.ts index 041e7d814c5..9515ddc130a 100644 --- a/src/vs/platform/terminal/node/ptyHostMain.ts +++ b/src/vs/platform/terminal/node/ptyHostMain.ts @@ -9,6 +9,7 @@ import { PtyService } from 'vs/platform/terminal/node/ptyService'; import { TerminalIpcChannels } from 'vs/platform/terminal/common/terminal'; import { ConsoleLogger, LogService } from 'vs/platform/log/common/log'; import { LogLevelChannel } from 'vs/platform/log/common/logIpc'; +import { HeartbeatService } from 'vs/platform/terminal/node/heartbeatService'; const server = new Server('ptyHost'); @@ -16,10 +17,14 @@ const logService = new LogService(new ConsoleLogger()); const logChannel = new LogLevelChannel(logService); server.registerChannel(TerminalIpcChannels.Log, logChannel); -const service = new PtyService(logService); -server.registerChannel(TerminalIpcChannels.PtyHost, ProxyChannel.fromService(service)); +const heartbeatService = new HeartbeatService(); +server.registerChannel(TerminalIpcChannels.Heartbeat, ProxyChannel.fromService(heartbeatService)); + +const ptyService = new PtyService(logService); +server.registerChannel(TerminalIpcChannels.PtyHost, ProxyChannel.fromService(ptyService)); process.once('exit', () => { logService.dispose(); - service.dispose(); + heartbeatService.dispose(); + ptyService.dispose(); }); diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index d2c85301bb9..7d3bbd05485 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -14,10 +14,9 @@ import { ISetTerminalLayoutInfoArgs, ITerminalTabLayoutInfoDto, IPtyHostDescript import { ILogService } from 'vs/platform/log/common/log'; import { createRandomIPCHandle } from 'vs/base/parts/ipc/node/ipc.net'; -let persistentTerminalId = 0; - +// TODO: On disconnect/restart, this will overwrite the older terminals +let currentPtyId = 0; type WorkspaceId = string; - export class PtyService extends Disposable implements IPtyService { declare readonly _serviceBrand: undefined; @@ -25,6 +24,9 @@ export class PtyService extends Disposable implements IPtyService { private readonly _workspaceLayoutInfos = new Map(); + private readonly _onHeartbeat = this._register(new Emitter()); + readonly onHeartbeat = this._onHeartbeat.event; + private readonly _onProcessData = this._register(new Emitter<{ id: number, event: IProcessDataEvent | string }>()); readonly onProcessData = this._onProcessData.event; private readonly _onProcessReplay = this._register(new Emitter<{ id: number, event: IPtyHostProcessReplayEvent }>()); @@ -75,7 +77,7 @@ export class PtyService extends Disposable implements IPtyService { } async createProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, executableEnv: IProcessEnvironment, windowsEnableConpty: boolean, workspaceId: string, workspaceName: string): Promise { - const id = ++persistentTerminalId; + const id = ++currentPtyId; this._logService.info('creating process', id); const process = new TerminalProcess(shellLaunchConfig, cwd, cols, rows, env, executableEnv, windowsEnableConpty, this._logService); process.onProcessData(event => this._onProcessData.fire({ id, event })); @@ -100,8 +102,11 @@ export class PtyService extends Disposable implements IPtyService { } async start(id: number): Promise { - let terminalProcess = this._throwIfNoPty(id); - return terminalProcess.start(); + return this._throwIfNoPty(id).start(); + } + + async shutdownAll(): Promise { + this.dispose(); } async shutdown(id: number, immediate: boolean): Promise { @@ -242,7 +247,7 @@ export class PersistentTerminalProcess extends Disposable { cols: number, rows: number, ipcHandlePath: string, private readonly _logService: ILogService, - private readonly _onExit: () => void, + private readonly _onExit: () => void ) { super(); this._recorder = new TerminalRecorder(cols, rows); @@ -282,19 +287,19 @@ export class PersistentTerminalProcess extends Disposable { // console.trace(`data ${e}`); this._recorder.recordData(e); })); - // this._register(this._terminalProcess.onProcessExit(exitCode => { - // this._bufferer.stopBuffering(this._persistentTerminalId); + this._register(this._terminalProcess.onProcessExit(exitCode => { + // this._bufferer.stopBuffering(this._persistentTerminalId); - // // const ev: IPtyHostProcessExitEvent = { - // // type: 'exit', - // // exitCode: exitCode - // // }; - // // this._events.fire(ev); + // const ev: IPtyHostProcessExitEvent = { + // type: 'exit', + // exitCode: exitCode + // }; + // this._events.fire(ev); - // // Remove process reference - // // TODO: Use an event - // this._onExit(); - // })); + // Remove process reference + // TODO: Use an event + this._onExit(); + })); } acknowledgeDataEvent(charCount: number): void { throw new Error('Method not implemented.'); 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/userDataSync/common/abstractSynchronizer.ts b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts index b8be4d3be94..0124ffeab7d 100644 --- a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts +++ b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts @@ -34,7 +34,7 @@ type SyncSourceClassification = { source?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; }; -function isSyncData(thing: any): thing is ISyncData { +export function isSyncData(thing: any): thing is ISyncData { if (thing && (thing.version !== undefined && typeof thing.version === 'number') && (thing.content !== undefined && typeof thing.content === 'string')) { diff --git a/src/vs/platform/userDataSync/common/globalStateMerge.ts b/src/vs/platform/userDataSync/common/globalStateMerge.ts index e519854a95b..cea1c3d6d9f 100644 --- a/src/vs/platform/userDataSync/common/globalStateMerge.ts +++ b/src/vs/platform/userDataSync/common/globalStateMerge.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as objects from 'vs/base/common/objects'; -import { IStorageValue } from 'vs/platform/userDataSync/common/userDataSync'; +import { IStorageValue, SYNC_SERVICE_URL_TYPE } from 'vs/platform/userDataSync/common/userDataSync'; import { IStringDictionary } from 'vs/base/common/collections'; import { ILogService } from 'vs/platform/log/common/log'; @@ -30,15 +30,17 @@ export function merge(localStorage: IStringDictionary, remoteStor const local: { added: IStringDictionary, removed: string[], updated: IStringDictionary } = { added: {}, removed: [], updated: {} }; const remote: IStringDictionary = objects.deepClone(remoteStorage); + const isFirstTimeSync = !baseStorage; + // Added in local for (const key of baseToLocal.added.values()) { - // Skip if local was not synced before and remote also has the key - // In this case, remote gets precedence - if (!baseStorage && baseToRemote.added.has(key)) { + // If syncing for first time remote value gets precedence always, + // except for sync service type key - local value takes precedence for this key + if (key !== SYNC_SERVICE_URL_TYPE && isFirstTimeSync && baseToRemote.added.has(key)) { continue; - } else { - remote[key] = localStorage[key]; } + + remote[key] = localStorage[key]; } // Updated in local @@ -70,6 +72,12 @@ export function merge(localStorage: IStringDictionary, remoteStor if (localValue && localValue.value === remoteValue.value) { continue; } + + // Local sync service type value takes precedence if syncing for first time + if (key === SYNC_SERVICE_URL_TYPE && isFirstTimeSync && baseToLocal.added.has(key)) { + continue; + } + if (localValue) { local.updated[key] = remoteValue; } else { diff --git a/src/vs/platform/userDataSync/common/globalStateSync.ts b/src/vs/platform/userDataSync/common/globalStateSync.ts index 7c69ad4635a..2e58f17ae95 100644 --- a/src/vs/platform/userDataSync/common/globalStateSync.ts +++ b/src/vs/platform/userDataSync/common/globalStateSync.ts @@ -5,7 +5,7 @@ import { IUserDataSyncStoreService, IUserDataSyncLogService, IGlobalState, SyncResource, IUserDataSynchroniser, IUserDataSyncResourceEnablementService, - IUserDataSyncBackupStoreService, ISyncResourceHandle, IStorageValue, USER_DATA_SYNC_SCHEME, IRemoteUserData, Change + IUserDataSyncBackupStoreService, ISyncResourceHandle, IStorageValue, USER_DATA_SYNC_SCHEME, IRemoteUserData, Change, ALL_SYNC_RESOURCES, getEnablementKey, SYNC_SERVICE_URL_TYPE, UserDataSyncStoreType, IUserData, ISyncData, createSyncHeaders, UserDataSyncError, UserDataSyncErrorCode } from 'vs/platform/userDataSync/common/userDataSync'; import { VSBuffer } from 'vs/base/common/buffer'; import { Event } from 'vs/base/common/event'; @@ -15,7 +15,7 @@ import { IStringDictionary } from 'vs/base/common/collections'; import { edit } from 'vs/platform/userDataSync/common/content'; import { merge } from 'vs/platform/userDataSync/common/globalStateMerge'; import { parse } from 'vs/base/common/json'; -import { AbstractInitializer, AbstractSynchroniser, IAcceptResult, IMergeResult, IResourcePreview } from 'vs/platform/userDataSync/common/abstractSynchronizer'; +import { AbstractInitializer, AbstractSynchroniser, IAcceptResult, IMergeResult, IResourcePreview, isSyncData } from 'vs/platform/userDataSync/common/abstractSynchronizer'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { URI } from 'vs/base/common/uri'; @@ -23,6 +23,12 @@ import { format } from 'vs/base/common/jsonFormatter'; import { applyEdits } from 'vs/base/common/jsonEdit'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { isWeb } from 'vs/base/common/platform'; +import { UserDataSyncStoreClient } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; +import { getServiceMachineId } from 'vs/platform/serviceMachineId/common/serviceMachineId'; +import { generateUuid } from 'vs/base/common/uuid'; +import { IHeaders } from 'vs/base/parts/request/common/request'; +import { ILogService } from 'vs/platform/log/common/log'; const argvStoragePrefx = 'globalState.argv.'; const argvProperties: string[] = ['locale']; @@ -40,6 +46,18 @@ export interface IGlobalStateResourcePreview extends IResourcePreview { readonly storageKeys: StorageKeys; } +function formatAndStringify(globalState: IGlobalState): string { + const storageKeys = globalState.storage ? Object.keys(globalState.storage).sort() : []; + const storage: IStringDictionary = {}; + storageKeys.forEach(key => storage[key] = globalState.storage[key]); + globalState.storage = storage; + const content = JSON.stringify(globalState); + const edits = format(content, undefined, {}); + return applyEdits(content, edits); +} + +const GLOBAL_STATE_DATA_VERSION = 1; + /** * Synchronises global state that includes * - Global storage with user scope @@ -52,7 +70,7 @@ export interface IGlobalStateResourcePreview extends IResourcePreview { export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUserDataSynchroniser { private static readonly GLOBAL_STATE_DATA_URI = URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'globalState', path: `/globalState.json` }); - protected readonly version: number = 1; + protected readonly version: number = GLOBAL_STATE_DATA_VERSION; private readonly previewResource: URI = this.extUri.joinPath(this.syncPreviewFolder, 'globalState.json'); private readonly localResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }); private readonly remoteResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }); @@ -107,10 +125,10 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs return [{ localResource: this.localResource, - localContent: this.format(localGloablState), + localContent: formatAndStringify(localGloablState), localUserData: localGloablState, remoteResource: this.remoteResource, - remoteContent: remoteGlobalState ? this.format(remoteGlobalState) : null, + remoteContent: remoteGlobalState ? formatAndStringify(remoteGlobalState) : null, previewResource: this.previewResource, previewResult, localChange: previewResult.localChange, @@ -215,7 +233,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs async resolveContent(uri: URI): Promise { if (this.extUri.isEqual(uri, GlobalStateSynchroniser.GLOBAL_STATE_DATA_URI)) { const localGlobalState = await this.getLocalGlobalState(); - return this.format(localGlobalState); + return formatAndStringify(localGlobalState); } if (this.extUri.isEqual(this.remoteResource, uri) || this.extUri.isEqual(this.localResource, uri) || this.extUri.isEqual(this.acceptedResource, uri)) { @@ -233,7 +251,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs if (syncData) { switch (this.extUri.basename(uri)) { case 'globalState.json': - return this.format(JSON.parse(syncData.content)); + return formatAndStringify(JSON.parse(syncData.content)); } } } @@ -241,16 +259,6 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs return null; } - private format(globalState: IGlobalState): string { - const storageKeys = globalState.storage ? Object.keys(globalState.storage).sort() : []; - const storage: IStringDictionary = {}; - storageKeys.forEach(key => storage[key] = globalState.storage[key]); - globalState.storage = storage; - const content = JSON.stringify(globalState); - const edits = format(content, undefined, {}); - return applyEdits(content, edits); - } - async hasLocalData(): Promise { try { const { storage } = await this.getLocalGlobalState(); @@ -346,6 +354,14 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs const machine = this.storageService.keys(StorageScope.GLOBAL, StorageTarget.MACHINE); const registered = [...user, ...machine]; const unregistered = lastSyncGlobalState?.storage ? Object.keys(lastSyncGlobalState.storage).filter(key => !key.startsWith(argvStoragePrefx) && !registered.includes(key) && this.storageService.get(key, StorageScope.GLOBAL) !== undefined) : []; + + if (!isWeb) { + // Following keys are synced only in web. Do not sync these keys in other platforms + const keysSyncedOnlyInWeb = [...ALL_SYNC_RESOURCES.map(resource => getEnablementKey(resource)), SYNC_SERVICE_URL_TYPE]; + unregistered.push(...keysSyncedOnlyInWeb); + machine.push(...keysSyncedOnlyInWeb); + } + return { user, machine, unregistered }; } } @@ -401,3 +417,62 @@ export class GlobalStateInitializer extends AbstractInitializer { } +export class UserDataSyncStoreTypeSynchronizer { + + constructor( + private readonly userDataSyncStoreClient: UserDataSyncStoreClient, + @IStorageService private readonly storageService: IStorageService, + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IFileService private readonly fileService: IFileService, + @ILogService private readonly logService: ILogService, + ) { + } + + getSyncStoreType(userData: IUserData): UserDataSyncStoreType | undefined { + const remoteGlobalState = this.parseGlobalState(userData); + return remoteGlobalState?.storage[SYNC_SERVICE_URL_TYPE]?.value as UserDataSyncStoreType; + } + + async sync(userDataSyncStoreType: UserDataSyncStoreType): Promise { + const syncHeaders = createSyncHeaders(generateUuid()); + try { + return await this.doSync(userDataSyncStoreType, syncHeaders); + } catch (e) { + if (e instanceof UserDataSyncError) { + switch (e.code) { + case UserDataSyncErrorCode.PreconditionFailed: + this.logService.info(`Failed to synchronize UserDataSyncStoreType as there is a new remote version available. Synchronizing again...`); + return this.doSync(userDataSyncStoreType, syncHeaders); + } + } + throw e; + } + } + + private async doSync(userDataSyncStoreType: UserDataSyncStoreType, syncHeaders: IHeaders): Promise { + // Read the global state from remote + const globalStateUserData = await this.userDataSyncStoreClient.read(SyncResource.GlobalState, null, syncHeaders); + const remoteGlobalState = this.parseGlobalState(globalStateUserData) || { storage: {} }; + + // Update the sync store type + remoteGlobalState.storage[SYNC_SERVICE_URL_TYPE] = { value: userDataSyncStoreType, version: GLOBAL_STATE_DATA_VERSION }; + + // Write the global state to remote + const machineId = await getServiceMachineId(this.environmentService, this.fileService, this.storageService); + const syncDataToUpdate: ISyncData = { version: GLOBAL_STATE_DATA_VERSION, machineId, content: formatAndStringify(remoteGlobalState) }; + await this.userDataSyncStoreClient.write(SyncResource.GlobalState, JSON.stringify(syncDataToUpdate), globalStateUserData.ref, syncHeaders); + } + + private parseGlobalState({ content }: IUserData): IGlobalState | null { + if (!content) { + return null; + } + const syncData = JSON.parse(content); + if (isSyncData(syncData)) { + return syncData ? JSON.parse(syncData.content) : null; + } + throw new Error('Invalid remote data'); + } + +} + diff --git a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts index 0a4a2fded18..0d258c8e48c 100644 --- a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts @@ -18,6 +18,8 @@ import { localize } from 'vs/nls'; import { toLocalISOString } from 'vs/base/common/date'; import { URI } from 'vs/base/common/uri'; import { isEqual } from 'vs/base/common/resources'; +import { isWeb } from 'vs/base/common/platform'; +import { IProductService } from 'vs/platform/product/common/productService'; type AutoSyncClassification = { sources: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; @@ -36,6 +38,7 @@ const enablementKey = 'sync.enable'; const disableMachineEventuallyKey = 'sync.disableMachineEventually'; const sessionIdKey = 'sync.sessionId'; const storeUrlKey = 'sync.storeUrl'; +const productQualityKey = 'sync.productQuality'; interface _IUserDataAutoSyncEnablementService extends IUserDataAutoSyncEnablementService { canToggleEnablement(): boolean; @@ -73,9 +76,10 @@ export class UserDataAutoSyncEnablementService extends Disposable implements _IU } setEnablement(enabled: boolean): void { - if (this.canToggleEnablement()) { - this.storageService.store(enablementKey, enabled, StorageScope.GLOBAL, StorageTarget.MACHINE); + if (enabled && !this.canToggleEnablement()) { + return; } + this.storageService.store(enablementKey, enabled, StorageScope.GLOBAL, StorageTarget.MACHINE); } private onDidStorageChange(storageChangeEvent: IStorageValueChangeEvent): void { @@ -118,7 +122,20 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto } } + private previousProductQuality: string | undefined; + private get productQuality(): string | undefined { + return this.storageService.get(productQualityKey, StorageScope.GLOBAL); + } + private set productQuality(productQuality: string | undefined) { + if (productQuality) { + this.storageService.store(productQualityKey, productQuality, StorageScope.GLOBAL, StorageTarget.MACHINE); + } else { + this.storageService.remove(productQualityKey, StorageScope.GLOBAL); + } + } + constructor( + @IProductService productService: IProductService, @IUserDataSyncStoreManagementService private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService, @IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService, @IUserDataSyncResourceEnablementService private readonly userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, @@ -137,6 +154,9 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto this.lastSyncUrl = this.syncUrl; this.syncUrl = userDataSyncStoreManagementService.userDataSyncStore?.url; + this.previousProductQuality = this.productQuality; + this.productQuality = productService.quality; + if (this.syncUrl) { this.logService.info('Using settings sync service', this.syncUrl.toString()); @@ -165,6 +185,7 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto this._register(userDataSyncStoreService.onDidChangeDonotMakeRequestsUntil(() => this.updateAutoSync())); this._register(Event.debounce(userDataSyncService.onDidChangeLocal, (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerSync(sources, false, false))); this._register(Event.filter(this.userDataSyncResourceEnablementService.onDidChangeResourceEnablement, ([, enabled]) => enabled)(() => this.triggerSync(['resourceEnablement'], false, false))); + this._register(this.userDataSyncStoreManagementService.onDidChangeUserDataSyncStore(() => this.triggerSync(['userDataSyncStoreChanged'], false, false))); } } @@ -256,6 +277,10 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto } } + private hasProductQualityChanged(): boolean { + return !!this.previousProductQuality && !!this.productQuality && this.previousProductQuality !== this.productQuality; + } + private async onDidFinishSync(error: Error | undefined): Promise { if (!error) { // Sync finished without errors @@ -313,9 +338,22 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto // Service changed else if (userDataSyncError.code === UserDataSyncErrorCode.ServiceChanged || userDataSyncError.code === UserDataSyncErrorCode.DefaultServiceChanged) { - await this.turnOff(false, true /* force soft turnoff on error */, true /* do not disable machine */); - await this.turnOn(); - this.logService.info('Auto Sync: Sync Service changed. Turned off auto sync, reset local state and turned on auto sync.'); + + // Check if default settings sync service has changed in web without changing the product quality + // Then turn off settings sync and ask user to turn on again + if (isWeb && userDataSyncError.code === UserDataSyncErrorCode.DefaultServiceChanged && !this.hasProductQualityChanged()) { + await this.turnOff(false, true /* force soft turnoff on error */); + this.logService.info('Auto Sync: Turned off sync because default sync service is changed.'); + } + + // Service has changed by the user. So turn off and turn on sync. + // Show a prompt to the user about service change. + else { + await this.turnOff(false, true /* force soft turnoff on error */, true /* do not disable machine */); + await this.turnOn(); + this.logService.info('Auto Sync: Sync Service changed. Turned off auto sync, reset local state and turned on auto sync.'); + } + } else { diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index 310fea185f2..d34583109a8 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -108,6 +108,7 @@ export type IAuthenticationProvider = { id: string, scopes: string[] }; export interface IUserDataSyncStore { readonly url: URI; + readonly type: UserDataSyncStoreType; readonly defaultUrl: URI; readonly stableUrl: URI; readonly insidersUrl: URI; @@ -192,6 +193,12 @@ export interface IUserDataSyncBackupStoreService { export const HEADER_OPERATION_ID = 'x-operation-id'; export const HEADER_EXECUTION_ID = 'X-Execution-Id'; +export function createSyncHeaders(executionId: string): IHeaders { + const headers: IHeaders = {}; + headers[HEADER_EXECUTION_ID] = executionId; + return headers; +} + //#endregion // #region User Data Sync Error @@ -385,6 +392,13 @@ export interface IUserDataSynchroniser { //#endregion +// #region keys synced only in web + +export const SYNC_SERVICE_URL_TYPE = 'sync.store.url.type'; +export function getEnablementKey(resource: SyncResource) { return `sync.enable.${resource}`; } + +// #endregion + // #region User Data Sync Services export const IUserDataSyncResourceEnablementService = createDecorator('IUserDataSyncResourceEnablementService'); diff --git a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts index f67dfb1c0b3..fc9acf98945 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts @@ -3,115 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IServerChannel, IChannel, IPCServer } from 'vs/base/parts/ipc/common/ipc'; +import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event } from 'vs/base/common/event'; -import { IUserDataSyncService, IUserDataSyncUtilService, IUserDataAutoSyncService, IManualSyncTask, IUserDataManifest, IUserDataSyncStoreManagementService, SyncStatus } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncUtilService, IUserDataAutoSyncService, IUserDataSyncStoreManagementService, UserDataSyncStoreType, IUserDataSyncStore } from 'vs/platform/userDataSync/common/userDataSync'; import { URI } from 'vs/base/common/uri'; import { IStringDictionary } from 'vs/base/common/collections'; import { FormattingOptions } from 'vs/base/common/jsonFormatter'; -import { ILogService } from 'vs/platform/log/common/log'; import { IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; - -export class UserDataSyncChannel implements IServerChannel { - - constructor(private server: IPCServer, private readonly service: IUserDataSyncService, private readonly logService: ILogService) { } - - listen(_: unknown, event: string): Event { - switch (event) { - case 'onDidChangeStatus': return this.service.onDidChangeStatus; - case 'onDidChangeConflicts': return this.service.onDidChangeConflicts; - case 'onDidChangeLocal': return this.service.onDidChangeLocal; - case 'onDidChangeLastSyncTime': return this.service.onDidChangeLastSyncTime; - case 'onSyncErrors': return this.service.onSyncErrors; - case 'onDidResetLocal': return this.service.onDidResetLocal; - case 'onDidResetRemote': return this.service.onDidResetRemote; - } - throw new Error(`Event not found: ${event}`); - } - - async call(context: any, command: string, args?: any): Promise { - try { - const result = await this._call(context, command, args); - return result; - } catch (e) { - this.logService.error(e); - throw e; - } - } - - private _call(context: any, command: string, args?: any): Promise { - switch (command) { - case '_getInitialData': return Promise.resolve([this.service.status, this.service.conflicts, this.service.lastSyncTime]); - - case 'createManualSyncTask': return this.createManualSyncTask(); - - case 'replace': return this.service.replace(URI.revive(args[0])); - case 'reset': return this.service.reset(); - case 'resetRemote': return this.service.resetRemote(); - case 'resetLocal': return this.service.resetLocal(); - case 'hasPreviouslySynced': return this.service.hasPreviouslySynced(); - case 'hasLocalData': return this.service.hasLocalData(); - case 'accept': return this.service.accept(args[0], URI.revive(args[1]), args[2], args[3]); - case 'resolveContent': return this.service.resolveContent(URI.revive(args[0])); - case 'getLocalSyncResourceHandles': return this.service.getLocalSyncResourceHandles(args[0]); - case 'getRemoteSyncResourceHandles': return this.service.getRemoteSyncResourceHandles(args[0]); - case 'getAssociatedResources': return this.service.getAssociatedResources(args[0], { created: args[1].created, uri: URI.revive(args[1].uri) }); - case 'getMachineId': return this.service.getMachineId(args[0], { created: args[1].created, uri: URI.revive(args[1].uri) }); - } - throw new Error('Invalid call'); - } - - private async createManualSyncTask(): Promise<{ id: string, manifest: IUserDataManifest | null, status: SyncStatus }> { - const manualSyncTask = await this.service.createManualSyncTask(); - const manualSyncTaskChannel = new ManualSyncTaskChannel(manualSyncTask, this.logService); - this.server.registerChannel(`manualSyncTask-${manualSyncTask.id}`, manualSyncTaskChannel); - return { id: manualSyncTask.id, manifest: manualSyncTask.manifest, status: manualSyncTask.status }; - } -} - -class ManualSyncTaskChannel implements IServerChannel { - - constructor( - private readonly manualSyncTask: IManualSyncTask, - private readonly logService: ILogService - ) { } - - listen(_: unknown, event: string): Event { - switch (event) { - case 'onSynchronizeResources': return this.manualSyncTask.onSynchronizeResources; - } - throw new Error(`Event not found: ${event}`); - } - - async call(context: any, command: string, args?: any): Promise { - try { - const result = await this._call(context, command, args); - return result; - } catch (e) { - this.logService.error(e); - throw e; - } - } - - private async _call(context: any, command: string, args?: any): Promise { - switch (command) { - case 'preview': return this.manualSyncTask.preview(); - case 'accept': return this.manualSyncTask.accept(URI.revive(args[0]), args[1]); - case 'merge': return this.manualSyncTask.merge(URI.revive(args[0])); - case 'discard': return this.manualSyncTask.discard(URI.revive(args[0])); - case 'discardConflicts': return this.manualSyncTask.discardConflicts(); - case 'apply': return this.manualSyncTask.apply(); - case 'pull': return this.manualSyncTask.pull(); - case 'push': return this.manualSyncTask.push(); - case 'stop': return this.manualSyncTask.stop(); - case '_getStatus': return this.manualSyncTask.status; - case 'dispose': return this.manualSyncTask.dispose(); - } - throw new Error('Invalid call'); - } - -} +import { Disposable } from 'vs/base/common/lifecycle'; export class UserDataAutoSyncChannel implements IServerChannel { @@ -235,3 +135,34 @@ export class UserDataSyncStoreManagementServiceChannel implements IServerChannel throw new Error('Invalid call'); } } + +export class UserDataSyncStoreManagementServiceChannelClient extends Disposable { + + readonly onDidChangeUserDataSyncStore: Event; + + constructor(private readonly channel: IChannel) { + super(); + this.onDidChangeUserDataSyncStore = this.channel.listen('onDidChangeUserDataSyncStore'); + } + + async switch(type: UserDataSyncStoreType): Promise { + return this.channel.call('switch', [type]); + } + + async getPreviousUserDataSyncStore(): Promise { + const userDataSyncStore = await this.channel.call('getPreviousUserDataSyncStore'); + return this.revive(userDataSyncStore); + } + + private revive(userDataSyncStore: IUserDataSyncStore): IUserDataSyncStore { + return { + url: URI.revive(userDataSyncStore.url), + type: userDataSyncStore.type, + defaultUrl: URI.revive(userDataSyncStore.defaultUrl), + insidersUrl: URI.revive(userDataSyncStore.insidersUrl), + stableUrl: URI.revive(userDataSyncStore.stableUrl), + canSwitch: userDataSyncStore.canSwitch, + authenticationProviders: userDataSyncStore.authenticationProviders, + }; + } +} diff --git a/src/vs/platform/userDataSync/common/userDataSyncResourceEnablementService.ts b/src/vs/platform/userDataSync/common/userDataSyncResourceEnablementService.ts index 4fd2b3c339e..c96fbad455b 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncResourceEnablementService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncResourceEnablementService.ts @@ -3,19 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IUserDataSyncResourceEnablementService, ALL_SYNC_RESOURCES, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncResourceEnablementService, ALL_SYNC_RESOURCES, SyncResource, getEnablementKey } from 'vs/platform/userDataSync/common/userDataSync'; import { Disposable } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; import { IStorageService, IStorageValueChangeEvent, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { isWeb } from 'vs/base/common/platform'; type SyncEnablementClassification = { enabled?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; }; -const enablementKey = 'sync.enable'; -function getEnablementKey(resource: SyncResource) { return `${enablementKey}.${resource}`; } - export class UserDataSyncResourceEnablementService extends Disposable implements IUserDataSyncResourceEnablementService { _serviceBrand: any; @@ -39,10 +37,14 @@ export class UserDataSyncResourceEnablementService extends Disposable implements if (this.isResourceEnabled(resource) !== enabled) { const resourceEnablementKey = getEnablementKey(resource); this.telemetryService.publicLog2<{ enabled: boolean }, SyncEnablementClassification>(resourceEnablementKey, { enabled }); - this.storageService.store(resourceEnablementKey, enabled, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storeResourceEnablement(resourceEnablementKey, enabled); } } + private storeResourceEnablement(resourceEnablementKey: string, enabled: boolean): void { + this.storageService.store(resourceEnablementKey, enabled, StorageScope.GLOBAL, isWeb ? StorageTarget.USER /* sync in web */ : StorageTarget.MACHINE); + } + private onDidStorageChange(storageChangeEvent: IStorageValueChangeEvent): void { if (storageChangeEvent.scope === StorageScope.GLOBAL) { const resourceKey = ALL_SYNC_RESOURCES.filter(resourceKey => getEnablementKey(resourceKey) === storageChangeEvent.key)[0]; diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index cf0d022f8ca..b059b81ee40 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -5,7 +5,7 @@ import { IUserDataSyncService, SyncStatus, IUserDataSyncStoreService, SyncResource, IUserDataSyncLogService, IUserDataSynchroniser, UserDataSyncErrorCode, - UserDataSyncError, ISyncResourceHandle, IUserDataManifest, ISyncTask, IResourcePreview, IManualSyncTask, ISyncResourcePreview, HEADER_EXECUTION_ID, MergeState, Change, IUserDataSyncStoreManagementService, UserDataSyncStoreError + UserDataSyncError, ISyncResourceHandle, IUserDataManifest, ISyncTask, IResourcePreview, IManualSyncTask, ISyncResourcePreview, MergeState, Change, IUserDataSyncStoreManagementService, UserDataSyncStoreError, createSyncHeaders } from 'vs/platform/userDataSync/common/userDataSync'; import { Disposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -37,12 +37,6 @@ type SyncErrorClassification = { const LAST_SYNC_TIME_KEY = 'sync.lastSyncTime'; -function createSyncHeaders(executionId: string): IHeaders { - const headers: IHeaders = {}; - headers[HEADER_EXECUTION_ID] = executionId; - return headers; -} - export class UserDataSyncService extends Disposable implements IUserDataSyncService { _serviceBrand: any; diff --git a/src/vs/platform/userDataSync/common/userDataSyncServiceIpc.ts b/src/vs/platform/userDataSync/common/userDataSyncServiceIpc.ts new file mode 100644 index 00000000000..ee55c0be9aa --- /dev/null +++ b/src/vs/platform/userDataSync/common/userDataSyncServiceIpc.ts @@ -0,0 +1,381 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { Emitter, Event } from 'vs/base/common/event'; +import { IUserDataSyncService, IManualSyncTask, IUserDataManifest, SyncStatus, IResourcePreview, ISyncResourceHandle, ISyncResourcePreview, ISyncTask, SyncResource, UserDataSyncError } from 'vs/platform/userDataSync/common/userDataSync'; +import { URI } from 'vs/base/common/uri'; +import { ILogService } from 'vs/platform/log/common/log'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { isArray } from 'vs/base/common/types'; + +type ManualSyncTaskEvent = { manualSyncTaskId: string, data: T }; + +export class UserDataSyncChannel implements IServerChannel { + + private readonly manualSyncTasks = new Map(); + private readonly onManualSynchronizeResources = new Emitter>(); + + constructor(private readonly service: IUserDataSyncService, private readonly logService: ILogService) { } + + listen(_: unknown, event: string): Event { + switch (event) { + // sync + case 'onDidChangeStatus': return this.service.onDidChangeStatus; + case 'onDidChangeConflicts': return this.service.onDidChangeConflicts; + case 'onDidChangeLocal': return this.service.onDidChangeLocal; + case 'onDidChangeLastSyncTime': return this.service.onDidChangeLastSyncTime; + case 'onSyncErrors': return this.service.onSyncErrors; + case 'onDidResetLocal': return this.service.onDidResetLocal; + case 'onDidResetRemote': return this.service.onDidResetRemote; + + // manual sync + case 'manualSync/onSynchronizeResources': return this.onManualSynchronizeResources.event; + } + + throw new Error(`Event not found: ${event}`); + } + + async call(context: any, command: string, args?: any): Promise { + try { + const result = await this._call(context, command, args); + return result; + } catch (e) { + this.logService.error(e); + throw e; + } + } + + private async _call(context: any, command: string, args?: any): Promise { + switch (command) { + + // sync + case '_getInitialData': return Promise.resolve([this.service.status, this.service.conflicts, this.service.lastSyncTime]); + case 'replace': return this.service.replace(URI.revive(args[0])); + case 'reset': return this.service.reset(); + case 'resetRemote': return this.service.resetRemote(); + case 'resetLocal': return this.service.resetLocal(); + case 'hasPreviouslySynced': return this.service.hasPreviouslySynced(); + case 'hasLocalData': return this.service.hasLocalData(); + case 'accept': return this.service.accept(args[0], URI.revive(args[1]), args[2], args[3]); + case 'resolveContent': return this.service.resolveContent(URI.revive(args[0])); + case 'getLocalSyncResourceHandles': return this.service.getLocalSyncResourceHandles(args[0]); + case 'getRemoteSyncResourceHandles': return this.service.getRemoteSyncResourceHandles(args[0]); + case 'getAssociatedResources': return this.service.getAssociatedResources(args[0], { created: args[1].created, uri: URI.revive(args[1].uri) }); + case 'getMachineId': return this.service.getMachineId(args[0], { created: args[1].created, uri: URI.revive(args[1].uri) }); + + case 'createManualSyncTask': return this.createManualSyncTask(); + } + + // manual sync + if (command.startsWith('manualSync/')) { + const manualSyncTaskCommand = command.substring('manualSync/'.length); + const manualSyncTaskId = args[0]; + const manualSyncTask = this.getManualSyncTask(manualSyncTaskId); + args = (>args).slice(1); + + switch (manualSyncTaskCommand) { + case 'preview': return manualSyncTask.preview(); + case 'accept': return manualSyncTask.accept(URI.revive(args[0]), args[1]); + case 'merge': return manualSyncTask.merge(URI.revive(args[0])); + case 'discard': return manualSyncTask.discard(URI.revive(args[0])); + case 'discardConflicts': return manualSyncTask.discardConflicts(); + case 'apply': return manualSyncTask.apply(); + case 'pull': return manualSyncTask.pull(); + case 'push': return manualSyncTask.push(); + case 'stop': return manualSyncTask.stop(); + case '_getStatus': return manualSyncTask.status; + case 'dispose': return this.disposeManualSyncTask(manualSyncTask); + } + } + + throw new Error('Invalid call'); + } + + private getManualSyncTask(manualSyncTaskId: string): IManualSyncTask { + const value = this.manualSyncTasks.get(this.createKey(manualSyncTaskId)); + if (!value) { + throw new Error(`Manual sync taks not found: ${manualSyncTaskId}`); + } + return value.manualSyncTask; + } + + private async createManualSyncTask(): Promise<{ id: string, manifest: IUserDataManifest | null, status: SyncStatus }> { + const disposables = new DisposableStore(); + const manualSyncTask = disposables.add(await this.service.createManualSyncTask()); + disposables.add(manualSyncTask.onSynchronizeResources(synchronizeResources => this.onManualSynchronizeResources.fire({ manualSyncTaskId: manualSyncTask.id, data: synchronizeResources }))); + this.manualSyncTasks.set(this.createKey(manualSyncTask.id), { manualSyncTask, disposables }); + return { id: manualSyncTask.id, manifest: manualSyncTask.manifest, status: manualSyncTask.status }; + } + + private disposeManualSyncTask(manualSyncTask: IManualSyncTask): void { + manualSyncTask.dispose(); + const key = this.createKey(manualSyncTask.id); + this.manualSyncTasks.get(key)?.disposables.dispose(); + this.manualSyncTasks.delete(key); + } + + private createKey(manualSyncTaskId: string): string { return `manualSyncTask-${manualSyncTaskId}`; } + +} + +export class UserDataSyncChannelClient extends Disposable implements IUserDataSyncService { + + declare readonly _serviceBrand: undefined; + + private readonly channel: IChannel; + + private _status: SyncStatus = SyncStatus.Uninitialized; + get status(): SyncStatus { return this._status; } + private _onDidChangeStatus: Emitter = this._register(new Emitter()); + readonly onDidChangeStatus: Event = this._onDidChangeStatus.event; + + get onDidChangeLocal(): Event { return this.channel.listen('onDidChangeLocal'); } + + private _conflicts: [SyncResource, IResourcePreview[]][] = []; + get conflicts(): [SyncResource, IResourcePreview[]][] { return this._conflicts; } + private _onDidChangeConflicts: Emitter<[SyncResource, IResourcePreview[]][]> = this._register(new Emitter<[SyncResource, IResourcePreview[]][]>()); + readonly onDidChangeConflicts: Event<[SyncResource, IResourcePreview[]][]> = this._onDidChangeConflicts.event; + + private _lastSyncTime: number | undefined = undefined; + get lastSyncTime(): number | undefined { return this._lastSyncTime; } + private _onDidChangeLastSyncTime: Emitter = this._register(new Emitter()); + readonly onDidChangeLastSyncTime: Event = this._onDidChangeLastSyncTime.event; + + private _onSyncErrors: Emitter<[SyncResource, UserDataSyncError][]> = this._register(new Emitter<[SyncResource, UserDataSyncError][]>()); + readonly onSyncErrors: Event<[SyncResource, UserDataSyncError][]> = this._onSyncErrors.event; + + get onDidResetLocal(): Event { return this.channel.listen('onDidResetLocal'); } + get onDidResetRemote(): Event { return this.channel.listen('onDidResetRemote'); } + + constructor(userDataSyncChannel: IChannel) { + super(); + this.channel = { + call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise { + return userDataSyncChannel.call(command, arg, cancellationToken) + .then(null, error => { throw UserDataSyncError.toUserDataSyncError(error); }); + }, + listen(event: string, arg?: any): Event { + return userDataSyncChannel.listen(event, arg); + } + }; + this.channel.call<[SyncStatus, [SyncResource, IResourcePreview[]][], number | undefined]>('_getInitialData').then(([status, conflicts, lastSyncTime]) => { + this.updateStatus(status); + this.updateConflicts(conflicts); + if (lastSyncTime) { + this.updateLastSyncTime(lastSyncTime); + } + this._register(this.channel.listen('onDidChangeStatus')(status => this.updateStatus(status))); + this._register(this.channel.listen('onDidChangeLastSyncTime')(lastSyncTime => this.updateLastSyncTime(lastSyncTime))); + }); + this._register(this.channel.listen<[SyncResource, IResourcePreview[]][]>('onDidChangeConflicts')(conflicts => this.updateConflicts(conflicts))); + this._register(this.channel.listen<[SyncResource, Error][]>('onSyncErrors')(errors => this._onSyncErrors.fire(errors.map(([source, error]) => ([source, UserDataSyncError.toUserDataSyncError(error)]))))); + } + + createSyncTask(): Promise { + throw new Error('not supported'); + } + + async createManualSyncTask(): Promise { + const { id, manifest, status } = await this.channel.call<{ id: string, manifest: IUserDataManifest | null, status: SyncStatus }>('createManualSyncTask'); + const that = this; + const manualSyncTaskChannelClient = new ManualSyncTaskChannelClient(id, manifest, status, { + async call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise { + return that.channel.call(`manualSync/${command}`, [id, ...(isArray(arg) ? arg : [arg])], cancellationToken); + }, + listen(event: string, arg?: any): Event { + return Event.map( + Event.filter(that.channel.listen<{ manualSyncTaskId: string, data: T }>(`manualSync/${event}`, arg), e => !manualSyncTaskChannelClient.isDiposed() && e.manualSyncTaskId === id), + e => e.data); + } + }); + return manualSyncTaskChannelClient; + } + + replace(uri: URI): Promise { + return this.channel.call('replace', [uri]); + } + + reset(): Promise { + return this.channel.call('reset'); + } + + resetRemote(): Promise { + return this.channel.call('resetRemote'); + } + + resetLocal(): Promise { + return this.channel.call('resetLocal'); + } + + hasPreviouslySynced(): Promise { + return this.channel.call('hasPreviouslySynced'); + } + + hasLocalData(): Promise { + return this.channel.call('hasLocalData'); + } + + accept(syncResource: SyncResource, resource: URI, content: string | null, apply: boolean): Promise { + return this.channel.call('accept', [syncResource, resource, content, apply]); + } + + resolveContent(resource: URI): Promise { + return this.channel.call('resolveContent', [resource]); + } + + async getLocalSyncResourceHandles(resource: SyncResource): Promise { + const handles = await this.channel.call('getLocalSyncResourceHandles', [resource]); + return handles.map(({ created, uri }) => ({ created, uri: URI.revive(uri) })); + } + + async getRemoteSyncResourceHandles(resource: SyncResource): Promise { + const handles = await this.channel.call('getRemoteSyncResourceHandles', [resource]); + return handles.map(({ created, uri }) => ({ created, uri: URI.revive(uri) })); + } + + async getAssociatedResources(resource: SyncResource, syncResourceHandle: ISyncResourceHandle): Promise<{ resource: URI, comparableResource: URI }[]> { + const result = await this.channel.call<{ resource: URI, comparableResource: URI }[]>('getAssociatedResources', [resource, syncResourceHandle]); + return result.map(({ resource, comparableResource }) => ({ resource: URI.revive(resource), comparableResource: URI.revive(comparableResource) })); + } + + async getMachineId(resource: SyncResource, syncResourceHandle: ISyncResourceHandle): Promise { + return this.channel.call('getMachineId', [resource, syncResourceHandle]); + } + + private async updateStatus(status: SyncStatus): Promise { + this._status = status; + this._onDidChangeStatus.fire(status); + } + + private async updateConflicts(conflicts: [SyncResource, IResourcePreview[]][]): Promise { + // Revive URIs + this._conflicts = conflicts.map(([syncResource, conflicts]) => + ([ + syncResource, + conflicts.map(r => + ({ + ...r, + localResource: URI.revive(r.localResource), + remoteResource: URI.revive(r.remoteResource), + previewResource: URI.revive(r.previewResource), + })) + ])); + this._onDidChangeConflicts.fire(this._conflicts); + } + + private updateLastSyncTime(lastSyncTime: number): void { + if (this._lastSyncTime !== lastSyncTime) { + this._lastSyncTime = lastSyncTime; + this._onDidChangeLastSyncTime.fire(lastSyncTime); + } + } +} + +class ManualSyncTaskChannelClient extends Disposable implements IManualSyncTask { + + private readonly channel: IChannel; + + get onSynchronizeResources(): Event<[SyncResource, URI[]][]> { return this.channel.listen<[SyncResource, URI[]][]>('onSynchronizeResources'); } + + private _status: SyncStatus; + get status(): SyncStatus { return this._status; } + + constructor( + readonly id: string, + readonly manifest: IUserDataManifest | null, + status: SyncStatus, + manualSyncTaskChannel: IChannel + ) { + super(); + this._status = status; + const that = this; + this.channel = { + async call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise { + try { + const result = await manualSyncTaskChannel.call(command, arg, cancellationToken); + if (!that.isDiposed()) { + that._status = await manualSyncTaskChannel.call('_getStatus'); + } + return result; + } catch (error) { + throw UserDataSyncError.toUserDataSyncError(error); + } + }, + listen(event: string, arg?: any): Event { + return manualSyncTaskChannel.listen(event, arg); + } + }; + } + + async preview(): Promise<[SyncResource, ISyncResourcePreview][]> { + const previews = await this.channel.call<[SyncResource, ISyncResourcePreview][]>('preview'); + return this.deserializePreviews(previews); + } + + async accept(resource: URI, content?: string | null): Promise<[SyncResource, ISyncResourcePreview][]> { + const previews = await this.channel.call<[SyncResource, ISyncResourcePreview][]>('accept', [resource, content]); + return this.deserializePreviews(previews); + } + + async merge(resource?: URI): Promise<[SyncResource, ISyncResourcePreview][]> { + const previews = await this.channel.call<[SyncResource, ISyncResourcePreview][]>('merge', [resource]); + return this.deserializePreviews(previews); + } + + async discard(resource: URI): Promise<[SyncResource, ISyncResourcePreview][]> { + const previews = await this.channel.call<[SyncResource, ISyncResourcePreview][]>('discard', [resource]); + return this.deserializePreviews(previews); + } + + async discardConflicts(): Promise<[SyncResource, ISyncResourcePreview][]> { + const previews = await this.channel.call<[SyncResource, ISyncResourcePreview][]>('discardConflicts'); + return this.deserializePreviews(previews); + } + + async apply(): Promise<[SyncResource, ISyncResourcePreview][]> { + const previews = await this.channel.call<[SyncResource, ISyncResourcePreview][]>('apply'); + return this.deserializePreviews(previews); + } + + pull(): Promise { + return this.channel.call('pull'); + } + + push(): Promise { + return this.channel.call('push'); + } + + stop(): Promise { + return this.channel.call('stop'); + } + + private _disposed = false; + isDiposed() { return this._disposed; } + + dispose(): void { + this._disposed = true; + this.channel.call('dispose'); + } + + private deserializePreviews(previews: [SyncResource, ISyncResourcePreview][]): [SyncResource, ISyncResourcePreview][] { + return previews.map(([syncResource, preview]) => + ([ + syncResource, + { + isLastSyncFromCurrentMachine: preview.isLastSyncFromCurrentMachine, + resourcePreviews: preview.resourcePreviews.map(r => ({ + ...r, + localResource: URI.revive(r.localResource), + remoteResource: URI.revive(r.remoteResource), + previewResource: URI.revive(r.previewResource), + acceptedResource: URI.revive(r.acceptedResource), + })) + } + ])); + } +} + diff --git a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts index a4bde40f314..c12dedbad75 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable, toDisposable, } from 'vs/base/common/lifecycle'; -import { IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, IUserDataSyncStore, ServerResource, UserDataSyncStoreError, IUserDataSyncLogService, IUserDataManifest, IResourceRefHandle, HEADER_OPERATION_ID, HEADER_EXECUTION_ID, CONFIGURATION_SYNC_STORE_KEY, IAuthenticationProvider, IUserDataSyncStoreManagementService, UserDataSyncStoreType, IUserDataSyncStoreClient } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, IUserDataSyncStore, ServerResource, UserDataSyncStoreError, IUserDataSyncLogService, IUserDataManifest, IResourceRefHandle, HEADER_OPERATION_ID, HEADER_EXECUTION_ID, CONFIGURATION_SYNC_STORE_KEY, IAuthenticationProvider, IUserDataSyncStoreManagementService, UserDataSyncStoreType, IUserDataSyncStoreClient, SYNC_SERVICE_URL_TYPE } from 'vs/platform/userDataSync/common/userDataSync'; import { IRequestService, asText, isSuccess as isSuccessContext, asJson } from 'vs/platform/request/common/request'; import { joinPath, relativePath } from 'vs/base/common/resources'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -23,7 +23,6 @@ import { isString, isObject, isArray } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { getErrorMessage, isPromiseCanceledError } from 'vs/base/common/errors'; -const SYNC_SERVICE_URL_TYPE = 'sync.store.url.type'; const SYNC_PREVIOUS_STORE = 'sync.previous.store'; const DONOT_MAKE_REQUESTS_UNTIL_KEY = 'sync.donot-make-requests-until'; const USER_SESSION_ID_KEY = 'sync.user-session-id'; @@ -31,7 +30,7 @@ const MACHINE_SESSION_ID_KEY = 'sync.machine-session-id'; const REQUEST_SESSION_LIMIT = 100; const REQUEST_SESSION_INTERVAL = 1000 * 60 * 5; /* 5 minutes */ -type UserDataSyncStore = IUserDataSyncStore & { defaultType?: UserDataSyncStoreType; type?: UserDataSyncStoreType }; +type UserDataSyncStore = IUserDataSyncStore & { defaultType: UserDataSyncStoreType; }; export abstract class AbstractUserDataSyncStoreManagementService extends Disposable implements IUserDataSyncStoreManagementService { @@ -42,6 +41,13 @@ export abstract class AbstractUserDataSyncStoreManagementService extends Disposa private _userDataSyncStore: UserDataSyncStore | undefined; get userDataSyncStore(): UserDataSyncStore | undefined { return this._userDataSyncStore; } + protected get userDataSyncStoreType(): UserDataSyncStoreType | undefined { + return this.storageService.get(SYNC_SERVICE_URL_TYPE, StorageScope.GLOBAL) as UserDataSyncStoreType; + } + protected set userDataSyncStoreType(type: UserDataSyncStoreType | undefined) { + this.storageService.store(SYNC_SERVICE_URL_TYPE, type, StorageScope.GLOBAL, isWeb ? StorageTarget.USER /* sync in web */ : StorageTarget.MACHINE); + } + constructor( @IProductService protected readonly productService: IProductService, @IConfigurationService protected readonly configurationService: IConfigurationService, @@ -49,6 +55,7 @@ export abstract class AbstractUserDataSyncStoreManagementService extends Disposa ) { super(); this.updateUserDataSyncStore(); + this._register(Event.filter(storageService.onDidChangeValue, e => e.key === SYNC_SERVICE_URL_TYPE && e.scope === StorageScope.GLOBAL && this.userDataSyncStoreType !== this.userDataSyncStore?.type)(() => this.updateUserDataSyncStore())); } protected updateUserDataSyncStore(): void { @@ -56,8 +63,8 @@ export abstract class AbstractUserDataSyncStoreManagementService extends Disposa this._onDidChangeUserDataSyncStore.fire(); } - protected toUserDataSyncStore(productStore: ConfigurationSyncStore | undefined, configuredStore?: ConfigurationSyncStore): UserDataSyncStore | undefined { - // Web overrides + protected toUserDataSyncStore(productStore: ConfigurationSyncStore & { web?: ConfigurationSyncStore } | undefined, configuredStore?: ConfigurationSyncStore): UserDataSyncStore | undefined { + // Check for web overrides for backward compatibility while reading previous store productStore = isWeb && productStore?.web ? { ...productStore, ...productStore.web } : productStore; const value: Partial = { ...(productStore || {}), ...(configuredStore || {}) }; if (value @@ -67,7 +74,8 @@ export abstract class AbstractUserDataSyncStoreManagementService extends Disposa ) { const syncStore = value as ConfigurationSyncStore; const canSwitch = !!syncStore.canSwitch && !configuredStore?.url; - const type: UserDataSyncStoreType | undefined = canSwitch ? this.storageService.get(SYNC_SERVICE_URL_TYPE, StorageScope.GLOBAL) as UserDataSyncStoreType : undefined; + const defaultType: UserDataSyncStoreType = syncStore.url === syncStore.insidersUrl ? 'insiders' : 'stable'; + const type: UserDataSyncStoreType = (canSwitch ? this.userDataSyncStoreType : undefined) || defaultType; const url = configuredStore?.url || (type === 'insiders' ? syncStore.insidersUrl : type === 'stable' ? syncStore.stableUrl @@ -75,11 +83,11 @@ export abstract class AbstractUserDataSyncStoreManagementService extends Disposa return { url: URI.parse(url), type, - defaultType: syncStore.url === syncStore.insidersUrl ? 'insiders' : syncStore.url === syncStore.stableUrl ? 'stable' : undefined, + defaultType, defaultUrl: URI.parse(syncStore.url), stableUrl: URI.parse(syncStore.stableUrl), insidersUrl: URI.parse(syncStore.insidersUrl), - canSwitch: !!syncStore.canSwitch && !configuredStore?.url, + canSwitch, authenticationProviders: Object.keys(syncStore.authenticationProviders).reduce((result, id) => { result.push({ id, scopes: syncStore!.authenticationProviders[id].scopes }); return result; @@ -119,12 +127,8 @@ export class UserDataSyncStoreManagementService extends AbstractUserDataSyncStor } async switch(type: UserDataSyncStoreType): Promise { - if (this.userDataSyncStore?.canSwitch && type !== this.userDataSyncStore.type) { - if (type === this.userDataSyncStore.defaultType) { - this.storageService.remove(SYNC_SERVICE_URL_TYPE, StorageScope.GLOBAL); - } else { - this.storageService.store(SYNC_SERVICE_URL_TYPE, type, StorageScope.GLOBAL, StorageTarget.MACHINE); - } + if (type !== this.userDataSyncStoreType) { + this.userDataSyncStoreType = type; this.updateUserDataSyncStore(); } } diff --git a/src/vs/platform/userDataSync/electron-sandbox/userDataAutoSyncService.ts b/src/vs/platform/userDataSync/electron-sandbox/userDataAutoSyncService.ts index 115f5ff011f..b2757dcd7b7 100644 --- a/src/vs/platform/userDataSync/electron-sandbox/userDataAutoSyncService.ts +++ b/src/vs/platform/userDataSync/electron-sandbox/userDataAutoSyncService.ts @@ -11,10 +11,12 @@ import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/use import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; +import { IProductService } from 'vs/platform/product/common/productService'; export class UserDataAutoSyncService extends BaseUserDataAutoSyncService { constructor( + @IProductService productService: IProductService, @IUserDataSyncStoreManagementService userDataSyncStoreManagementService: IUserDataSyncStoreManagementService, @IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService, @IUserDataSyncResourceEnablementService userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, @@ -27,7 +29,7 @@ export class UserDataAutoSyncService extends BaseUserDataAutoSyncService { @IStorageService storageService: IStorageService, @IUserDataAutoSyncEnablementService userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService, ) { - super(userDataSyncStoreManagementService, userDataSyncStoreService, userDataSyncResourceEnablementService, userDataSyncService, logService, authTokenService, telemetryService, userDataSyncMachinesService, storageService, userDataAutoSyncEnablementService); + super(productService, userDataSyncStoreManagementService, userDataSyncStoreService, userDataSyncResourceEnablementService, userDataSyncService, logService, authTokenService, telemetryService, userDataSyncMachinesService, storageService, userDataAutoSyncEnablementService); this._register(Event.debounce(Event.any( Event.map(nativeHostService.onDidFocusWindow, () => 'windowFocus'), diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts b/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts index 4550c939bb5..345fe859c44 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts @@ -51,6 +51,7 @@ suite('UserDataSyncStoreManagementService', () => { const expected: IUserDataSyncStore = { url: URI.parse('http://configureHost:3000'), + type: 'stable', defaultUrl: URI.parse('http://configureHost:3000'), stableUrl: URI.parse('http://configureHost:3000'), insidersUrl: URI.parse('http://configureHost:3000'), diff --git a/src/vs/platform/webview/electron-main/webviewMainService.ts b/src/vs/platform/webview/electron-main/webviewMainService.ts index f569607a424..ccce8561a33 100644 --- a/src/vs/platform/webview/electron-main/webviewMainService.ts +++ b/src/vs/platform/webview/electron-main/webviewMainService.ts @@ -35,12 +35,16 @@ export class WebviewMainService extends Disposable implements IWebviewManagerSer this.portMappingProvider = this._register(new WebviewPortMappingProvider(tunnelService)); const sess = session.fromPartition(webviewPartitionId); - sess.setPermissionRequestHandler((webContents, permission /* 'media' | 'geolocation' | 'notifications' | 'midiSysex' | 'pointerLock' | 'fullscreen' | 'openExternal' */, callback) => { + sess.setPermissionRequestHandler((_webContents, permission, callback) => { + if (permission === 'clipboard-read') { + return callback(true); + } + return callback(false); }); - sess.setPermissionCheckHandler((webContents, permission /* 'media' */) => { - return false; + sess.setPermissionCheckHandler((_webContents, permission /* 'media' */) => { + return permission === 'clipboard-read'; }); } @@ -87,7 +91,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/webview/electron-main/webviewPortMappingProvider.ts b/src/vs/platform/webview/electron-main/webviewPortMappingProvider.ts index da60a2bc507..f97a8bc0aae 100644 --- a/src/vs/platform/webview/electron-main/webviewPortMappingProvider.ts +++ b/src/vs/platform/webview/electron-main/webviewPortMappingProvider.ts @@ -3,8 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { OnBeforeRequestListenerDetails, session } from 'electron'; +import { OnBeforeRequestListenerDetails, session, webContents } from 'electron'; import { Disposable } from 'vs/base/common/lifecycle'; +import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { IAddress } from 'vs/platform/remote/common/remoteAgentConnection'; import { ITunnelService } from 'vs/platform/remote/common/tunnel'; @@ -21,12 +22,14 @@ interface PortMappingData { readonly resolvedAuthority: IAddress | null | undefined; } +interface WebviewData { + readonly manager: WebviewPortMappingManager; + readonly metadata: PortMappingData; +} + export class WebviewPortMappingProvider extends Disposable { - private readonly _webviewData = new Map(); + private readonly _webviewData = new Map(); constructor( @ITunnelService private readonly _tunnelService: ITunnelService, @@ -34,7 +37,6 @@ export class WebviewPortMappingProvider extends Disposable { super(); const sess = session.fromPartition(webviewPartitionId); - sess.webRequest.onBeforeRequest({ urls: [ '*://localhost:*/*', @@ -42,14 +44,26 @@ export class WebviewPortMappingProvider extends Disposable { '*://0.0.0.0:*/*', ] }, async (details: OnBeforeRequestListenerDetails_Extended, callback) => { - let origin: URI; + let webviewId: string | undefined; try { - origin = URI.parse(details.lastCommittedOrigin!); + if (details.lastCommittedOrigin) { + const origin = URI.parse(details.lastCommittedOrigin); + webviewId = origin.authority; + } else if (typeof details.webContentsId === 'number') { + const contents = webContents.fromId(details.webContentsId); + const url = URI.parse(contents.getURL()); + if (url.scheme === Schemas.vscodeWebview) { + webviewId = url.authority; + } + } } catch { return callback({}); } - const webviewId = origin.authority; + if (!webviewId) { + return callback({}); + } + const entry = this._webviewData.get(webviewId); if (!entry) { return callback({}); 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 f4e93782bff..a0301e5b0fa 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -64,23 +64,28 @@ declare module 'vscode' { readonly onDidChangeSessions: Event; /** - * Returns an array of current sessions. + * 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 - getSessions(): 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 - login(scopes: string[]): Thenable; + 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 - logout(sessionId: string): Thenable; + removeSession(sessionId: string): Thenable; } /** @@ -199,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 { @@ -233,6 +244,8 @@ declare module 'vscode' { elevation: boolean; public: boolean; }; + + candidatePortSource?: CandidatePortSource; } export namespace workspace { @@ -729,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. @@ -991,7 +1145,7 @@ declare module 'vscode' { //#region https://github.com/microsoft/vscode/issues/106744, Notebooks (misc) - export enum CellKind { + export enum NotebookCellKind { Markdown = 1, Code = 2 } @@ -1008,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 @@ -1043,48 +1207,52 @@ declare module 'vscode' { readonly index: number; readonly notebook: NotebookDocument; readonly uri: Uri; - readonly cellKind: CellKind; + readonly cellKind: NotebookCellKind; 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; - /** - * Languages the document supports - */ - languages?: string[]; + // 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 { @@ -1104,27 +1272,26 @@ 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; readonly cells: ReadonlyArray; readonly contentOptions: NotebookDocumentContentOptions; - // todo@API - // - move to kernel -> control runnable state of a cell - // - remove from this type - languages: string[]; readonly metadata: NotebookDocumentMetadata; } // 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 { @@ -1248,7 +1415,7 @@ declare module 'vscode' { // todo@API support ids https://github.com/jupyter/enhancement-proposals/blob/master/62-cell-id/cell-id.md export interface NotebookCellData { - readonly cellKind: CellKind; + readonly cellKind: NotebookCellKind; readonly source: string; readonly language: string; // todo@API maybe use a separate data type? @@ -1258,7 +1425,6 @@ declare module 'vscode' { export interface NotebookData { readonly cells: NotebookCellData[]; - readonly languages: string[]; readonly metadata: NotebookDocumentMetadata; } @@ -1310,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; @@ -1324,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; } @@ -1335,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; } @@ -1354,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 @@ -1364,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 @@ -1483,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 @@ -1532,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; @@ -1583,10 +1688,13 @@ declare module 'vscode' { isPreferred?: boolean; preloads?: Uri[]; - // todo@API - // languages supported by kernel - // first is preferred - // languages: string[]; + // TODO@API control runnable state of cell + /** + * languages supported by kernel + * - first is preferred + * - `undefined` means all languages available in the editor + */ + supportedLanguages?: string[]; // @roblourens // todo@API change to `executeCells(document: NotebookDocument, cells: NotebookCellRange[], context:{isWholeNotebooke: boolean}, token: CancelationToken): void;` @@ -2048,6 +2156,15 @@ declare module 'vscode' { //#endregion + //#region https://github.com/microsoft/vscode/issues/116906 + + export interface ExtensionContext { + readonly extensionId: string; + readonly extensionVersion: string; + } + + //#endregion + //#region https://github.com/microsoft/vscode/issues/102091 export interface TextDocument { @@ -2095,24 +2212,17 @@ declare module 'vscode' { export function createDocumentTestObserver(document: TextDocument): TestObserver; /** - * The last or selected test run. Cleared when a new test run starts. - */ - export const testResults: TestResults | undefined; + * List of test results stored by VS Code, sorted in descnding + * order by their `completedAt` time. + */ + export const testResults: ReadonlyArray; /** - * Event that fires when the testResults are updated. - */ + * Event that fires when the {@link testResults} array is updated. + */ export const onDidChangeTestResults: Event; } - export interface TestResults { - /** - * The results from the latest test run. The array contains a snapshot of - * all tests involved in the run at the moment when it completed. - */ - readonly tests: ReadonlyArray | undefined; - } - export interface TestObserver { /** * List of tests returned by test provider for files in the workspace. @@ -2419,6 +2529,45 @@ declare module 'vscode' { location?: Location; } + /** + * TestResults can be provided to VS Code, or read from it. + * + * The results contain a 'snapshot' of the tests at the point when the test + * run is complete. Therefore, information such as {@link Location} instances + * may be out of date. If the test still exists in the workspace, consumers + * can use its `id` to correlate the result instance with the living test. + * + * @todo coverage and other info may eventually live here + */ + export interface TestResults { + /** + * Unix milliseconds timestamp at which the tests were completed. + */ + completedAt: number; + + /** + * List of test results. The items in this array are the items that + * were passed in the {@link test.runTests} method. + */ + results: ReadonlyArray>; + } + + /** + * A {@link TestItem} with an associated result, which appear or can be + * provided in {@link TestResult} interfaces. + */ + export interface TestItemWithResults extends TestItem { + /** + * Current result of the test. + */ + result: TestState; + + /** + * Optional list of nested tests for this item. + */ + children?: Readonly[]; + } + //#endregion //#region Opener service (https://github.com/microsoft/vscode/issues/109277) diff --git a/src/vs/workbench/api/browser/mainThreadAuthentication.ts b/src/vs/workbench/api/browser/mainThreadAuthentication.ts index fe87eb11f64..114ac174f64 100644 --- a/src/vs/workbench/api/browser/mainThreadAuthentication.ts +++ b/src/vs/workbench/api/browser/mainThreadAuthentication.ts @@ -18,11 +18,6 @@ import { fromNow } from 'vs/base/common/date'; import { ActivationKind, IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; export class MainThreadAuthenticationProvider extends Disposable { - private _accounts = new Map(); // Map account name to session ids - private _sessions = new Map(); // Map account id to name - - private _hasInitializedSessions = false; - constructor( private readonly _proxy: ExtHostAuthenticationShape, public readonly id: string, @@ -35,10 +30,6 @@ export class MainThreadAuthenticationProvider extends Disposable { ) { super(); } - public hasSessions(): boolean { - return !!this._sessions.size; - } - public manageTrustedExtensions(accountName: string) { const allowedExtensions = readAllowedExtensions(this.storageService, this.id, accountName); @@ -80,21 +71,8 @@ export class MainThreadAuthenticationProvider extends Disposable { quickPick.show(); } - private registerSession(session: modes.AuthenticationSession) { - this._sessions.set(session.id, session.account.label); - - const existingSessionsForAccount = this._accounts.get(session.account.label); - if (existingSessionsForAccount) { - this._accounts.set(session.account.label, existingSessionsForAccount.concat(session.id)); - return; - } else { - this._accounts.set(session.account.label, [session.id]); - } - } - - async signOut(accountName: string): Promise { + async removeAccountSessions(accountName: string, sessions: modes.AuthenticationSession[]): Promise { const accountUsages = readAccountUsages(this.storageService, this.id, accountName); - const sessionsForAccount = this._accounts.get(accountName); const result = await this.dialogService.confirm({ title: nls.localize('signOutConfirm', "Sign out of {0}", accountName), @@ -104,49 +82,23 @@ export class MainThreadAuthenticationProvider extends Disposable { }); if (result.confirmed) { - sessionsForAccount?.forEach(sessionId => this.logout(sessionId)); + const removeSessionPromises = sessions.map(session => this.removeSession(session.id)); + await Promise.all(removeSessionPromises); removeAccountUsage(this.storageService, this.id, accountName); this.storageService.remove(`${this.id}-${accountName}`, StorageScope.GLOBAL); } } - async getSessions(): Promise> { - const sessions = await this._proxy.$getSessions(this.id); - if (!this._hasInitializedSessions) { - sessions.forEach(session => this.registerSession(session)); - this._hasInitializedSessions = true; - } - - return sessions; + async getSessions(scopes?: string[]) { + return this._proxy.$getSessions(this.id, scopes); } - async updateSessionItems(event: modes.AuthenticationSessionsChangeEvent): Promise { - const { added, removed } = event; - - removed.forEach(session => { - const sessionId = session.id; - const accountName = this._sessions.get(sessionId); - if (accountName) { - this._sessions.delete(sessionId); - let sessionsForAccount = this._accounts.get(accountName) || []; - const sessionIndex = sessionsForAccount.indexOf(sessionId); - sessionsForAccount.splice(sessionIndex); - - if (!sessionsForAccount.length) { - this._accounts.delete(accountName); - } - } - }); - - added.forEach(session => this.registerSession(session)); + createSession(scopes: string[]): Promise { + return this._proxy.$createSession(this.id, scopes); } - login(scopes: string[]): Promise { - return this._proxy.$login(this.id, scopes); - } - - async logout(sessionId: string): Promise { - await this._proxy.$logout(this.id, sessionId); + async removeSession(sessionId: string): Promise { + await this._proxy.$removeSession(this.id, sessionId); this.notificationService.info(nls.localize('signedOut', "Successfully signed out.")); } } @@ -203,8 +155,8 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu this.authenticationService.sessionsUpdate(id, event); } - $logout(providerId: string, sessionId: string): Promise { - return this.authenticationService.logout(providerId, sessionId); + $removeSession(providerId: string, sessionId: string): Promise { + return this.authenticationService.removeSession(providerId, sessionId); } private async loginPrompt(providerName: string, extensionName: string): Promise { const { choice } = await this.dialogService.show( @@ -230,7 +182,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu } - private async selectSession(providerId: string, extensionId: string, extensionName: string, potentialSessions: modes.AuthenticationSession[], clearSessionPreference: boolean): Promise { + private async selectSession(providerId: string, extensionId: string, extensionName: string, potentialSessions: readonly modes.AuthenticationSession[], clearSessionPreference: boolean): Promise { if (!potentialSessions.length) { throw new Error('No potential sessions found'); } @@ -259,8 +211,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu } async $getSession(providerId: string, scopes: string[], extensionId: string, extensionName: string, options: { createIfNone: boolean, clearSessionPreference: boolean }): Promise { - const orderedScopes = scopes.sort().join(' '); - const sessions = (await this.authenticationService.getSessions(providerId, true)).filter(session => session.scopes.slice().sort().join(' ') === orderedScopes); + const sessions = await this.authenticationService.getSessions(providerId, scopes, true); const silent = !options.createIfNone; let session: modes.AuthenticationSession | undefined; @@ -276,6 +227,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu } } else { this.authenticationService.requestSessionAccess(providerId, extensionId, extensionName, [session]); + return undefined; } } } else { @@ -283,6 +235,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu session = await this.selectSession(providerId, extensionId, extensionName, sessions, !!options.clearSessionPreference); } else { this.authenticationService.requestSessionAccess(providerId, extensionId, extensionName, sessions); + return undefined; } } } else { @@ -292,7 +245,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu throw new Error('User did not consent to login.'); } - session = await this.authenticationService.login(providerId, scopes, true); + session = await this.authenticationService.createSession(providerId, scopes, true); await this.setTrustedExtensionAndAccountPreference(providerId, session.account.label, extensionId, extensionName, session.id); } else { await this.authenticationService.requestNewSession(providerId, scopes, extensionId, extensionName); diff --git a/src/vs/workbench/api/browser/mainThreadDocuments.ts b/src/vs/workbench/api/browser/mainThreadDocuments.ts index abdd99cd816..ca3afccd5e4 100644 --- a/src/vs/workbench/api/browser/mainThreadDocuments.ts +++ b/src/vs/workbench/api/browser/mainThreadDocuments.ts @@ -13,7 +13,6 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { IFileService, FileOperation } from 'vs/platform/files/common/files'; import { MainThreadDocumentsAndEditors } from 'vs/workbench/api/browser/mainThreadDocumentsAndEditors'; import { ExtHostContext, ExtHostDocumentsShape, IExtHostContext, MainThreadDocumentsShape } from 'vs/workbench/api/common/extHost.protocol'; -import { ITextEditorModel } from 'vs/workbench/common/editor'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { toLocalResource, extUri, IExtUri } from 'vs/base/common/resources'; @@ -47,8 +46,8 @@ export class BoundModelReferenceCollection { } } - add(uri: URI, ref: IReference): void { - const length = ref.object.textEditorModel.getValueLength(); + add(uri: URI, ref: IReference, length: number = 0): void { + // const length = ref.object.textEditorModel.getValueLength(); let handle: any; let entry: { uri: URI, length: number, dispose(): void }; const dispose = () => { @@ -267,7 +266,7 @@ export class MainThreadDocuments extends Disposable implements MainThreadDocumen private _handleAsResourceInput(uri: URI): Promise { return this._textModelResolverService.createModelReference(uri).then(ref => { - this._modelReferenceCollection.add(uri, ref); + this._modelReferenceCollection.add(uri, ref, ref.object.textEditorModel.getValueLength()); return ref.object.textEditorModel.uri; }); } 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 7e09d9d1bbf..566299adf3b 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -3,58 +3,49 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as DOM from 'vs/base/browser/dom'; import { CancellationToken } from 'vs/base/common/cancellation'; import { diffMaps, diffSets } from 'vs/base/common/collections'; 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 { combinedDisposable, Disposable, DisposableStore, dispose, IDisposable } 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 { 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'; import { EditorActivation, ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; +import { BoundModelReferenceCollection } from 'vs/workbench/api/browser/mainThreadDocuments'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { viewColumnToEditorGroup } from 'vs/workbench/common/editor'; import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; -import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, DisplayOrderKey, ICellEditOperation, ICellRange, IEditor, IMainCellDto, INotebookDecorationRenderOptions, INotebookDocumentFilter, INotebookEditorModel, INotebookExclusiveDocumentFilter, NotebookCellsChangeType, NOTEBOOK_DISPLAY_ORDER, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, DisplayOrderKey, ICellEditOperation, ICellRange, IEditor, IMainCellDto, INotebookDecorationRenderOptions, INotebookDocumentFilter, INotebookExclusiveDocumentFilter, INotebookKernel, NotebookCellsChangeType, NOTEBOOK_DISPLAY_ORDER, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IEditorGroup, IEditorGroupsService, preferredSideBySideGroupDirection } from 'vs/workbench/services/editor/common/editorGroupsService'; 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 { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookCellStatusBarEntryDto, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookModelAddedData, MainContext, MainThreadNotebookShape, NotebookEditorRevealType, NotebookExtensionDescription } from '../common/extHost.protocol'; +import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookCellStatusBarEntryDto, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookEditorAddData, INotebookModelAddedData, MainContext, MainThreadNotebookShape, NotebookEditorRevealType, NotebookExtensionDescription } from '../common/extHost.protocol'; class DocumentAndEditorState { static compute(before: DocumentAndEditorState | undefined, after: DocumentAndEditorState): INotebookDocumentsAndEditorsDelta { if (!before) { - const apiEditors = []; - for (let id in after.textEditors) { - const editor = after.textEditors.get(id)!; - apiEditors.push({ id, documentUri: editor.uri!, selections: editor!.getSelectionHandles(), visibleRanges: editor.visibleRanges }); - } - return { - addedDocuments: [], - addedEditors: apiEditors, + addedDocuments: [...after.documents].map(DocumentAndEditorState._asModelAddData), + addedEditors: [...after.textEditors.values()].map(DocumentAndEditorState._asEditorAddData), visibleEditors: [...after.visibleEditors].map(editor => editor[0]) }; } const documentDelta = diffSets(before.documents, after.documents); const editorDelta = diffMaps(before.textEditors, after.textEditors); - const addedAPIEditors = editorDelta.added.map(add => ({ - id: add.getId(), - documentUri: add.uri!, - selections: add.getSelectionHandles(), - visibleRanges: add.visibleRanges - })); + const addedAPIEditors = editorDelta.added.map(DocumentAndEditorState._asEditorAddData); const removedAPIEditors = editorDelta.removed.map(removed => removed.getId()); @@ -64,29 +55,7 @@ class DocumentAndEditorState { const visibleEditorDelta = diffMaps(before.visibleEditors, after.visibleEditors); return { - addedDocuments: documentDelta.added.map((e: NotebookTextModel): INotebookModelAddedData => { - return { - viewType: e.viewType, - uri: e.uri, - metadata: e.metadata, - versionId: e.versionId, - cells: e.cells.map(cell => ({ - handle: cell.handle, - uri: cell.uri, - source: cell.textBuffer.getLinesContent(), - eol: cell.textBuffer.getEOL(), - language: cell.language, - cellKind: cell.cellKind, - outputs: cell.outputs, - metadata: cell.metadata - })), - contentOptions: e.transientOptions, - // attachedEditor: editorId ? { - // id: editorId, - // selections: document.textModel.selections - // } : undefined - }; - }), + addedDocuments: documentDelta.added.map(DocumentAndEditorState._asModelAddData), removedDocuments: documentDelta.removed.map(e => e.uri), addedEditors: addedAPIEditors, removedEditors: removedAPIEditors, @@ -105,30 +74,61 @@ class DocumentAndEditorState { ) { // } + + private static _asModelAddData(e: NotebookTextModel): INotebookModelAddedData { + return { + viewType: e.viewType, + uri: e.uri, + metadata: e.metadata, + versionId: e.versionId, + cells: e.cells.map(cell => ({ + handle: cell.handle, + uri: cell.uri, + source: cell.textBuffer.getLinesContent(), + eol: cell.textBuffer.getEOL(), + language: cell.language, + cellKind: cell.cellKind, + outputs: cell.outputs, + metadata: cell.metadata + })), + contentOptions: e.transientOptions, + }; + } + + private static _asEditorAddData(add: IEditor): INotebookEditorAddData { + return { + id: add.getId(), + documentUri: add.uri!, + selections: add.getSelectionHandles(), + visibleRanges: add.visibleRanges + }; + } } @extHostNamedCustomer(MainContext.MainThreadNotebook) export class MainThreadNotebooks extends Disposable implements MainThreadNotebookShape { + + private readonly _proxy: ExtHostNotebookShape; private readonly _notebookProviders = new Map(); private readonly _notebookKernelProviders = new Map, provider: IDisposable }>(); - private readonly _proxy: ExtHostNotebookShape; - private _toDisposeOnEditorRemove = new Map(); - private _currentState?: DocumentAndEditorState; - private _editorEventListenersMapping: Map = new Map(); - private _documentEventListenersMapping: ResourceMap = new ResourceMap(); + private readonly _toDisposeOnEditorRemove = new Map(); + private readonly _editorEventListenersMapping: Map = new Map(); + private readonly _documentEventListenersMapping: ResourceMap = new ResourceMap(); private readonly _cellStatusBarEntries: Map = new Map(); private readonly _modelReferenceCollection: BoundModelReferenceCollection; + private _currentState?: DocumentAndEditorState; + constructor( extHostContext: IExtHostContext, + @IWorkingCopyService private readonly _workingCopyService: IWorkingCopyService, @INotebookService private _notebookService: INotebookService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @IEditorService private readonly editorService: IEditorService, - @IEditorGroupsService private readonly editorGroupsService: IEditorGroupsService, - @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, - @IAccessibilityService private readonly accessibilityService: IAccessibilityService, - @ILogService private readonly logService: ILogService, - @INotebookCellStatusBarService private readonly cellStatusBarService: INotebookCellStatusBarService, + @IConfigurationService private readonly _configurationService: IConfigurationService, + @IEditorService private readonly _editorService: IEditorService, + @IEditorGroupsService private readonly _editorGroupsService: IEditorGroupsService, + @IAccessibilityService private readonly _accessibilityService: IAccessibilityService, + @ILogService private readonly _logService: ILogService, + @INotebookCellStatusBarService private readonly _cellStatusBarService: INotebookCellStatusBarService, @INotebookEditorModelResolverService private readonly _notebookModelResolverService: INotebookEditorModelResolverService, @IUriIdentityService private readonly _uriIdentityService: IUriIdentityService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @@ -136,10 +136,30 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo super(); this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebook); this._modelReferenceCollection = new BoundModelReferenceCollection(this._uriIdentityService.extUri); - this._register(this._modelReferenceCollection); this.registerListeners(); } + dispose(): void { + super.dispose(); + + this._modelReferenceCollection.dispose(); + + // remove all notebook providers + for (let item of this._notebookProviders.values()) { + item.disposable.dispose(); + } + + // remove all kernel providers + for (let item of this._notebookKernelProviders.values()) { + item.emitter.dispose(); + item.provider.dispose(); + } + dispose(this._editorEventListenersMapping.values()); + dispose(this._documentEventListenersMapping.values()); + dispose(this._toDisposeOnEditorRemove.values()); + dispose(this._cellStatusBarEntries.values()); + } + async $tryApplyEdits(_viewType: string, resource: UriComponents, modelVersionId: number, cellEdits: ICellEditOperation[]): Promise { const textModel = this._notebookService.getNotebookTextModel(URI.from(resource)); if (!textModel) { @@ -185,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); }); @@ -324,26 +360,26 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo })); const updateOrder = () => { - let userOrder = this.configurationService.getValue(DisplayOrderKey); + let userOrder = this._configurationService.getValue(DisplayOrderKey); this._proxy.$acceptDisplayOrder({ - defaultOrder: this.accessibilityService.isScreenReaderOptimized() ? ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER : NOTEBOOK_DISPLAY_ORDER, + defaultOrder: this._accessibilityService.isScreenReaderOptimized() ? ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER : NOTEBOOK_DISPLAY_ORDER, userOrder: userOrder }); }; updateOrder(); - this._register(this.configurationService.onDidChangeConfiguration(e => { + this._register(this._configurationService.onDidChangeConfiguration(e => { if (e.affectedKeys.indexOf(DisplayOrderKey) >= 0) { updateOrder(); } })); - this._register(this.accessibilityService.onDidChangeScreenReaderOptimized(() => { + this._register(this._accessibilityService.onDidChangeScreenReaderOptimized(() => { updateOrder(); })); - const activeEditorPane = this.editorService.activeEditorPane as any | undefined; + const activeEditorPane = this._editorService.activeEditorPane as any | undefined; const notebookEditor = activeEditorPane?.isNotebookEditor ? activeEditorPane.getControl() : undefined; this._updateState(notebookEditor); } @@ -356,7 +392,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo }), )); - const activeEditorPane = this.editorService.activeEditorPane as any | undefined; + const activeEditorPane = this._editorService.activeEditorPane as any | undefined; const notebookEditor = activeEditorPane?.isNotebookEditor ? activeEditorPane.getControl() : undefined; this._updateState(notebookEditor); } @@ -376,7 +412,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo private async _updateState(focusedNotebookEditor?: IEditor) { let activeEditor: string | null = null; - const activeEditorPane = this.editorService.activeEditorPane as any | undefined; + const activeEditorPane = this._editorService.activeEditorPane as any | undefined; if (activeEditorPane?.isNotebookEditor) { const notebookEditor = (activeEditorPane.getControl() as INotebookEditor); activeEditor = notebookEditor && notebookEditor.hasModel() ? notebookEditor!.getId() : null; @@ -393,7 +429,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo }); const visibleEditorsMap = new Map(); - this.editorService.visibleEditorPanes.forEach(editor => { + this._editorService.visibleEditorPanes.forEach(editor => { if ((editor as any).isNotebookEditor) { const nbEditorWidget = (editor as any).getControl() as INotebookEditor; if (nbEditorWidget && editors.has(nbEditorWidget.getId())) { @@ -422,7 +458,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo // if (!isEmptyChange) { this._currentState = newState; - await this._emitDelta(delta); + this._emitDelta(delta); // } } @@ -443,24 +479,8 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo contentOptions.transientOutputs = newOptions.transientOutputs; }, viewOptions: options.viewOptions, - reloadNotebook: async (mainthreadTextModel: NotebookTextModel) => { - const data = await this._proxy.$resolveNotebookData(viewType, mainthreadTextModel.uri); - mainthreadTextModel.updateLanguages(data.languages); - mainthreadTextModel.metadata = data.metadata; - mainthreadTextModel.transientOptions = contentOptions; - - const edits: ICellEditOperation[] = [ - { editType: CellEditType.Replace, index: 0, count: mainthreadTextModel.cells.length, cells: data.cells } - ]; - await new Promise(resolve => { - DOM.scheduleAtNextAnimationFrame(() => { - const ret = mainthreadTextModel!.applyEdits(mainthreadTextModel!.versionId, edits, true, undefined, () => undefined, undefined); - resolve(ret); - }); - }); - }, - 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 @@ -485,7 +505,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 { @@ -512,38 +531,46 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo async $registerNotebookKernelProvider(extension: NotebookExtensionDescription, handle: number, documentFilter: INotebookDocumentFilter): Promise { const emitter = new Emitter(); const that = this; + const provider = this._notebookService.registerNotebookKernelProvider({ providerExtensionId: extension.id.value, providerDescription: extension.description, onDidChangeKernels: emitter.event, selector: documentFilter, - provideKernels: async (uri: URI, token: CancellationToken) => { - const kernels = await that._proxy.$provideNotebookKernels(handle, uri, token); - return kernels.map(kernel => { - return { - ...kernel, - providerHandle: handle - }; - }); - }, - resolveKernel: (editorId: string, uri: URI, kernelId: string, token: CancellationToken) => { - return that._proxy.$resolveNotebookKernel(handle, editorId, uri, kernelId, token); - }, - executeNotebook: (uri: URI, kernelId: string, cellHandle: number | undefined) => { - this.logService.debug('MainthreadNotebooks.registerNotebookKernelProvider#executeNotebook', uri.path, kernelId, cellHandle); - return that._proxy.$executeNotebookKernelFromProvider(handle, uri, kernelId, cellHandle); - }, - cancelNotebook: (uri: URI, kernelId: string, cellHandle: number | undefined) => { - this.logService.debug('MainthreadNotebooks.registerNotebookKernelProvider#cancelNotebook', uri.path, kernelId, cellHandle); - return that._proxy.$cancelNotebookKernelFromProvider(handle, uri, kernelId, cellHandle); - }, + provideKernels: async (uri: URI, token: CancellationToken): Promise => { + 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, + label: dto.label, + extension: dto.extension, + extensionLocation: URI.revive(dto.extensionLocation), + providerHandle: dto.providerHandle, + description: dto.description, + detail: dto.detail, + isPreferred: dto.isPreferred, + preloads: dto.preloads?.map(u => URI.revive(u)), + supportedLanguages: dto.supportedLanguages, + resolve: (uri: URI, editorId: string, token: CancellationToken): Promise => { + this._logService.debug('MainthreadNotebooks.resolveNotebookKernel', uri.path, dto.friendlyId); + return this._proxy.$resolveNotebookKernel(handle, editorId, uri, dto.friendlyId, token); + }, + executeNotebookCell: (uri: URI, cellHandle: number | undefined): Promise => { + this._logService.debug('MainthreadNotebooks.executeNotebookCell', uri.path, dto.friendlyId, cellHandle); + return this._proxy.$executeNotebookKernelFromProvider(handle, uri, dto.friendlyId, cellHandle); + }, + cancelNotebookCell: (uri: URI, cellHandle: number | undefined): Promise => { + this._logService.debug('MainthreadNotebooks.cancelNotebookCell', uri.path, dto.friendlyId, cellHandle); + return this._proxy.$cancelNotebookKernelFromProvider(handle, uri, dto.friendlyId, cellHandle); + } + }); + } + return result; + } }); - this._notebookKernelProviders.set(handle, { - extension, - emitter, - provider - }); - + this._notebookKernelProviders.set(handle, { extension, emitter, provider }); return; } @@ -563,12 +590,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo entry?.emitter.fire(uriComponents ? URI.revive(uriComponents) : undefined); } - async $updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise { - this.logService.debug('MainThreadNotebooks#updateNotebookLanguages', resource.path, languages); - const textModel = this._notebookService.getNotebookTextModel(URI.from(resource)); - textModel?.updateLanguages(languages); - } - async $postMessage(editorId: string, forRendererId: string | undefined, value: any): Promise { const editor = this._notebookService.getNotebookEditor(editorId) as INotebookEditor | undefined; if (editor?.isNotebookEditor) { @@ -635,9 +656,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo } if (statusBarEntry.visible) { - this._cellStatusBarEntries.set( - id, - this.cellStatusBarService.addEntry(statusBarEntry)); + this._cellStatusBarEntries.set(id, this._cellStatusBarService.addEntry(statusBarEntry)); } } @@ -661,23 +680,23 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo override: false, }; - const columnArg = viewColumnToEditorGroup(this._editorGroupService, options.position); + const columnArg = viewColumnToEditorGroup(this._editorGroupsService, options.position); let group: IEditorGroup | undefined = undefined; if (columnArg === SIDE_GROUP) { - const direction = preferredSideBySideGroupDirection(this.configurationService); + const direction = preferredSideBySideGroupDirection(this._configurationService); - let neighbourGroup = this.editorGroupsService.findGroup({ direction }); + let neighbourGroup = this._editorGroupsService.findGroup({ direction }); if (!neighbourGroup) { - neighbourGroup = this.editorGroupsService.addGroup(this.editorGroupsService.activeGroup, direction); + neighbourGroup = this._editorGroupsService.addGroup(this._editorGroupsService.activeGroup, direction); } group = neighbourGroup; } else { - group = this.editorGroupsService.getGroup(viewColumnToEditorGroup(this.editorGroupsService, columnArg)) ?? this.editorGroupsService.activeGroup; + group = this._editorGroupsService.getGroup(viewColumnToEditorGroup(this._editorGroupsService, columnArg)) ?? this._editorGroupsService.activeGroup; } - const input = this.editorService.createEditorInput({ resource: URI.revive(resource), options: editorOptions }); + const input = this._editorService.createEditorInput({ resource: URI.revive(resource), options: editorOptions }); // TODO: handle options.selection const editorPane = await this._instantiationService.invokeFunction(openEditorWith, input, viewType, options, group); @@ -695,45 +714,3 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo } } } - - -export class BoundModelReferenceCollection { - - private _data = new Array<{ uri: URI, dispose(): void }>(); - - constructor( - private readonly _extUri: IExtUri, - private readonly _maxAge: number = 1000 * 60 * 3, - ) { - // - } - - dispose(): void { - this._data = dispose(this._data); - } - - remove(uri: URI): void { - for (const entry of [...this._data] /* copy array because dispose will modify it */) { - if (this._extUri.isEqualOrParent(entry.uri, uri)) { - entry.dispose(); - } - } - } - - add(uri: URI, ref: IReference): void { - let handle: any; - let entry: { uri: URI, dispose(): void }; - const dispose = () => { - const idx = this._data.indexOf(entry); - if (idx >= 0) { - ref.dispose(); - clearTimeout(handle); - this._data.splice(idx, 1); - } - }; - handle = setTimeout(dispose, this._maxAge); - entry = { uri, dispose }; - - this._data.push(entry); - } -} diff --git a/src/vs/workbench/api/browser/mainThreadTesting.ts b/src/vs/workbench/api/browser/mainThreadTesting.ts index 95fc3e9ae7f..496ffe00d82 100644 --- a/src/vs/workbench/api/browser/mainThreadTesting.ts +++ b/src/vs/workbench/api/browser/mainThreadTesting.ts @@ -5,7 +5,9 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { isDefined } from 'vs/base/common/types'; 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 +20,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); } } } @@ -38,11 +41,20 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh this._register(this.testService.onShouldSubscribe(args => this.proxy.$subscribeToTests(args.resource, args.uri))); this._register(this.testService.onShouldUnsubscribe(args => this.proxy.$unsubscribeFromTests(args.resource, args.uri))); - // const testCompleteListener = this._register(new MutableDisposable()); - // todo(@connor4312): reimplement, maybe - // this._register(resultService.onResultsChanged(results => { - // testCompleteListener.value = results.onComplete(() => this.proxy.$publishTestResults({ tests: [] })); - // })); + + const prevResults = resultService.results.map(r => r.toJSON()).filter(isDefined); + if (prevResults.length) { + this.proxy.$publishTestResults(prevResults); + } + + this._register(resultService.onResultsChanged(evt => { + if ('completed' in evt) { + const serialized = evt.completed.toJSON(); + if (serialized) { + this.proxy.$publishTestResults([serialized]); + } + } + })); testService.updateRootProviderCount(1); @@ -71,6 +83,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/browser/mainThreadUriOpeners.ts b/src/vs/workbench/api/browser/mainThreadUriOpeners.ts index 200d4ae4dc0..96eb6fd8698 100644 --- a/src/vs/workbench/api/browser/mainThreadUriOpeners.ts +++ b/src/vs/workbench/api/browser/mainThreadUriOpeners.ts @@ -87,7 +87,10 @@ export class MainThreadUriOpeners extends Disposable implements MainThreadUriOpe this.notificationService.notify({ severity: Severity.Error, - message: localize('openerFailedMessage', 'Could not open uri with \'{0}\': {1}', id, e.toString()), + message: localize({ + key: 'openerFailedMessage', + comment: ['{0} is the id of the opener. {1} is the url being opened.'], + }, 'Could not open uri with \'{0}\': {1}', id, e.toString()), actions: { primary: [ openDefaultAction diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 865ded63659..b383152bc59 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); } } @@ -230,7 +231,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }, logout(providerId: string, sessionId: string): Thenable { checkProposedApiEnabled(extension); - return extHostAuthentication.logout(providerId, sessionId); + return extHostAuthentication.removeSession(providerId, sessionId); } }; @@ -340,11 +341,11 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }, get onDidChangeTestResults() { checkProposedApiEnabled(extension); - return extHostTesting.onLastResultsChanged; + return extHostTesting.onResultsChanged; }, get testResults() { checkProposedApiEnabled(extension); - return extHostTesting.lastResults; + return extHostTesting.results; }, }; @@ -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,14 +1262,23 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I // checkProposedApiEnabled(extension); return extHostTypes.TimelineItem; }, - get CellKind() { + get NotebookCellRange() { + return extHostTypes.NotebookCellRange; + }, + get NotebookCellKind() { // checkProposedApiEnabled(extension); - return extHostTypes.CellKind; + return extHostTypes.NotebookCellKind; }, get NotebookCellRunState() { // 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 9138a7ea7f5..523ddb0578a 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -50,13 +50,13 @@ import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService'; import { TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions } from 'vs/platform/remote/common/tunnel'; import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, InternalTimelineOptions } from 'vs/workbench/contrib/timeline/common/timeline'; import { revive } from 'vs/base/common/marshalling'; -import { INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEventDto, NotebookDataDto, IMainCellDto, INotebookDocumentFilter, INotebookKernelInfoDto2, TransientMetadata, INotebookCellStatusBarEntry, ICellRange, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter, IOutputDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEventDto, NotebookDataDto, IMainCellDto, INotebookDocumentFilter, TransientMetadata, INotebookCellStatusBarEntry, ICellRange, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter, IOutputDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy'; import { Dto } from 'vs/base/common/types'; import { DebugConfigurationProviderTriggerKind, WorkspaceTrustState } from 'vs/workbench/api/common/extHostTypes'; import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility'; import { IExtensionIdWithVersion } from 'vs/platform/userDataSync/common/extensionsStorageSync'; -import { InternalTestItem, InternalTestResults, ITestState, RunTestForProviderRequest, RunTestsRequest, TestIdWithProvider, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection'; +import { InternalTestItem, ITestState, RunTestForProviderRequest, RunTestsRequest, TestIdWithProvider, TestsDiff, ISerializedTestResults } from 'vs/workbench/contrib/testing/common/testCollection'; import { CandidatePort } from 'vs/workbench/services/remote/common/remoteExplorerService'; import { WorkspaceTrustStateChangeEvent } from 'vs/platform/workspace/common/workspaceTrust'; import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; @@ -168,7 +168,7 @@ export interface MainThreadAuthenticationShape extends IDisposable { $ensureProvider(id: string): Promise; $sendDidChangeSessions(providerId: string, event: modes.AuthenticationSessionsChangeEvent): void; $getSession(providerId: string, scopes: string[], extensionId: string, extensionName: string, options: { createIfNone?: boolean, clearSessionPreference?: boolean }): Promise; - $logout(providerId: string, sessionId: string): Promise; + $removeSession(providerId: string, sessionId: string): Promise; } export interface MainThreadSecretStateShape extends IDisposable { @@ -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; @@ -789,7 +791,6 @@ export interface MainThreadNotebookShape extends IDisposable { $unregisterNotebookKernelProvider(handle: number): Promise; $onNotebookKernelChange(handle: number, uri: UriComponents | undefined): void; $tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[]): Promise; - $updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise; $postMessage(editorId: string, forRendererId: string | undefined, value: any): Promise; $setStatusBarEntry(id: number, statusBarEntry: INotebookCellStatusBarEntryDto): Promise; $tryOpenDocument(uriComponents: UriComponents, viewType?: string): Promise; @@ -991,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; @@ -999,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 { @@ -1126,10 +1134,9 @@ export interface ExtHostLabelServiceShape { } export interface ExtHostAuthenticationShape { - $getSessions(id: string): Promise>; - $getSessionAccessToken(id: string, sessionId: string): Promise; - $login(id: string, scopes: string[]): Promise; - $logout(id: string, sessionId: 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; $onDidChangeAuthenticationProviders(added: modes.AuthenticationProviderInformation[], removed: modes.AuthenticationProviderInformation[]): Promise; $setProviders(providers: modes.AuthenticationProviderInformation[]): Promise; @@ -1468,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; @@ -1479,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; @@ -1733,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 { @@ -1777,8 +1784,22 @@ export interface INotebookDocumentsAndEditorsDelta { visibleEditors?: string[]; } +export interface INotebookKernelInfoDto2 { + id?: string; + friendlyId: string; + label: string; + extension: ExtensionIdentifier; + extensionLocation: UriComponents; + providerHandle?: number; + description?: string; + detail?: string; + isPreferred?: boolean; + preloads?: UriComponents[]; + supportedLanguages?: string[] +} + 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; @@ -1791,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; @@ -1831,7 +1853,7 @@ export interface ExtHostTestingShape { $unsubscribeFromTests(resource: ExtHostTestingResource, uri: UriComponents): void; $lookupTest(test: TestIdWithProvider): Promise; $acceptDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void; - $publishTestResults(results: InternalTestResults): void; + $publishTestResults(results: ISerializedTestResults[]): void; } export interface MainThreadTestingShape { 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 5a3aab45657..b4445864a7c 100644 --- a/src/vs/workbench/api/common/extHostAuthentication.ts +++ b/src/vs/workbench/api/common/extHostAuthentication.ts @@ -87,13 +87,13 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape { return this._proxy.$getSession(providerId, scopes, extensionId, extensionName, options); } - async logout(providerId: string, sessionId: string): Promise { + async removeSession(providerId: string, sessionId: string): Promise { const providerData = this._authenticationProviders.get(providerId); if (!providerData) { - return this._proxy.$logout(providerId, sessionId); + return this._proxy.$removeSession(providerId, sessionId); } - return providerData.provider.logout(sessionId); + return providerData.provider.removeSession(sessionId); } registerAuthenticationProvider(id: string, label: string, provider: vscode.AuthenticationProvider, options?: vscode.AuthenticationProviderOptions): vscode.Disposable { @@ -129,43 +129,28 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape { }); } - $login(providerId: string, scopes: string[]): Promise { + $createSession(providerId: string, scopes: string[]): Promise { const providerData = this._authenticationProviders.get(providerId); if (providerData) { - return Promise.resolve(providerData.provider.login(scopes)); + return Promise.resolve(providerData.provider.createSession(scopes)); } throw new Error(`Unable to find authentication provider with handle: ${providerId}`); } - $logout(providerId: string, sessionId: string): Promise { + $removeSession(providerId: string, sessionId: string): Promise { const providerData = this._authenticationProviders.get(providerId); if (providerData) { - return Promise.resolve(providerData.provider.logout(sessionId)); + return Promise.resolve(providerData.provider.removeSession(sessionId)); } throw new Error(`Unable to find authentication provider with handle: ${providerId}`); } - $getSessions(providerId: string): Promise> { + $getSessions(providerId: string, scopes?: string[]): Promise> { const providerData = this._authenticationProviders.get(providerId); if (providerData) { - return Promise.resolve(providerData.provider.getSessions()); - } - - throw new Error(`Unable to find authentication provider with handle: ${providerId}`); - } - - async $getSessionAccessToken(providerId: string, sessionId: string): Promise { - const providerData = this._authenticationProviders.get(providerId); - if (providerData) { - const sessions = await providerData.provider.getSessions(); - const session = sessions.find(session => session.id === sessionId); - if (session) { - return session.accessToken; - } - - throw new Error(`Unable to find session with id: ${sessionId}`); + return Promise.resolve(providerData.provider.getSessions(scopes)); } throw new Error(`Unable to find authentication provider with handle: ${providerId}`); diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 2274a4f12e7..47641e7993c 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -412,6 +412,14 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme get storageUri() { return that._storagePath.workspaceValue(extensionDescription); }, get globalStorageUri() { return that._storagePath.globalValue(extensionDescription); }, get extensionMode() { return extensionMode; }, + get extensionId() { + checkProposedApiEnabled(extensionDescription); + return extensionDescription.identifier.value; + }, + get extensionVersion() { + checkProposedApiEnabled(extensionDescription); + return extensionDescription.version; + }, get extensionRuntime() { checkProposedApiEnabled(extensionDescription); return that.extensionRuntime; 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 3ca6106e7e7..e6ba271e43d 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -9,7 +9,7 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; import * as UUID from 'vs/base/common/uuid'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { ExtHostNotebookShape, ICommandDto, IMainContext, IModelAddedData, INotebookDocumentPropertiesChangeData, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookEditorPropertiesChangeData, MainContext, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostNotebookShape, ICommandDto, IMainContext, IModelAddedData, INotebookDocumentPropertiesChangeData, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookEditorPropertiesChangeData, INotebookKernelInfoDto2, MainContext, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol'; import { ILogService } from 'vs/platform/log/common/log'; import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; @@ -17,7 +17,7 @@ import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePa import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview'; -import { CellStatusbarAlignment, CellUri, INotebookCellStatusBarEntry, INotebookDisplayOrder, INotebookExclusiveDocumentFilter, INotebookKernelInfoDto2, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookDataDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellStatusbarAlignment, CellUri, INotebookCellStatusBarEntry, INotebookDisplayOrder, INotebookExclusiveDocumentFilter, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookDataDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import * as vscode from 'vscode'; import { ResourceMap } from 'vs/base/common/map'; import { ExtHostCell, ExtHostNotebookDocument } from './extHostNotebookDocument'; @@ -123,10 +123,12 @@ 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, - preloads: kernel.preloads + preloads: kernel.preloads, + supportedLanguages: kernel.supportedLanguages }; }); @@ -402,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 { @@ -441,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}'`); @@ -453,7 +455,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN ...notebookDocumentMetadataDefaults, ...data.metadata }, - languages: data.languages, cells: data.cells.map(typeConverters.NotebookCellData.from), }; } @@ -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._proxy, 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/extHostNotebookConcatDocument.ts b/src/vs/workbench/api/common/extHostNotebookConcatDocument.ts index 4702e1c8165..6b1fb1f5e95 100644 --- a/src/vs/workbench/api/common/extHostNotebookConcatDocument.ts +++ b/src/vs/workbench/api/common/extHostNotebookConcatDocument.ts @@ -11,7 +11,6 @@ import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { score } from 'vs/editor/common/modes/languageSelector'; -import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ResourceMap } from 'vs/base/common/map'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; @@ -76,7 +75,7 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD const cellLengths: number[] = []; const cellLineCounts: number[] = []; for (const cell of this._notebook.cells) { - if (cell.cellKind === CellKind.Code && (!this._selector || score(this._selector, cell.uri, cell.language, true))) { + if (cell.cellKind === types.NotebookCellKind.Code && (!this._selector || score(this._selector, cell.uri, cell.language, true))) { this._cellUris.set(cell.uri, this._cells.length); this._cells.push(cell); cellLengths.push(cell.document.getText().length + 1); diff --git a/src/vs/workbench/api/common/extHostNotebookDocument.ts b/src/vs/workbench/api/common/extHostNotebookDocument.ts index b20fb518865..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, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol'; +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 { @@ -88,10 +84,10 @@ export class ExtHostCell extends Disposable { get index() { return that._notebook.getCellIndex(that); }, notebook: that._notebook.notebookDocument, uri: that.uri, - cellKind: this._cellData.cellKind, + 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,22 +139,18 @@ 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; - private _languages: string[] = []; constructor( - private readonly _proxy: MainThreadNotebookShape, private readonly _documentsAndEditors: ExtHostDocumentsAndEditors, 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 ) { @@ -177,8 +175,6 @@ export class ExtHostNotebookDocument extends Disposable { get isDirty() { return that._isDirty; }, get isUntitled() { return that.uri.scheme === Schemas.untitled; }, get cells(): ReadonlyArray { return that._cells.map(cell => cell.cell); }, - get languages() { return that._languages; }, - set languages(value: string[]) { that._trySetLanguages(value); }, get metadata() { return that._metadata; }, set metadata(_value: Required) { throw new Error('Use WorkspaceEdit to update metadata.'); }, get contentOptions() { return that._contentOptions; } @@ -187,11 +183,6 @@ export class ExtHostNotebookDocument extends Disposable { return this._notebook; } - private _trySetLanguages(newLanguages: string[]) { - this._languages = newLanguages; - this._proxy.$updateNotebookLanguages(this._viewType, this.uri, this._languages); - } - getNewBackupUri(): URI { if (!this._storagePath) { throw new Error('Backup requires a valid storage path'); @@ -215,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 }); } @@ -231,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) { @@ -272,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({ @@ -319,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/extHostTesting.ts b/src/vs/workbench/api/common/extHostTesting.ts index e1c86d4975c..b24ae70ab53 100644 --- a/src/vs/workbench/api/common/extHostTesting.ts +++ b/src/vs/workbench/api/common/extHostTesting.ts @@ -9,6 +9,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter } from 'vs/base/common/event'; import { once } from 'vs/base/common/functional'; import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { deepFreeze } from 'vs/base/common/objects'; import { isDefined } from 'vs/base/common/types'; import { URI, UriComponents } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; @@ -20,7 +21,7 @@ import { TestItem, TestState } from 'vs/workbench/api/common/extHostTypeConverte import { Disposable } from 'vs/workbench/api/common/extHostTypes'; import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import { OwnedTestCollection, SingleUseTestCollection } from 'vs/workbench/contrib/testing/common/ownedTestCollection'; -import { AbstractIncrementalTestCollection, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, InternalTestItemWithChildren, InternalTestResults, RunTestForProviderRequest, TestDiffOpType, TestIdWithProvider, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection'; +import { AbstractIncrementalTestCollection, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, ISerializedTestResults, RunTestForProviderRequest, SerializedTestResultItem, TestDiffOpType, TestIdWithProvider, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection'; import type * as vscode from 'vscode'; const getTestSubscriptionKey = (resource: ExtHostTestingResource, uri: URI) => `${resource}:${uri.toString()}`; @@ -39,8 +40,8 @@ export class ExtHostTesting implements ExtHostTestingShape { private workspaceObservers: WorkspaceFolderTestObserverFactory; private textDocumentObservers: TextDocumentTestObserverFactory; - public onLastResultsChanged = this.resultsChangedEmitter.event; - public lastResults?: vscode.TestResults; + public onResultsChanged = this.resultsChangedEmitter.event; + public results: ReadonlyArray = []; constructor(@IExtHostRpcService rpc: IExtHostRpcService, @IExtHostDocumentsAndEditors private readonly documents: IExtHostDocumentsAndEditors, @IExtHostWorkspace private readonly workspace: IExtHostWorkspace) { this.proxy = rpc.getProxy(MainContext.MainThreadTesting); @@ -105,11 +106,15 @@ export class ExtHostTesting implements ExtHostTestingShape { * Updates test results shown to extensions. * @override */ - public $publishTestResults(results: InternalTestResults): void { - const convert = (item: InternalTestItemWithChildren): vscode.RequiredTestItem => - ({ ...TestItem.toShallow(item.item), children: item.children.map(convert) }); + public $publishTestResults(results: ISerializedTestResults[]): void { + this.results = Object.freeze( + results + .map(r => deepFreeze(convertTestResults(r))) + .concat(this.results) + .sort((a, b) => b.completedAt - a.completedAt) + .slice(0, 32), + ); - this.lastResults = { tests: results.tests.map(convert) }; this.resultsChangedEmitter.fire(); } @@ -350,6 +355,29 @@ export class ExtHostTesting implements ExtHostTestingShape { } } +const convertTestResultItem = (item: SerializedTestResultItem, byInternalId: Map): vscode.TestItemWithResults => ({ + ...TestItem.toShallow(item.item), + result: TestState.to(item.state), + children: item.children + .map(c => byInternalId.get(c)) + .filter(isDefined) + .map(c => convertTestResultItem(c, byInternalId)), +}); + +const convertTestResults = (r: ISerializedTestResults): vscode.TestResults => { + const roots: SerializedTestResultItem[] = []; + const byInternalId = new Map(); + for (const item of r.items) { + byInternalId.set(item.id, item); + if (item.direct) { + roots.push(item); + } + } + + return { completedAt: r.completedAt, results: roots.map(r => convertTestResultItem(r, byInternalId)) }; +}; + + /* * A class which wraps a vscode.TestItem that provides the ability to filter a TestItem's children * to only the children that are located in a certain vscode.Uri. 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 5f211700488..bc0db6649e5 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, 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,21 +1394,74 @@ 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.runState, 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): notebooks.CellKind { + switch (data) { + case types.NotebookCellKind.Markdown: + return notebooks.CellKind.Markdown; + case types.NotebookCellKind.Code: + default: + return notebooks.CellKind.Code; + } + } + + export function to(data: notebooks.CellKind): vscode.NotebookCellKind { + switch (data) { + case notebooks.CellKind.Markdown: + return types.NotebookCellKind.Markdown; + case notebooks.CellKind.Code: + default: + return types.NotebookCellKind.Code; + } + } +} + export namespace NotebookCellData { - export function from(data: vscode.NotebookCellData): ICellDto2 { + export function from(data: vscode.NotebookCellData): notebooks.ICellDto2 { return { - cellKind: data.cellKind, + cellKind: NotebookCellKind.from(data.cellKind), language: data.language, source: data.source, metadata: data.metadata, @@ -1363,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); } } @@ -1466,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, @@ -1485,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, })) ?? [], }; } @@ -1514,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 dac847fc2ae..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( @@ -2896,7 +2966,6 @@ export class NotebookDocumentMetadata { readonly custom: { [key: string]: any; } = {}, readonly runState: NotebookRunState = NotebookRunState.Idle, readonly trusted: boolean = true, - readonly languages: string[] = [], ) { } with(change: Partial>) { @@ -2909,8 +2978,7 @@ export class NotebookDocumentMetadata { change.displayOrder ?? this.displayOrder, change.custom ?? this.custom, change.runState ?? this.runState, - change.trusted ?? this.trusted, - change.languages ?? this.languages, + change.trusted ?? this.trusted ); } } @@ -2924,18 +2992,33 @@ 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 CellKind { +export enum NotebookCellKind { Markdown = 1, Code = 2 } 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/developerActions.ts b/src/vs/workbench/browser/actions/developerActions.ts index 9eb0d3ea714..94160516029 100644 --- a/src/vs/workbench/browser/actions/developerActions.ts +++ b/src/vs/workbench/browser/actions/developerActions.ts @@ -5,7 +5,7 @@ import 'vs/css!./media/actions'; -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { domEvent } from 'vs/base/browser/event'; import { Color } from 'vs/base/common/color'; @@ -34,7 +34,7 @@ class InspectContextKeysAction extends Action2 { constructor() { super({ id: 'workbench.action.inspectContextKeys', - title: { value: nls.localize('inspect context keys', "Inspect Context Keys"), original: 'Inspect Context Keys' }, + title: { value: localize('inspect context keys', "Inspect Context Keys"), original: 'Inspect Context Keys' }, category: CATEGORIES.Developer, f1: true }); @@ -96,7 +96,7 @@ class ToggleScreencastModeAction extends Action2 { constructor() { super({ id: 'workbench.action.toggleScreencastMode', - title: { value: nls.localize('toggle screencast mode', "Toggle Screencast Mode"), original: 'Toggle Screencast Mode' }, + title: { value: localize('toggle screencast mode', "Toggle Screencast Mode"), original: 'Toggle Screencast Mode' }, category: CATEGORIES.Developer, f1: true }); @@ -241,7 +241,7 @@ class LogStorageAction extends Action2 { constructor() { super({ id: 'workbench.action.logStorage', - title: { value: nls.localize({ key: 'logStorage', comment: ['A developer only action to log the contents of the storage for the current window.'] }, "Log Storage Database Contents"), original: 'Log Storage Database Contents' }, + title: { value: localize({ key: 'logStorage', comment: ['A developer only action to log the contents of the storage for the current window.'] }, "Log Storage Database Contents"), original: 'Log Storage Database Contents' }, category: CATEGORIES.Developer, f1: true }); @@ -257,7 +257,7 @@ class LogWorkingCopiesAction extends Action2 { constructor() { super({ id: 'workbench.action.logWorkingCopies', - title: { value: nls.localize({ key: 'logWorkingCopies', comment: ['A developer only action to log the working copies that exist.'] }, "Log Working Copies"), original: 'Log Working Copies' }, + title: { value: localize({ key: 'logWorkingCopies', comment: ['A developer only action to log the working copies that exist.'] }, "Log Working Copies"), original: 'Log Working Copies' }, category: CATEGORIES.Developer, f1: true }); @@ -291,7 +291,7 @@ const configurationRegistry = Registry.as(ConfigurationE configurationRegistry.registerConfiguration({ id: 'screencastMode', order: 9, - title: nls.localize('screencastModeConfigurationTitle', "Screencast Mode"), + title: localize('screencastModeConfigurationTitle', "Screencast Mode"), type: 'object', properties: { 'screencastMode.verticalOffset': { @@ -299,18 +299,18 @@ configurationRegistry.registerConfiguration({ default: 20, minimum: 0, maximum: 90, - description: nls.localize('screencastMode.location.verticalPosition', "Controls the vertical offset of the screencast mode overlay from the bottom as a percentage of the workbench height.") + description: localize('screencastMode.location.verticalPosition', "Controls the vertical offset of the screencast mode overlay from the bottom as a percentage of the workbench height.") }, 'screencastMode.fontSize': { type: 'number', default: 56, minimum: 20, maximum: 100, - description: nls.localize('screencastMode.fontSize', "Controls the font size (in pixels) of the screencast mode keyboard.") + description: localize('screencastMode.fontSize', "Controls the font size (in pixels) of the screencast mode keyboard.") }, 'screencastMode.onlyKeyboardShortcuts': { type: 'boolean', - description: nls.localize('screencastMode.onlyKeyboardShortcuts', "Only show keyboard shortcuts in screencast mode."), + description: localize('screencastMode.onlyKeyboardShortcuts', "Only show keyboard shortcuts in screencast mode."), default: false }, 'screencastMode.keyboardOverlayTimeout': { @@ -318,20 +318,20 @@ configurationRegistry.registerConfiguration({ default: 800, minimum: 500, maximum: 5000, - description: nls.localize('screencastMode.keyboardOverlayTimeout', "Controls how long (in milliseconds) the keyboard overlay is shown in screencast mode.") + description: localize('screencastMode.keyboardOverlayTimeout', "Controls how long (in milliseconds) the keyboard overlay is shown in screencast mode.") }, 'screencastMode.mouseIndicatorColor': { type: 'string', format: 'color-hex', default: '#FF0000', - description: nls.localize('screencastMode.mouseIndicatorColor', "Controls the color in hex (#RGB, #RGBA, #RRGGBB or #RRGGBBAA) of the mouse indicator in screencast mode.") + description: localize('screencastMode.mouseIndicatorColor', "Controls the color in hex (#RGB, #RGBA, #RRGGBB or #RRGGBBAA) of the mouse indicator in screencast mode.") }, 'screencastMode.mouseIndicatorSize': { type: 'number', default: 20, minimum: 20, maximum: 100, - description: nls.localize('screencastMode.mouseIndicatorSize', "Controls the size (in pixels) of the mouse indicator in screencast mode.") + description: localize('screencastMode.mouseIndicatorSize', "Controls the size (in pixels) of the mouse indicator in screencast mode.") }, } }); diff --git a/src/vs/workbench/browser/actions/helpActions.ts b/src/vs/workbench/browser/actions/helpActions.ts index e12b61219f2..fccf6072614 100644 --- a/src/vs/workbench/browser/actions/helpActions.ts +++ b/src/vs/workbench/browser/actions/helpActions.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import product from 'vs/platform/product/common/product'; import { isMacintosh, isLinux, language } from 'vs/base/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -24,7 +24,7 @@ class KeybindingsReferenceAction extends Action2 { constructor() { super({ id: KeybindingsReferenceAction.ID, - title: { value: nls.localize('keybindingsReference', "Keyboard Shortcuts Reference"), original: 'Keyboard Shortcuts Reference' }, + title: { value: localize('keybindingsReference', "Keyboard Shortcuts Reference"), original: 'Keyboard Shortcuts Reference' }, category: CATEGORIES.Help, f1: true, keybinding: { @@ -54,7 +54,7 @@ class OpenDocumentationUrlAction extends Action2 { constructor() { super({ id: OpenDocumentationUrlAction.ID, - title: { value: nls.localize('openDocumentationUrl', "Documentation"), original: 'Documentation' }, + title: { value: localize('openDocumentationUrl', "Documentation"), original: 'Documentation' }, category: CATEGORIES.Help, f1: true }); @@ -78,7 +78,7 @@ class OpenIntroductoryVideosUrlAction extends Action2 { constructor() { super({ id: OpenIntroductoryVideosUrlAction.ID, - title: { value: nls.localize('openIntroductoryVideosUrl', "Introductory Videos"), original: 'Introductory Videos' }, + title: { value: localize('openIntroductoryVideosUrl', "Introductory Videos"), original: 'Introductory Videos' }, category: CATEGORIES.Help, f1: true }); @@ -102,7 +102,7 @@ class OpenTipsAndTricksUrlAction extends Action2 { constructor() { super({ id: OpenTipsAndTricksUrlAction.ID, - title: { value: nls.localize('openTipsAndTricksUrl', "Tips and Tricks"), original: 'Tips and Tricks' }, + title: { value: localize('openTipsAndTricksUrl', "Tips and Tricks"), original: 'Tips and Tricks' }, category: CATEGORIES.Help, f1: true }); @@ -126,7 +126,7 @@ class OpenNewsletterSignupUrlAction extends Action2 { constructor() { super({ id: OpenNewsletterSignupUrlAction.ID, - title: { value: nls.localize('newsletterSignup', "Signup for the VS Code Newsletter"), original: 'Signup for the VS Code Newsletter' }, + title: { value: localize('newsletterSignup', "Signup for the VS Code Newsletter"), original: 'Signup for the VS Code Newsletter' }, category: CATEGORIES.Help, f1: true }); @@ -151,7 +151,7 @@ class OpenTwitterUrlAction extends Action2 { constructor() { super({ id: OpenTwitterUrlAction.ID, - title: { value: nls.localize('openTwitterUrl', "Join Us on Twitter"), original: 'Join Us on Twitter' }, + title: { value: localize('openTwitterUrl', "Join Us on Twitter"), original: 'Join Us on Twitter' }, category: CATEGORIES.Help, f1: true }); @@ -175,7 +175,7 @@ class OpenRequestFeatureUrlAction extends Action2 { constructor() { super({ id: OpenRequestFeatureUrlAction.ID, - title: { value: nls.localize('openUserVoiceUrl', "Search Feature Requests"), original: 'Search Feature Requests' }, + title: { value: localize('openUserVoiceUrl', "Search Feature Requests"), original: 'Search Feature Requests' }, category: CATEGORIES.Help, f1: true }); @@ -199,7 +199,7 @@ class OpenLicenseUrlAction extends Action2 { constructor() { super({ id: OpenLicenseUrlAction.ID, - title: { value: nls.localize('openLicenseUrl', "View License"), original: 'View License' }, + title: { value: localize('openLicenseUrl', "View License"), original: 'View License' }, category: CATEGORIES.Help, f1: true }); @@ -228,7 +228,7 @@ class OpenPrivacyStatementUrlAction extends Action2 { constructor() { super({ id: OpenPrivacyStatementUrlAction.ID, - title: { value: nls.localize('openPrivacyStatement', "Privacy Statement"), original: 'Privacy Statement' }, + title: { value: localize('openPrivacyStatement', "Privacy Statement"), original: 'Privacy Statement' }, category: CATEGORIES.Help, f1: true }); @@ -296,7 +296,7 @@ if (OpenDocumentationUrlAction.AVAILABLE) { group: '1_welcome', command: { id: OpenDocumentationUrlAction.ID, - title: nls.localize({ key: 'miDocumentation', comment: ['&& denotes a mnemonic'] }, "&&Documentation") + title: localize({ key: 'miDocumentation', comment: ['&& denotes a mnemonic'] }, "&&Documentation") }, order: 3 }); @@ -308,7 +308,7 @@ if (KeybindingsReferenceAction.AVAILABLE) { group: '2_reference', command: { id: KeybindingsReferenceAction.ID, - title: nls.localize({ key: 'miKeyboardShortcuts', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts Reference") + title: localize({ key: 'miKeyboardShortcuts', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts Reference") }, order: 1 }); @@ -319,7 +319,7 @@ if (OpenIntroductoryVideosUrlAction.AVAILABLE) { group: '2_reference', command: { id: OpenIntroductoryVideosUrlAction.ID, - title: nls.localize({ key: 'miIntroductoryVideos', comment: ['&& denotes a mnemonic'] }, "Introductory &&Videos") + title: localize({ key: 'miIntroductoryVideos', comment: ['&& denotes a mnemonic'] }, "Introductory &&Videos") }, order: 2 }); @@ -330,7 +330,7 @@ if (OpenTipsAndTricksUrlAction.AVAILABLE) { group: '2_reference', command: { id: OpenTipsAndTricksUrlAction.ID, - title: nls.localize({ key: 'miTipsAndTricks', comment: ['&& denotes a mnemonic'] }, "Tips and Tri&&cks") + title: localize({ key: 'miTipsAndTricks', comment: ['&& denotes a mnemonic'] }, "Tips and Tri&&cks") }, order: 3 }); @@ -342,7 +342,7 @@ if (OpenTwitterUrlAction.AVAILABLE) { group: '3_feedback', command: { id: OpenTwitterUrlAction.ID, - title: nls.localize({ key: 'miTwitter', comment: ['&& denotes a mnemonic'] }, "&&Join Us on Twitter") + title: localize({ key: 'miTwitter', comment: ['&& denotes a mnemonic'] }, "&&Join Us on Twitter") }, order: 1 }); @@ -353,7 +353,7 @@ if (OpenRequestFeatureUrlAction.AVAILABLE) { group: '3_feedback', command: { id: OpenRequestFeatureUrlAction.ID, - title: nls.localize({ key: 'miUserVoice', comment: ['&& denotes a mnemonic'] }, "&&Search Feature Requests") + title: localize({ key: 'miUserVoice', comment: ['&& denotes a mnemonic'] }, "&&Search Feature Requests") }, order: 2 }); @@ -365,7 +365,7 @@ if (OpenLicenseUrlAction.AVAILABLE) { group: '4_legal', command: { id: OpenLicenseUrlAction.ID, - title: nls.localize({ key: 'miLicense', comment: ['&& denotes a mnemonic'] }, "View &&License") + title: localize({ key: 'miLicense', comment: ['&& denotes a mnemonic'] }, "View &&License") }, order: 1 }); @@ -376,7 +376,7 @@ if (OpenPrivacyStatementUrlAction.AVAILABE) { group: '4_legal', command: { id: OpenPrivacyStatementUrlAction.ID, - title: nls.localize({ key: 'miPrivacyStatement', comment: ['&& denotes a mnemonic'] }, "Privac&&y Statement") + title: localize({ key: 'miPrivacyStatement', comment: ['&& denotes a mnemonic'] }, "Privac&&y Statement") }, order: 2 }); diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index 7220bc5c04f..2f3c7c31c6a 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { Action } from 'vs/base/common/actions'; import { SyncActionDescriptor, MenuId, MenuRegistry, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; @@ -33,7 +33,7 @@ class CloseSidebarAction extends Action2 { constructor() { super({ id: 'workbench.action.closeSidebar', - title: { value: nls.localize('closeSidebar', "Close Side Bar"), original: 'Close Side Bar' }, + title: { value: localize('closeSidebar', "Close Side Bar"), original: 'Close Side Bar' }, category: CATEGORIES.View, f1: true }); @@ -51,7 +51,7 @@ registerAction2(CloseSidebarAction); export class ToggleActivityBarVisibilityAction extends Action2 { static readonly ID = 'workbench.action.toggleActivityBarVisibility'; - static readonly LABEL = nls.localize('toggleActivityBar', "Toggle Activity Bar Visibility"); + static readonly LABEL = localize('toggleActivityBar', "Toggle Activity Bar Visibility"); private static readonly activityBarVisibleKey = 'workbench.activityBar.visible'; @@ -81,7 +81,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '2_workbench_layout', command: { id: ToggleActivityBarVisibilityAction.ID, - title: nls.localize({ key: 'miShowActivityBar', comment: ['&& denotes a mnemonic'] }, "Show &&Activity Bar"), + title: localize({ key: 'miShowActivityBar', comment: ['&& denotes a mnemonic'] }, "Show &&Activity Bar"), toggled: ContextKeyExpr.equals('config.workbench.activityBar.visible', true) }, order: 4 @@ -96,7 +96,7 @@ class ToggleCenteredLayout extends Action2 { constructor() { super({ id: ToggleCenteredLayout.ID, - title: { value: nls.localize('toggleCenteredLayout', "Toggle Centered Layout"), original: 'Toggle Centered Layout' }, + title: { value: localize('toggleCenteredLayout', "Toggle Centered Layout"), original: 'Toggle Centered Layout' }, category: CATEGORIES.View, f1: true }); @@ -115,7 +115,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '1_toggle_view', command: { id: ToggleCenteredLayout.ID, - title: nls.localize({ key: 'miToggleCenteredLayout', comment: ['&& denotes a mnemonic'] }, "&&Centered Layout"), + title: localize({ key: 'miToggleCenteredLayout', comment: ['&& denotes a mnemonic'] }, "&&Centered Layout"), toggled: IsCenteredLayoutContext }, order: 3 @@ -126,7 +126,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { export class ToggleSidebarPositionAction extends Action { static readonly ID = 'workbench.action.toggleSidebarPosition'; - static readonly LABEL = nls.localize('toggleSidebarPosition', "Toggle Side Bar Position"); + static readonly LABEL = localize('toggleSidebarPosition', "Toggle Side Bar Position"); private static readonly sidebarPositionConfigurationKey = 'workbench.sideBar.location'; @@ -147,7 +147,7 @@ export class ToggleSidebarPositionAction extends Action { } static getLabel(layoutService: IWorkbenchLayoutService): string { - return layoutService.getSideBarPosition() === Position.LEFT ? nls.localize('moveSidebarRight', "Move Side Bar Right") : nls.localize('moveSidebarLeft', "Move Side Bar Left"); + return layoutService.getSideBarPosition() === Position.LEFT ? localize('moveSidebarRight', "Move Side Bar Right") : localize('moveSidebarLeft', "Move Side Bar Left"); } } @@ -155,7 +155,7 @@ registerAction2(class extends Action2 { constructor() { super({ id: ToggleSidebarPositionAction.ID, - title: { value: nls.localize('toggleSidebarPosition', "Toggle Side Bar Position"), original: 'Toggle Side Bar Position' }, + title: { value: localize('toggleSidebarPosition', "Toggle Side Bar Position"), original: 'Toggle Side Bar Position' }, category: CATEGORIES.View, f1: true }); @@ -170,7 +170,7 @@ MenuRegistry.appendMenuItems([{ group: '3_workbench_layout_move', command: { id: ToggleSidebarPositionAction.ID, - title: nls.localize('move sidebar right', "Move Side Bar Right") + title: localize('move sidebar right', "Move Side Bar Right") }, when: ContextKeyExpr.and(ContextKeyExpr.notEquals('config.workbench.sideBar.location', 'right'), ContextKeyExpr.equals('viewContainerLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar))), order: 1 @@ -181,7 +181,7 @@ MenuRegistry.appendMenuItems([{ group: '3_workbench_layout_move', command: { id: ToggleSidebarPositionAction.ID, - title: nls.localize('move sidebar right', "Move Side Bar Right") + title: localize('move sidebar right', "Move Side Bar Right") }, when: ContextKeyExpr.and(ContextKeyExpr.notEquals('config.workbench.sideBar.location', 'right'), ContextKeyExpr.equals('viewLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar))), order: 1 @@ -192,7 +192,7 @@ MenuRegistry.appendMenuItems([{ group: '3_workbench_layout_move', command: { id: ToggleSidebarPositionAction.ID, - title: nls.localize('move sidebar left', "Move Side Bar Left") + title: localize('move sidebar left', "Move Side Bar Left") }, when: ContextKeyExpr.and(ContextKeyExpr.equals('config.workbench.sideBar.location', 'right'), ContextKeyExpr.equals('viewContainerLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar))), order: 1 @@ -203,7 +203,7 @@ MenuRegistry.appendMenuItems([{ group: '3_workbench_layout_move', command: { id: ToggleSidebarPositionAction.ID, - title: nls.localize('move sidebar left', "Move Side Bar Left") + title: localize('move sidebar left', "Move Side Bar Left") }, when: ContextKeyExpr.and(ContextKeyExpr.equals('config.workbench.sideBar.location', 'right'), ContextKeyExpr.equals('viewLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar))), order: 1 @@ -214,7 +214,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '3_workbench_layout_move', command: { id: ToggleSidebarPositionAction.ID, - title: nls.localize({ key: 'miMoveSidebarRight', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Right") + title: localize({ key: 'miMoveSidebarRight', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Right") }, when: ContextKeyExpr.notEquals('config.workbench.sideBar.location', 'right'), order: 2 @@ -224,7 +224,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '3_workbench_layout_move', command: { id: ToggleSidebarPositionAction.ID, - title: nls.localize({ key: 'miMoveSidebarLeft', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Left") + title: localize({ key: 'miMoveSidebarLeft', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Left") }, when: ContextKeyExpr.equals('config.workbench.sideBar.location', 'right'), order: 2 @@ -234,7 +234,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { export class ToggleEditorVisibilityAction extends Action { static readonly ID = 'workbench.action.toggleEditorVisibility'; - static readonly LABEL = nls.localize('toggleEditor', "Toggle Editor Area Visibility"); + static readonly LABEL = localize('toggleEditor', "Toggle Editor Area Visibility"); constructor( id: string, @@ -255,7 +255,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '2_workbench_layout', command: { id: ToggleEditorVisibilityAction.ID, - title: nls.localize({ key: 'miShowEditorArea', comment: ['&& denotes a mnemonic'] }, "Show &&Editor Area"), + title: localize({ key: 'miShowEditorArea', comment: ['&& denotes a mnemonic'] }, "Show &&Editor Area"), toggled: EditorAreaVisibleContext }, order: 5 @@ -263,7 +263,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { group: '2_appearance', - title: nls.localize({ key: 'miAppearance', comment: ['&& denotes a mnemonic'] }, "&&Appearance"), + title: localize({ key: 'miAppearance', comment: ['&& denotes a mnemonic'] }, "&&Appearance"), submenu: MenuId.MenubarAppearanceMenu, order: 1 }); @@ -273,7 +273,7 @@ registerAction2(class extends Action2 { constructor() { super({ id: TOGGLE_SIDEBAR_VISIBILITY_ACTION_ID, - title: { value: nls.localize('toggleSidebar', "Toggle Side Bar Visibility"), original: 'Toggle Side Bar Visibility' }, + title: { value: localize('toggleSidebar', "Toggle Side Bar Visibility"), original: 'Toggle Side Bar Visibility' }, category: CATEGORIES.View, f1: true, keybinding: { @@ -293,7 +293,7 @@ MenuRegistry.appendMenuItems([{ group: '3_workbench_layout_move', command: { id: TOGGLE_SIDEBAR_VISIBILITY_ACTION_ID, - title: nls.localize('compositePart.hideSideBarLabel', "Hide Side Bar"), + title: localize('compositePart.hideSideBarLabel', "Hide Side Bar"), }, when: ContextKeyExpr.and(SideBarVisibleContext, ContextKeyExpr.equals('viewContainerLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar))), order: 2 @@ -304,7 +304,7 @@ MenuRegistry.appendMenuItems([{ group: '3_workbench_layout_move', command: { id: TOGGLE_SIDEBAR_VISIBILITY_ACTION_ID, - title: nls.localize('compositePart.hideSideBarLabel', "Hide Side Bar"), + title: localize('compositePart.hideSideBarLabel', "Hide Side Bar"), }, when: ContextKeyExpr.and(SideBarVisibleContext, ContextKeyExpr.equals('viewLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar))), order: 2 @@ -315,7 +315,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '2_workbench_layout', command: { id: TOGGLE_SIDEBAR_VISIBILITY_ACTION_ID, - title: nls.localize({ key: 'miShowSidebar', comment: ['&& denotes a mnemonic'] }, "Show &&Side Bar"), + title: localize({ key: 'miShowSidebar', comment: ['&& denotes a mnemonic'] }, "Show &&Side Bar"), toggled: SideBarVisibleContext }, order: 1 @@ -326,7 +326,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { export class ToggleStatusbarVisibilityAction extends Action { static readonly ID = 'workbench.action.toggleStatusbarVisibility'; - static readonly LABEL = nls.localize('toggleStatusbar', "Toggle Status Bar Visibility"); + static readonly LABEL = localize('toggleStatusbar', "Toggle Status Bar Visibility"); private static readonly statusbarVisibleKey = 'workbench.statusBar.visible'; @@ -353,7 +353,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '2_workbench_layout', command: { id: ToggleStatusbarVisibilityAction.ID, - title: nls.localize({ key: 'miShowStatusbar', comment: ['&& denotes a mnemonic'] }, "Show S&&tatus Bar"), + title: localize({ key: 'miShowStatusbar', comment: ['&& denotes a mnemonic'] }, "Show S&&tatus Bar"), toggled: ContextKeyExpr.equals('config.workbench.statusBar.visible', true) }, order: 3 @@ -364,7 +364,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { class ToggleTabsVisibilityAction extends Action { static readonly ID = 'workbench.action.toggleTabsVisibility'; - static readonly LABEL = nls.localize('toggleTabs', "Toggle Tab Visibility"); + static readonly LABEL = localize('toggleTabs', "Toggle Tab Visibility"); private static readonly tabsVisibleKey = 'workbench.editor.showTabs'; @@ -395,7 +395,7 @@ registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleTabsVisibilityA class ToggleZenMode extends Action { static readonly ID = 'workbench.action.toggleZenMode'; - static readonly LABEL = nls.localize('toggleZenMode', "Toggle Zen Mode"); + static readonly LABEL = localize('toggleZenMode', "Toggle Zen Mode"); constructor( id: string, @@ -416,7 +416,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '1_toggle_view', command: { id: ToggleZenMode.ID, - title: nls.localize('miToggleZenMode', "Zen Mode"), + title: localize('miToggleZenMode', "Zen Mode"), toggled: InEditorZenModeContext }, order: 2 @@ -438,7 +438,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ export class ToggleMenuBarAction extends Action { static readonly ID = 'workbench.action.toggleMenuBar'; - static readonly LABEL = nls.localize('toggleMenuBar', "Toggle Menu Bar"); + static readonly LABEL = localize('toggleMenuBar', "Toggle Menu Bar"); constructor( id: string, @@ -461,8 +461,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '2_workbench_layout', command: { id: ToggleMenuBarAction.ID, - title: nls.localize({ key: 'miShowMenuBar', comment: ['&& denotes a mnemonic'] }, "Show Menu &&Bar"), - toggled: ContextKeyExpr.and(IsMacNativeContext.toNegated(), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'hidden'), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'toggle')) + title: localize({ key: 'miShowMenuBar', comment: ['&& denotes a mnemonic'] }, "Show Menu &&Bar"), + toggled: ContextKeyExpr.and(IsMacNativeContext.toNegated(), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'hidden'), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'toggle'), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'compact')) }, when: IsMacNativeContext.toNegated(), order: 0 @@ -472,7 +472,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { export class ResetViewLocationsAction extends Action { static readonly ID = 'workbench.action.resetViewLocations'; - static readonly LABEL = nls.localize('resetViewLocations', "Reset View Locations"); + static readonly LABEL = localize('resetViewLocations', "Reset View Locations"); constructor( id: string, @@ -492,7 +492,7 @@ registry.registerWorkbenchAction(SyncActionDescriptor.from(ResetViewLocationsAct // --- Move View with Command export class MoveViewAction extends Action { static readonly ID = 'workbench.action.moveView'; - static readonly LABEL = nls.localize('moveView', "Move View"); + static readonly LABEL = localize('moveView', "Move View"); constructor( id: string, @@ -521,7 +521,7 @@ export class MoveViewAction extends Action { if (!hasAddedView) { results.push({ type: 'separator', - label: nls.localize('sidebarContainer', "Side Bar / {0}", containerModel.title) + label: localize('sidebarContainer', "Side Bar / {0}", containerModel.title) }); hasAddedView = true; } @@ -545,7 +545,7 @@ export class MoveViewAction extends Action { if (!hasAddedView) { results.push({ type: 'separator', - label: nls.localize('panelContainer', "Panel / {0}", containerModel.title) + label: localize('panelContainer', "Panel / {0}", containerModel.title) }); hasAddedView = true; } @@ -563,7 +563,7 @@ export class MoveViewAction extends Action { private async getView(viewId?: string): Promise { const quickPick = this.quickInputService.createQuickPick(); - quickPick.placeholder = nls.localize('moveFocusedView.selectView', "Select a View to Move"); + quickPick.placeholder = localize('moveFocusedView.selectView', "Select a View to Move"); quickPick.items = this.getViewItems(); quickPick.selectedItems = quickPick.items.filter(item => (item as IQuickPickItem).id === viewId) as IQuickPickItem[]; @@ -608,7 +608,7 @@ registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveViewAction), 'Vie // --- Move Focused View with Command export class MoveFocusedViewAction extends Action { static readonly ID = 'workbench.action.moveFocusedView'; - static readonly LABEL = nls.localize('moveFocusedView', "Move Focused View"); + static readonly LABEL = localize('moveFocusedView', "Move Focused View"); constructor( id: string, @@ -628,19 +628,19 @@ export class MoveFocusedViewAction extends Action { const focusedViewId = viewId || FocusedViewContext.getValue(this.contextKeyService); if (focusedViewId === undefined || focusedViewId.trim() === '') { - this.notificationService.error(nls.localize('moveFocusedView.error.noFocusedView', "There is no view currently focused.")); + this.notificationService.error(localize('moveFocusedView.error.noFocusedView', "There is no view currently focused.")); return; } const viewDescriptor = this.viewDescriptorService.getViewDescriptorById(focusedViewId); if (!viewDescriptor || !viewDescriptor.canMoveView) { - this.notificationService.error(nls.localize('moveFocusedView.error.nonMovableView', "The currently focused view is not movable.")); + this.notificationService.error(localize('moveFocusedView.error.nonMovableView', "The currently focused view is not movable.")); return; } const quickPick = this.quickInputService.createQuickPick(); - quickPick.placeholder = nls.localize('moveFocusedView.selectDestination', "Select a Destination for the View"); - quickPick.title = nls.localize({ key: 'moveFocusedView.title', comment: ['{0} indicates the title of the view the user has selected to move.'] }, "View: Move {0}", viewDescriptor.name); + quickPick.placeholder = localize('moveFocusedView.selectDestination', "Select a Destination for the View"); + quickPick.title = localize({ key: 'moveFocusedView.title', comment: ['{0} indicates the title of the view the user has selected to move.'] }, "View: Move {0}", viewDescriptor.name); const items: Array = []; const currentContainer = this.viewDescriptorService.getViewContainerByViewId(focusedViewId)!; @@ -650,20 +650,20 @@ export class MoveFocusedViewAction extends Action { if (!(isViewSolo && currentLocation === ViewContainerLocation.Panel)) { items.push({ id: '_.panel.newcontainer', - label: nls.localize({ key: 'moveFocusedView.newContainerInPanel', comment: ['Creates a new top-level tab in the panel.'] }, "New Panel Entry"), + label: localize({ key: 'moveFocusedView.newContainerInPanel', comment: ['Creates a new top-level tab in the panel.'] }, "New Panel Entry"), }); } if (!(isViewSolo && currentLocation === ViewContainerLocation.Sidebar)) { items.push({ id: '_.sidebar.newcontainer', - label: nls.localize('moveFocusedView.newContainerInSidebar', "New Side Bar Entry") + label: localize('moveFocusedView.newContainerInSidebar', "New Side Bar Entry") }); } items.push({ type: 'separator', - label: nls.localize('sidebar', "Side Bar") + label: localize('sidebar', "Side Bar") }); const pinnedViewlets = this.activityBarService.getVisibleViewContainerIds(); @@ -684,7 +684,7 @@ export class MoveFocusedViewAction extends Action { items.push({ type: 'separator', - label: nls.localize('panel', "Panel") + label: localize('panel', "Panel") }); const pinnedPanels = this.panelService.getPinnedPanels(); @@ -731,7 +731,7 @@ registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveFocusedViewAction // --- Reset View Location with Command export class ResetFocusedViewLocationAction extends Action { static readonly ID = 'workbench.action.resetFocusedViewLocation'; - static readonly LABEL = nls.localize('resetFocusedViewLocation', "Reset Focused View Location"); + static readonly LABEL = localize('resetFocusedViewLocation', "Reset Focused View Location"); constructor( id: string, @@ -753,7 +753,7 @@ export class ResetFocusedViewLocationAction extends Action { } if (!viewDescriptor) { - this.notificationService.error(nls.localize('resetFocusedView.error.noFocusedView', "There is no view currently focused.")); + this.notificationService.error(localize('resetFocusedView.error.noFocusedView', "There is no view currently focused.")); return; } @@ -807,7 +807,7 @@ export class IncreaseViewSizeAction extends BaseResizeViewAction { constructor() { super({ id: 'workbench.action.increaseViewSize', - title: { value: nls.localize('increaseViewSize', "Increase Current View Size"), original: 'Increase Current View Size' }, + title: { value: localize('increaseViewSize', "Increase Current View Size"), original: 'Increase Current View Size' }, f1: true }); } @@ -822,7 +822,7 @@ export class IncreaseViewWidthAction extends BaseResizeViewAction { constructor() { super({ id: 'workbench.action.increaseViewWidth', - title: { value: nls.localize('increaseEditorWidth', "Increase Editor Width"), original: 'Increase Editor Width' }, + title: { value: localize('increaseEditorWidth', "Increase Editor Width"), original: 'Increase Editor Width' }, f1: true }); } @@ -837,7 +837,7 @@ export class IncreaseViewHeightAction extends BaseResizeViewAction { constructor() { super({ id: 'workbench.action.increaseViewHeight', - title: { value: nls.localize('increaseEditorHeight', "Increase Editor Height"), original: 'Increase Editor Height' }, + title: { value: localize('increaseEditorHeight', "Increase Editor Height"), original: 'Increase Editor Height' }, f1: true }); } @@ -851,7 +851,7 @@ export class DecreaseViewSizeAction extends BaseResizeViewAction { constructor() { super({ id: 'workbench.action.decreaseViewSize', - title: { value: nls.localize('decreaseViewSize', "Decrease Current View Size"), original: 'Decrease Current View Size' }, + title: { value: localize('decreaseViewSize', "Decrease Current View Size"), original: 'Decrease Current View Size' }, f1: true }); } @@ -865,7 +865,7 @@ export class DecreaseViewWidthAction extends BaseResizeViewAction { constructor() { super({ id: 'workbench.action.decreaseViewWidth', - title: { value: nls.localize('decreaseEditorWidth', "Decrease Editor Width"), original: 'Decrease Editor Width' }, + title: { value: localize('decreaseEditorWidth', "Decrease Editor Width"), original: 'Decrease Editor Width' }, f1: true }); } @@ -881,7 +881,7 @@ export class DecreaseViewHeightAction extends BaseResizeViewAction { constructor() { super({ id: 'workbench.action.decreaseViewHeight', - title: { value: nls.localize('decreaseEditorHeight', "Decrease Editor Height"), original: 'Decrease Editor Height' }, + title: { value: localize('decreaseEditorHeight', "Decrease Editor Height"), original: 'Decrease Editor Height' }, f1: true }); } diff --git a/src/vs/workbench/browser/actions/navigationActions.ts b/src/vs/workbench/browser/actions/navigationActions.ts index 01831676a97..6122d65d983 100644 --- a/src/vs/workbench/browser/actions/navigationActions.ts +++ b/src/vs/workbench/browser/actions/navigationActions.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { Action } from 'vs/base/common/actions'; import { IEditorGroupsService, GroupDirection, GroupLocation, IFindGroupScope } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -137,7 +137,7 @@ abstract class BaseNavigationAction extends Action { class NavigateLeftAction extends BaseNavigationAction { static readonly ID = 'workbench.action.navigateLeft'; - static readonly LABEL = nls.localize('navigateLeft', "Navigate to the View on the Left"); + static readonly LABEL = localize('navigateLeft', "Navigate to the View on the Left"); constructor( id: string, @@ -154,7 +154,7 @@ class NavigateLeftAction extends BaseNavigationAction { class NavigateRightAction extends BaseNavigationAction { static readonly ID = 'workbench.action.navigateRight'; - static readonly LABEL = nls.localize('navigateRight', "Navigate to the View on the Right"); + static readonly LABEL = localize('navigateRight', "Navigate to the View on the Right"); constructor( id: string, @@ -171,7 +171,7 @@ class NavigateRightAction extends BaseNavigationAction { class NavigateUpAction extends BaseNavigationAction { static readonly ID = 'workbench.action.navigateUp'; - static readonly LABEL = nls.localize('navigateUp', "Navigate to the View Above"); + static readonly LABEL = localize('navigateUp', "Navigate to the View Above"); constructor( id: string, @@ -188,7 +188,7 @@ class NavigateUpAction extends BaseNavigationAction { class NavigateDownAction extends BaseNavigationAction { static readonly ID = 'workbench.action.navigateDown'; - static readonly LABEL = nls.localize('navigateDown', "Navigate to the View Below"); + static readonly LABEL = localize('navigateDown', "Navigate to the View Below"); constructor( id: string, @@ -229,7 +229,7 @@ function focusNextOrPreviousPart(layoutService: IWorkbenchLayoutService, editorS export class FocusNextPart extends Action { static readonly ID = 'workbench.action.focusNextPart'; - static readonly LABEL = nls.localize('focusNextPart', "Focus Next Part"); + static readonly LABEL = localize('focusNextPart', "Focus Next Part"); constructor( id: string, @@ -247,7 +247,7 @@ export class FocusNextPart extends Action { export class FocusPreviousPart extends Action { static readonly ID = 'workbench.action.focusPreviousPart'; - static readonly LABEL = nls.localize('focusPreviousPart', "Focus Previous Part"); + static readonly LABEL = localize('focusPreviousPart', "Focus Previous Part"); constructor( id: string, diff --git a/src/vs/workbench/browser/actions/windowActions.ts b/src/vs/workbench/browser/actions/windowActions.ts index 926527871e8..758a5b12d3c 100644 --- a/src/vs/workbench/browser/actions/windowActions.ts +++ b/src/vs/workbench/browser/actions/windowActions.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { IWindowOpenable } from 'vs/platform/windows/common/windows'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; @@ -46,18 +46,18 @@ abstract class BaseOpenRecentAction extends Action { private readonly removeFromRecentlyOpened: IQuickInputButton = { iconClass: Codicon.removeClose.classNames, - tooltip: nls.localize('remove', "Remove from Recently Opened") + tooltip: localize('remove', "Remove from Recently Opened") }; private readonly dirtyRecentlyOpenedFolder: IQuickInputButton = { iconClass: 'dirty-workspace ' + Codicon.closeDirty.classNames, - tooltip: nls.localize('dirtyRecentlyOpenedFolder', "Folder With Unsaved Files"), + tooltip: localize('dirtyRecentlyOpenedFolder', "Folder With Unsaved Files"), alwaysVisible: true }; private readonly dirtyRecentlyOpenedWorkspace: IQuickInputButton = { ...this.dirtyRecentlyOpenedFolder, - tooltip: nls.localize('dirtyRecentlyOpenedWorkspace', "Workspace With Unsaved Files"), + tooltip: localize('dirtyRecentlyOpenedWorkspace', "Workspace With Unsaved Files"), }; constructor( @@ -133,14 +133,14 @@ abstract class BaseOpenRecentAction extends Action { let keyMods: IKeyMods | undefined; - const workspaceSeparator: IQuickPickSeparator = { type: 'separator', label: hasWorkspaces ? nls.localize('workspacesAndFolders', "folders & workspaces") : nls.localize('folders', "folders") }; - const fileSeparator: IQuickPickSeparator = { type: 'separator', label: nls.localize('files', "files") }; + const workspaceSeparator: IQuickPickSeparator = { type: 'separator', label: hasWorkspaces ? localize('workspacesAndFolders', "folders & workspaces") : localize('folders', "folders") }; + const fileSeparator: IQuickPickSeparator = { type: 'separator', label: localize('files', "files") }; const picks = [workspaceSeparator, ...workspacePicks, fileSeparator, ...filePicks]; const pick = await this.quickInputService.pick(picks, { contextKey: inRecentFilesPickerContextKey, activeItem: [...workspacePicks, ...filePicks][autoFocusSecondEntry ? 1 : 0], - placeHolder: isMacintosh ? nls.localize('openRecentPlaceholderMac', "Select to open (hold Cmd-key to force new window or Alt-key for same window)") : nls.localize('openRecentPlaceholder', "Select to open (hold Ctrl-key to force new window or Alt-key for same window)"), + placeHolder: isMacintosh ? localize('openRecentPlaceholderMac', "Select to open (hold Cmd-key to force new window or Alt-key for same window)") : localize('openRecentPlaceholder', "Select to open (hold Ctrl-key to force new window or Alt-key for same window)"), matchOnDescription: true, onKeyMods: mods => keyMods = mods, quickNavigate: this.isQuickNavigate() ? { keybindings: this.keybindingService.lookupKeybindings(this.id) } : undefined, @@ -157,9 +157,9 @@ abstract class BaseOpenRecentAction extends Action { const isDirtyWorkspace = context.button === this.dirtyRecentlyOpenedWorkspace; const result = await this.dialogService.confirm({ type: 'question', - title: isDirtyWorkspace ? nls.localize('dirtyWorkspace', "Workspace with Unsaved Files") : nls.localize('dirtyFolder', "Folder with Unsaved Files"), - message: isDirtyWorkspace ? nls.localize('dirtyWorkspaceConfirm', "Do you want to open the workspace to review the unsaved files?") : nls.localize('dirtyFolderConfirm', "Do you want to open the folder to review the unsaved files?"), - detail: isDirtyWorkspace ? nls.localize('dirtyWorkspaceConfirmDetail', "Workspaces with unsaved files cannot be removed until all unsaved files have been saved or reverted.") : nls.localize('dirtyFolderConfirmDetail', "Folders with unsaved files cannot be removed until all unsaved files have been saved or reverted.") + title: isDirtyWorkspace ? localize('dirtyWorkspace', "Workspace with Unsaved Files") : localize('dirtyFolder', "Folder with Unsaved Files"), + message: isDirtyWorkspace ? localize('dirtyWorkspaceConfirm', "Do you want to open the workspace to review the unsaved files?") : localize('dirtyFolderConfirm', "Do you want to open the folder to review the unsaved files?"), + detail: isDirtyWorkspace ? localize('dirtyWorkspaceConfirmDetail', "Workspaces with unsaved files cannot be removed until all unsaved files have been saved or reverted.") : localize('dirtyFolderConfirmDetail', "Folders with unsaved files cannot be removed until all unsaved files have been saved or reverted.") }); if (result.confirmed) { @@ -212,7 +212,7 @@ abstract class BaseOpenRecentAction extends Action { return { iconClasses, label: name, - ariaLabel: isDirty ? isWorkspace ? nls.localize('recentDirtyWorkspaceAriaLabel', "{0}, workspace with unsaved changes", name) : nls.localize('recentDirtyFolderAriaLabel', "{0}, folder with unsaved changes", name) : name, + ariaLabel: isDirty ? isWorkspace ? localize('recentDirtyWorkspaceAriaLabel', "{0}, workspace with unsaved changes", name) : localize('recentDirtyFolderAriaLabel', "{0}, folder with unsaved changes", name) : name, description: parentPath, buttons: isDirty ? [isWorkspace ? this.dirtyRecentlyOpenedWorkspace : this.dirtyRecentlyOpenedFolder] : [this.removeFromRecentlyOpened], openable, @@ -224,7 +224,7 @@ abstract class BaseOpenRecentAction extends Action { export class OpenRecentAction extends BaseOpenRecentAction { static readonly ID = 'workbench.action.openRecent'; - static readonly LABEL = nls.localize('openRecent', "Open Recent..."); + static readonly LABEL = localize('openRecent', "Open Recent..."); constructor( id: string, @@ -250,7 +250,7 @@ export class OpenRecentAction extends BaseOpenRecentAction { class QuickPickRecentAction extends BaseOpenRecentAction { static readonly ID = 'workbench.action.quickOpenRecent'; - static readonly LABEL = nls.localize('quickOpenRecent', "Quick Open Recent..."); + static readonly LABEL = localize('quickOpenRecent', "Quick Open Recent..."); constructor( id: string, @@ -276,7 +276,7 @@ class QuickPickRecentAction extends BaseOpenRecentAction { class ToggleFullScreenAction extends Action { static readonly ID = 'workbench.action.toggleFullScreen'; - static readonly LABEL = nls.localize('toggleFullScreen', "Toggle Full Screen"); + static readonly LABEL = localize('toggleFullScreen', "Toggle Full Screen"); constructor( id: string, @@ -294,7 +294,7 @@ class ToggleFullScreenAction extends Action { export class ReloadWindowAction extends Action { static readonly ID = 'workbench.action.reloadWindow'; - static readonly LABEL = nls.localize('reloadWindow', "Reload Window"); + static readonly LABEL = localize('reloadWindow', "Reload Window"); constructor( id: string, @@ -314,7 +314,7 @@ export class ReloadWindowAction extends Action { class ShowAboutDialogAction extends Action { static readonly ID = 'workbench.action.showAboutDialog'; - static readonly LABEL = nls.localize('about', "About"); + static readonly LABEL = localize('about', "About"); constructor( id: string, @@ -332,7 +332,7 @@ class ShowAboutDialogAction extends Action { export class NewWindowAction extends Action { static readonly ID = 'workbench.action.newWindow'; - static readonly LABEL = nls.localize('newWindow', "New Window"); + static readonly LABEL = localize('newWindow', "New Window"); constructor( id: string, @@ -352,7 +352,7 @@ class BlurAction extends Action2 { constructor() { super({ id: 'workbench.action.blur', - title: nls.localize('blur', "Remove keyboard focus from focused element") + title: localize('blur', "Remove keyboard focus from focused element") }); } @@ -369,7 +369,7 @@ const registry = Registry.as(Extensions.WorkbenchActio // --- Actions Registration -const fileCategory = nls.localize('file', "File"); +const fileCategory = localize('file', "File"); registry.registerWorkbenchAction(SyncActionDescriptor.from(NewWindowAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_N }), 'New Window'); registry.registerWorkbenchAction(SyncActionDescriptor.from(QuickPickRecentAction), 'File: Quick Open Recent...', fileCategory); registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenRecentAction, { primary: KeyMod.CtrlCmd | KeyCode.KEY_R, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_R } }), 'File: Open Recent...', fileCategory); @@ -426,7 +426,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { group: 'z_ConfirmClose', command: { id: 'workbench.action.toggleConfirmBeforeClose', - title: nls.localize('miConfirmClose', "Confirm Before Close"), + title: localize('miConfirmClose', "Confirm Before Close"), toggled: ContextKeyExpr.notEquals('config.window.confirmBeforeClose', 'never') }, order: 1, @@ -437,13 +437,13 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { group: '1_new', command: { id: NewWindowAction.ID, - title: nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window") + title: localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window") }, order: 2 }); MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - title: nls.localize({ key: 'miOpenRecent', comment: ['&& denotes a mnemonic'] }, "Open &&Recent"), + title: localize({ key: 'miOpenRecent', comment: ['&& denotes a mnemonic'] }, "Open &&Recent"), submenu: MenuId.MenubarRecentMenu, group: '2_open', order: 4 @@ -453,7 +453,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, { group: 'y_more', command: { id: OpenRecentAction.ID, - title: nls.localize({ key: 'miMore', comment: ['&& denotes a mnemonic'] }, "&&More...") + title: localize({ key: 'miMore', comment: ['&& denotes a mnemonic'] }, "&&More...") }, order: 1 }); @@ -462,7 +462,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '1_toggle_view', command: { id: ToggleFullScreenAction.ID, - title: nls.localize({ key: 'miToggleFullScreen', comment: ['&& denotes a mnemonic'] }, "&&Full Screen"), + title: localize({ key: 'miToggleFullScreen', comment: ['&& denotes a mnemonic'] }, "&&Full Screen"), toggled: IsFullscreenContext }, order: 1 @@ -472,7 +472,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { group: 'z_about', command: { id: ShowAboutDialogAction.ID, - title: nls.localize({ key: 'miAbout', comment: ['&& denotes a mnemonic'] }, "&&About") + title: localize({ key: 'miAbout', comment: ['&& denotes a mnemonic'] }, "&&About") }, order: 1, when: IsMacNativeContext.toNegated() diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index 713e088b96f..03628a38b89 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Action } from 'vs/base/common/actions'; -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; @@ -23,12 +23,12 @@ 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 { static readonly ID = 'workbench.action.files.openFile'; - static readonly LABEL = nls.localize('openFile', "Open File..."); + static readonly LABEL = localize('openFile', "Open File..."); constructor( id: string, @@ -46,7 +46,7 @@ export class OpenFileAction extends Action { export class OpenFolderAction extends Action { static readonly ID = 'workbench.action.files.openFolder'; - static readonly LABEL = nls.localize('openFolder', "Open Folder..."); + static readonly LABEL = localize('openFolder', "Open Folder..."); constructor( id: string, @@ -64,7 +64,7 @@ export class OpenFolderAction extends Action { export class OpenFileFolderAction extends Action { static readonly ID = 'workbench.action.files.openFileFolder'; - static readonly LABEL = nls.localize('openFileFolder', "Open..."); + static readonly LABEL = localize('openFileFolder', "Open..."); constructor( id: string, @@ -82,7 +82,7 @@ export class OpenFileFolderAction extends Action { export class OpenWorkspaceAction extends Action { static readonly ID = 'workbench.action.openWorkspace'; - static readonly LABEL = nls.localize('openWorkspaceAction', "Open Workspace..."); + static readonly LABEL = localize('openWorkspaceAction', "Open Workspace..."); constructor( id: string, @@ -100,7 +100,7 @@ export class OpenWorkspaceAction extends Action { export class CloseWorkspaceAction extends Action { static readonly ID = 'workbench.action.closeFolder'; - static readonly LABEL = nls.localize('closeWorkspace', "Close Workspace"); + static readonly LABEL = localize('closeWorkspace', "Close Workspace"); constructor( id: string, @@ -115,7 +115,7 @@ export class CloseWorkspaceAction extends Action { async run(): Promise { if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { - this.notificationService.info(nls.localize('noWorkspaceOrFolderOpened', "There is currently no workspace or folder opened in this instance to close.")); + this.notificationService.info(localize('noWorkspaceOrFolderOpened', "There is currently no workspace or folder opened in this instance to close.")); return; } @@ -126,7 +126,7 @@ export class CloseWorkspaceAction extends Action { export class OpenWorkspaceConfigFileAction extends Action { static readonly ID = 'workbench.action.openWorkspaceConfigFile'; - static readonly LABEL = nls.localize('openWorkspaceConfigFile', "Open Workspace Configuration File"); + static readonly LABEL = localize('openWorkspaceConfigFile', "Open Workspace Configuration File"); constructor( id: string, @@ -168,7 +168,7 @@ export class AddRootFolderAction extends Action { export class GlobalRemoveRootFolderAction extends Action { static readonly ID = 'workbench.action.removeRootFolder'; - static readonly LABEL = nls.localize('globalRemoveFolderFromWorkspace', "Remove Folder from Workspace..."); + static readonly LABEL = localize('globalRemoveFolderFromWorkspace', "Remove Folder from Workspace..."); constructor( id: string, @@ -196,7 +196,7 @@ export class GlobalRemoveRootFolderAction extends Action { export class SaveWorkspaceAsAction extends Action { static readonly ID = 'workbench.action.saveWorkspaceAs'; - static readonly LABEL = nls.localize('saveWorkspaceAsAction', "Save Workspace As..."); + static readonly LABEL = localize('saveWorkspaceAsAction', "Save Workspace As..."); constructor( id: string, @@ -226,7 +226,7 @@ export class SaveWorkspaceAsAction extends Action { export class DuplicateWorkspaceInNewWindowAction extends Action { static readonly ID = 'workbench.action.duplicateWorkspaceInNewWindow'; - static readonly LABEL = nls.localize('duplicateWorkspaceInNewWindow', "Duplicate As Workspace in New Window"); + static readonly LABEL = localize('duplicateWorkspaceInNewWindow', "Duplicate As Workspace in New Window"); constructor( id: string, @@ -255,16 +255,16 @@ class WorkspaceTrustManageAction extends Action2 { constructor() { super({ id: 'workbench.action.manageTrust', - title: { value: nls.localize('manageTrustAction', "Manage Workspace Trust"), original: 'Manage Workspace Trust' }, + title: { value: localize('manageTrustAction', "Manage Workspace Trust"), original: 'Manage Workspace Trust' }, precondition: ContextKeyExpr.equals(`config.${WORKSPACE_TRUST_ENABLED}`, true), - category: nls.localize('workspacesCategory', "Workspaces"), - f1: true, + category: localize('workspacesCategory', "Workspaces"), + 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'); } } @@ -273,7 +273,7 @@ registerAction2(WorkspaceTrustManageAction); // --- Actions Registration const registry = Registry.as(Extensions.WorkbenchActions); -const workspacesCategory = nls.localize('workspaces', "Workspaces"); +const workspacesCategory = localize('workspaces', "Workspaces"); registry.registerWorkbenchAction(SyncActionDescriptor.from(AddRootFolderAction), 'Workspaces: Add Folder to Workspace...', workspacesCategory); registry.registerWorkbenchAction(SyncActionDescriptor.from(GlobalRemoveRootFolderAction), 'Workspaces: Remove Folder from Workspace...', workspacesCategory); @@ -291,7 +291,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { group: '3_workspace', command: { id: ADD_ROOT_FOLDER_COMMAND_ID, - title: nls.localize({ key: 'miAddFolderToWorkspace', comment: ['&& denotes a mnemonic'] }, "A&&dd Folder to Workspace...") + title: localize({ key: 'miAddFolderToWorkspace', comment: ['&& denotes a mnemonic'] }, "A&&dd Folder to Workspace...") }, order: 1 }); @@ -300,7 +300,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { group: '3_workspace', command: { id: SaveWorkspaceAsAction.ID, - title: nls.localize('miSaveWorkspaceAs', "Save Workspace As...") + title: localize('miSaveWorkspaceAs', "Save Workspace As...") }, order: 2, when: EmptyWorkspaceSupportContext @@ -318,7 +318,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { group: '6_close', command: { id: CloseWorkspaceAction.ID, - title: nls.localize({ key: 'miCloseFolder', comment: ['&& denotes a mnemonic'] }, "Close &&Folder"), + title: localize({ key: 'miCloseFolder', comment: ['&& denotes a mnemonic'] }, "Close &&Folder"), precondition: WorkspaceFolderCountContext.notEqualsTo('0') }, order: 3, @@ -329,7 +329,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { group: '6_close', command: { id: CloseWorkspaceAction.ID, - title: nls.localize({ key: 'miCloseWorkspace', comment: ['&& denotes a mnemonic'] }, "Close &&Workspace") + title: localize({ key: 'miCloseWorkspace', comment: ['&& denotes a mnemonic'] }, "Close &&Workspace") }, order: 3, when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('workspace'), EmptyWorkspaceSupportContext) diff --git a/src/vs/workbench/browser/actions/workspaceCommands.ts b/src/vs/workbench/browser/actions/workspaceCommands.ts index 7f43e0fb0e5..2f6b8df6f13 100644 --- a/src/vs/workbench/browser/actions/workspaceCommands.ts +++ b/src/vs/workbench/browser/actions/workspaceCommands.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; -import * as resources from 'vs/base/common/resources'; +import { dirname, removeTrailingPathSeparator } from 'vs/base/common/resources'; import { CancellationToken } from 'vs/base/common/cancellation'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; @@ -24,7 +24,7 @@ import { IOpenWindowOptions, IWindowOpenable } from 'vs/platform/windows/common/ import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; export const ADD_ROOT_FOLDER_COMMAND_ID = 'addRootFolder'; -export const ADD_ROOT_FOLDER_LABEL = nls.localize('addFolderToWorkspace', "Add Folder to Workspace..."); +export const ADD_ROOT_FOLDER_LABEL = localize('addFolderToWorkspace', "Add Folder to Workspace..."); export const PICK_WORKSPACE_FOLDER_COMMAND_ID = '_workbench.pickWorkspaceFolder'; @@ -61,8 +61,8 @@ CommandsRegistry.registerCommand({ const workspaceEditingService = accessor.get(IWorkspaceEditingService); const dialogsService = accessor.get(IFileDialogService); const folders = await dialogsService.showOpenDialog({ - openLabel: mnemonicButtonLabel(nls.localize({ key: 'add', comment: ['&& denotes a mnemonic'] }, "&&Add")), - title: nls.localize('addFolderToWorkspaceTitle', "Add Folder to Workspace"), + openLabel: mnemonicButtonLabel(localize({ key: 'add', comment: ['&& denotes a mnemonic'] }, "&&Add")), + title: localize('addFolderToWorkspaceTitle', "Add Folder to Workspace"), canSelectFolders: true, canSelectMany: true, defaultUri: await dialogsService.defaultFolderPath() @@ -72,7 +72,7 @@ CommandsRegistry.registerCommand({ return; } - await workspaceEditingService.addFolders(folders.map(folder => ({ uri: resources.removeTrailingPathSeparator(folder) }))); + await workspaceEditingService.addFolders(folders.map(folder => ({ uri: removeTrailingPathSeparator(folder) }))); } }); @@ -91,7 +91,7 @@ CommandsRegistry.registerCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID, async functio const folderPicks: IQuickPickItem[] = folders.map(folder => { return { label: folder.name, - description: labelService.getUriLabel(resources.dirname(folder.uri), { relative: true }), + description: labelService.getUriLabel(dirname(folder.uri), { relative: true }), folder, iconClasses: getIconClasses(modelService, modeService, folder.uri, FileKind.ROOT_FOLDER) }; @@ -104,7 +104,7 @@ CommandsRegistry.registerCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID, async functio } if (!options.placeHolder) { - options.placeHolder = nls.localize('workspaceFolderPickerPlaceholder', "Select workspace folder"); + options.placeHolder = localize('workspaceFolderPickerPlaceholder', "Select workspace folder"); } if (typeof options.matchOnDescription !== 'boolean') { diff --git a/src/vs/workbench/browser/composite.ts b/src/vs/workbench/browser/composite.ts index c0087e858f9..ff991b74b94 100644 --- a/src/vs/workbench/browser/composite.ts +++ b/src/vs/workbench/browser/composite.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IAction, IActionRunner, ActionRunner, IActionViewItem } from 'vs/base/common/actions'; +import { IAction, IActionRunner, ActionRunner } from 'vs/base/common/actions'; import { Component } from 'vs/workbench/common/component'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IComposite, ICompositeControl } from 'vs/workbench/common/composite'; @@ -14,6 +14,7 @@ import { trackFocus, Dimension } from 'vs/base/browser/dom'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { Disposable } from 'vs/base/common/lifecycle'; import { assertIsDefined } from 'vs/base/common/types'; +import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; /** * Composites are layed out in the sidebar and panel part of the workbench. At a time only one composite diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 1399e64b7d5..aee8f415442 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -1586,11 +1586,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi let newVisibilityValue: string; if (currentVisibilityValue === 'visible' || currentVisibilityValue === 'classic') { - newVisibilityValue = 'toggle'; - } else if (currentVisibilityValue === 'compact') { - newVisibilityValue = 'hidden'; + newVisibilityValue = 'compact'; } else { - newVisibilityValue = (isWeb && currentVisibilityValue === 'hidden') ? 'compact' : 'classic'; + newVisibilityValue = 'classic'; } this.configurationService.updateValue(Storage.MENU_VISIBILITY, newVisibilityValue); diff --git a/src/vs/workbench/browser/panecomposite.ts b/src/vs/workbench/browser/panecomposite.ts index 700678c478a..38e33e2bee8 100644 --- a/src/vs/workbench/browser/panecomposite.ts +++ b/src/vs/workbench/browser/panecomposite.ts @@ -13,9 +13,11 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { Composite } from 'vs/workbench/browser/composite'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { ViewPaneContainer } from './parts/views/viewPaneContainer'; +import { ViewPaneContainer, ViewsSubMenu } from './parts/views/viewPaneContainer'; import { IPaneComposite } from 'vs/workbench/common/panecomposite'; -import { IAction, IActionViewItem } from 'vs/base/common/actions'; +import { IAction, Separator } from 'vs/base/common/actions'; +import { SubmenuItemAction } from 'vs/platform/actions/common/actions'; +import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; export abstract class PaneComposite extends Composite implements IPaneComposite { @@ -66,15 +68,52 @@ export abstract class PaneComposite extends Composite implements IPaneComposite } getContextMenuActions(): ReadonlyArray { - return this.viewPaneContainer?.getContextMenuActions() ?? []; + return this.viewPaneContainer?.menuActions?.getContextMenuActions() ?? []; } getActions(): ReadonlyArray { - return this.viewPaneContainer?.getActions() ?? []; + const result = []; + if (this.viewPaneContainer?.menuActions) { + result.push(...this.viewPaneContainer.menuActions.getPrimaryActions()); + if (this.viewPaneContainer.isViewMergedWithContainer()) { + result.push(...this.viewPaneContainer.panes[0].menuActions.getPrimaryActions()); + } + } + return result; } getSecondaryActions(): ReadonlyArray { - return this.viewPaneContainer?.getSecondaryActions() ?? []; + if (!this.viewPaneContainer?.menuActions) { + return []; + } + + const viewPaneActions = this.viewPaneContainer.isViewMergedWithContainer() ? this.viewPaneContainer.panes[0].menuActions.getSecondaryActions() : []; + let menuActions = this.viewPaneContainer.menuActions.getSecondaryActions(); + + const viewsSubmenuActionIndex = menuActions.findIndex(action => action instanceof SubmenuItemAction && action.item.submenu === ViewsSubMenu); + if (viewsSubmenuActionIndex !== -1) { + const viewsSubmenuAction = menuActions[viewsSubmenuActionIndex]; + if (viewsSubmenuAction.actions.some(({ enabled }) => enabled)) { + if (menuActions.length === 1 && viewPaneActions.length === 0) { + menuActions = viewsSubmenuAction.actions.slice(); + } else if (viewsSubmenuActionIndex !== 0) { + menuActions = [viewsSubmenuAction, ...menuActions.slice(0, viewsSubmenuActionIndex), ...menuActions.slice(viewsSubmenuActionIndex + 1)]; + } + } else { + // Remove views submenu if none of the actions are enabled + menuActions.splice(viewsSubmenuActionIndex, 1); + } + } + + if (menuActions.length && viewPaneActions.length) { + return [ + ...menuActions, + new Separator(), + ...viewPaneActions + ]; + } + + return menuActions.length ? menuActions : viewPaneActions; } getActionViewItem(action: IAction): IActionViewItem | undefined { diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index e9f0df8d2e0..8b62c5a88de 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -240,7 +240,7 @@ export class AccountsActivityActionViewItem extends MenuActivityActionViewItem { })); const signOutAction = disposables.add(new Action('signOut', localize('signOut', "Sign Out"), '', true, () => { - return this.authenticationService.signOutOfAccount(sessionInfo.providerId, accountName); + return this.authenticationService.removeAccountSessions(sessionInfo.providerId, accountName, sessionInfo.sessions[accountName]); })); const providerSubMenuActions = [manageExtensionsAction]; diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index b0908cbc3e2..f9ee130df8c 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -32,7 +32,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { CustomMenubarControl } from 'vs/workbench/browser/parts/titlebar/menubarControl'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { getMenuBarVisibility } from 'vs/platform/windows/common/windows'; -import { isNative, isWeb } from 'vs/base/common/platform'; +import { isNative } from 'vs/base/common/platform'; import { Before2D } from 'vs/workbench/browser/dnd'; import { Codicon } from 'vs/base/common/codicons'; import { IAction, Separator, toAction } from 'vs/base/common/actions'; @@ -166,7 +166,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { // Menu const menuBarVisibility = getMenuBarVisibility(this.configurationService); - if (menuBarVisibility === 'compact' || (menuBarVisibility === 'hidden' && isWeb)) { + if (menuBarVisibility === 'compact' || menuBarVisibility === 'hidden' || menuBarVisibility === 'toggle') { topActions.push({ id: 'toggleMenuVisibility', label: localize('menu', "Menu"), @@ -174,7 +174,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { tooltip: localize('menu', "Menu"), checked: menuBarVisibility === 'compact', enabled: true, - run: async () => this.layoutService.toggleMenuBar(), + run: async () => this.configurationService.updateValue('window.menuBarVisibility', menuBarVisibility === 'compact' ? 'toggle' : 'compact'), dispose: () => { } }); } @@ -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..d96f65dff94 100644 --- a/src/vs/workbench/browser/parts/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositeBar.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { IAction, toAction } from 'vs/base/common/actions'; import { illegalArgument } from 'vs/base/common/errors'; -import * as arrays from 'vs/base/common/arrays'; +import { equals } from 'vs/base/common/arrays'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { IBadge } from 'vs/workbench/services/activity/common/activity'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -224,10 +224,9 @@ export class CompositeBar extends Widget implements ICompositeBar { ); }, orientation: this.options.orientation, - ariaLabel: nls.localize('activityBarAriaLabel', "Active View Switcher"), + ariaLabel: localize('activityBarAriaLabel', "Active View Switcher"), animated: false, preventLoopNavigation: this.options.preventLoopNavigation, - ignoreOrientationForPreviousAndNextKey: true, triggerKeys: { keyDown: true } })); @@ -539,7 +538,7 @@ export class CompositeBar extends Widget implements ICompositeBar { compositesToShow.length ? compositesToShow.splice(compositesToShow.length - 2, 1) : compositesToShow.pop(); } - const visibleCompositesChange = !arrays.equals(compositesToShow, this.visibleComposites); + const visibleCompositesChange = !equals(compositesToShow, this.visibleComposites); // Pull out overflow action if there is a composite change so that we can add it to the end later if (this.compositeOverflowAction && visibleCompositesChange) { diff --git a/src/vs/workbench/browser/parts/compositeBarActions.ts b/src/vs/workbench/browser/parts/compositeBarActions.ts index 3aac3df41ee..ee3b38958e1 100644 --- a/src/vs/workbench/browser/parts/compositeBarActions.ts +++ b/src/vs/workbench/browser/parts/compositeBarActions.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { Action, IAction, Separator } from 'vs/base/common/actions'; -import * as dom from 'vs/base/browser/dom'; +import { $, addDisposableListener, append, clearNode, EventHelper, EventType, getDomNodePagePosition, hide, show } from 'vs/base/browser/dom'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { dispose, toDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -208,11 +208,11 @@ export class ActivityActionViewItem extends BaseActionViewItem { } // Try hard to prevent keyboard only focus feedback when using mouse - this._register(dom.addDisposableListener(this.container, dom.EventType.MOUSE_DOWN, () => { + this._register(addDisposableListener(this.container, EventType.MOUSE_DOWN, () => { this.container.classList.add('clicked'); })); - this._register(dom.addDisposableListener(this.container, dom.EventType.MOUSE_UP, () => { + this._register(addDisposableListener(this.container, EventType.MOUSE_UP, () => { if (this.mouseUpTimeout) { clearTimeout(this.mouseUpTimeout); } @@ -223,19 +223,19 @@ export class ActivityActionViewItem extends BaseActionViewItem { })); // Label - this.label = dom.append(container, dom.$('a')); + this.label = append(container, $('a')); // Badge - this.badge = dom.append(container, dom.$('.badge')); - this.badgeContent = dom.append(this.badge, dom.$('.badge-content')); + this.badge = append(container, $('.badge')); + this.badgeContent = append(this.badge, $('.badge-content')); // Activity bar active border + background const isActivityBarItem = this.options.icon; if (isActivityBarItem) { - dom.append(container, dom.$('.active-item-indicator')); + append(container, $('.active-item-indicator')); } - dom.hide(this.badge); + hide(this.badge); this.updateActivity(); this.updateStyles(); @@ -263,8 +263,8 @@ export class ActivityActionViewItem extends BaseActionViewItem { this.badgeDisposable.clear(); - dom.clearNode(this.badgeContent); - dom.hide(this.badge); + clearNode(this.badgeContent); + hide(this.badge); if (badge) { @@ -282,26 +282,26 @@ export class ActivityActionViewItem extends BaseActionViewItem { } } this.badgeContent.textContent = number; - dom.show(this.badge); + show(this.badge); } } // Text else if (badge instanceof TextBadge) { this.badgeContent.textContent = badge.text; - dom.show(this.badge); + show(this.badge); } // Icon else if (badge instanceof IconBadge) { const clazzList = ThemeIcon.asClassNameArray(badge.icon); this.badgeContent.classList.add(...clazzList); - dom.show(this.badge); + show(this.badge); } // Progress else if (badge instanceof ProgressBadge) { - dom.show(this.badge); + show(this.badge); } if (clazz) { @@ -314,7 +314,7 @@ export class ActivityActionViewItem extends BaseActionViewItem { let title: string; if (badge?.getDescription()) { if (this.activity.name) { - title = nls.localize('badgeTitle', "{0} - {1}", this.activity.name, badge.getDescription()); + title = localize('badgeTitle', "{0} - {1}", this.activity.name, badge.getDescription()); } else { title = badge.getDescription(); } @@ -369,7 +369,7 @@ export class CompositeOverflowActivityAction extends ActivityAction { ) { super({ id: 'additionalComposites.action', - name: nls.localize('additionalViews', "Additional Views"), + name: localize('additionalViews', "Additional Views"), cssClass: Codicon.more.classNames }); } @@ -424,7 +424,7 @@ export class CompositeOverflowActivityActionViewItem extends ActivityActionViewI } if (suffix) { - action.label = nls.localize('numberBadge', "{0} ({1})", composite.name, suffix); + action.label = localize('numberBadge', "{0} ({1})", composite.name, suffix); } else { action.label = composite.name || ''; } @@ -447,7 +447,7 @@ class ManageExtensionAction extends Action { constructor( @ICommandService private readonly commandService: ICommandService ) { - super('activitybar.manage.extension', nls.localize('manageExtension', "Manage Extension")); + super('activitybar.manage.extension', localize('manageExtension', "Manage Extension")); } run(id: string): Promise { @@ -513,7 +513,7 @@ export class CompositeActionViewItem extends ActivityActionViewItem { } const keybinding = this.compositeActivityAction.activity.keybindingId ? this.keybindingService.lookupKeybinding(this.compositeActivityAction.activity.keybindingId) : null; - return keybinding ? nls.localize('titleKeybinding', "{0} ({1})", name, keybinding.getLabel()) : name; + return keybinding ? localize('titleKeybinding', "{0} ({1})", name, keybinding.getLabel()) : name; } render(container: HTMLElement): void { @@ -522,8 +522,8 @@ export class CompositeActionViewItem extends ActivityActionViewItem { this.updateChecked(); this.updateEnabled(); - this._register(dom.addDisposableListener(this.container, dom.EventType.CONTEXT_MENU, e => { - dom.EventHelper.stop(e, true); + this._register(addDisposableListener(this.container, EventType.CONTEXT_MENU, e => { + EventHelper.stop(e, true); this.showContextMenu(container); })); @@ -546,7 +546,7 @@ export class CompositeActionViewItem extends ActivityActionViewItem { }, onDrop: e => { - dom.EventHelper.stop(e.eventData, true); + EventHelper.stop(e.eventData, true); this.dndHandler.drop(e.dragAndDropData, this.activity.id, e.eventData, insertDropBefore); insertDropBefore = this.updateFromDragging(container, false, e.eventData); }, @@ -626,10 +626,10 @@ export class CompositeActionViewItem extends ActivityActionViewItem { const isPinned = this.compositeBar.isPinned(this.activity.id); if (isPinned) { - this.toggleCompositePinnedAction.label = nls.localize('hide', "Hide '{0}'", this.getActivtyName(true)); + this.toggleCompositePinnedAction.label = localize('hide', "Hide '{0}'", this.getActivtyName(true)); this.toggleCompositePinnedAction.checked = false; } else { - this.toggleCompositePinnedAction.label = nls.localize('keep', "Keep '{0}'", this.getActivtyName(true)); + this.toggleCompositePinnedAction.label = localize('keep', "Keep '{0}'", this.getActivtyName(true)); } const otherActions = this.contextMenuActionsProvider(); @@ -638,7 +638,7 @@ export class CompositeActionViewItem extends ActivityActionViewItem { actions.push(...otherActions); } - const elementPosition = dom.getDomNodePagePosition(container); + const elementPosition = getDomNodePagePosition(container); const anchor = { x: Math.floor(elementPosition.left + (elementPosition.width / 2)), y: elementPosition.top + elementPosition.height @@ -690,7 +690,7 @@ export class ToggleCompositePinnedAction extends Action { private activity: IActivity | undefined, private compositeBar: ICompositeBar ) { - super('show.toggleCompositePinned', activity ? activity.name : nls.localize('toggle', "Toggle View Pinned")); + super('show.toggleCompositePinned', activity ? activity.name : localize('toggle', "Toggle View Pinned")); this.checked = !!this.activity && this.compositeBar.isPinned(this.activity.id); } diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index 3e7f9fb96cf..1ead62e5b8e 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -4,15 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/compositepart'; -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { defaultGenerator } from 'vs/base/common/idGenerator'; import { IDisposable, dispose, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { Emitter } from 'vs/base/common/event'; -import * as errors from 'vs/base/common/errors'; +import { isPromiseCanceledError } from 'vs/base/common/errors'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; -import { ActionsOrientation, prepareActions } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionsOrientation, IActionViewItem, prepareActions } from 'vs/base/browser/ui/actionbar/actionbar'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; -import { IAction, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification, IActionViewItem } from 'vs/base/common/actions'; +import { IAction, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; import { Part, IPartOptions } from 'vs/workbench/browser/part'; import { Composite, CompositeRegistry } from 'vs/workbench/browser/composite'; import { IComposite } from 'vs/workbench/common/composite'; @@ -258,7 +258,7 @@ export abstract class CompositePart extends Part { this.telemetryActionsListener.value = toolBar.actionRunner.onDidRun(e => { // Check for Error - if (e.error && !errors.isPromiseCanceledError(e.error)) { + if (e.error && !isPromiseCanceledError(e.error)) { this.notificationService.error(e.error); } @@ -318,7 +318,7 @@ export abstract class CompositePart extends Part { this.titleLabel.updateTitle(compositeId, compositeTitle, withNullAsUndefined(keybinding?.getLabel())); const toolBar = assertIsDefined(this.toolBar); - toolBar.setAriaLabel(nls.localize('ariaCompositeToolbarLabel', "{0} actions", compositeTitle)); + toolBar.setAriaLabel(localize('ariaCompositeToolbarLabel', "{0} actions", compositeTitle)); } private collectCompositeActions(composite?: Composite): () => void { @@ -394,7 +394,7 @@ export abstract class CompositePart extends Part { orientation: ActionsOrientation.HORIZONTAL, getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id), anchorAlignmentProvider: () => this.getTitleAreaDropDownAnchorAlignment(), - toggleMenuTitle: nls.localize('viewsAndMoreActions', "Views and More Actions...") + toggleMenuTitle: localize('viewsAndMoreActions', "Views and More Actions...") })); this.collectCompositeActions()(); @@ -413,7 +413,7 @@ export abstract class CompositePart extends Part { // The title label is shared for all composites in the base CompositePart if (!this.activeComposite || this.activeComposite.getId() === id) { titleLabel.innerText = title; - titleLabel.title = keybinding ? nls.localize('titleTooltip', "{0} ({1})", title, keybinding) : title; + titleLabel.title = keybinding ? localize('titleTooltip', "{0} ({1})", title, keybinding) : title; } }, diff --git a/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts b/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts index 83550c00b8e..babf68f0dfa 100644 --- a/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts +++ b/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { IDialogOptions, IConfirmation, IConfirmationResult, DialogType, IShowResult, IInputResult, ICheckbox, IInput, IDialogHandler } from 'vs/platform/dialogs/common/dialogs'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { ILogService } from 'vs/platform/log/common/log'; @@ -46,13 +46,13 @@ export class BrowserDialogHandler implements IDialogHandler { if (confirmation.primaryButton) { buttons.push(confirmation.primaryButton); } else { - buttons.push(nls.localize({ key: 'yesButton', comment: ['&& denotes a mnemonic'] }, "&&Yes")); + buttons.push(localize({ key: 'yesButton', comment: ['&& denotes a mnemonic'] }, "&&Yes")); } if (confirmation.secondaryButton) { buttons.push(confirmation.secondaryButton); } else if (typeof confirmation.secondaryButton === 'undefined') { - buttons.push(nls.localize('cancelButton', "Cancel")); + buttons.push(localize('cancelButton', "Cancel")); } const result = await this.doShow(confirmation.type, confirmation.message, buttons, confirmation.detail, 1, confirmation.checkbox); @@ -121,7 +121,7 @@ export class BrowserDialogHandler implements IDialogHandler { async about(): Promise { const detailString = (useAgo: boolean): string => { - return nls.localize('aboutDetail', + return localize('aboutDetail', "Version: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}", this.productService.version || 'Unknown', this.productService.commit || 'Unknown', @@ -134,7 +134,7 @@ export class BrowserDialogHandler implements IDialogHandler { const detailToCopy = detailString(false); - const { choice } = await this.show(Severity.Info, this.productService.nameLong, [nls.localize('copy', "Copy"), nls.localize('ok', "OK")], { detail, cancelId: 1 }); + const { choice } = await this.show(Severity.Info, this.productService.nameLong, [localize('copy', "Copy"), localize('ok', "OK")], { detail, cancelId: 1 }); if (choice === 0) { this.clipboardService.writeText(detailToCopy); diff --git a/src/vs/workbench/browser/parts/editor/binaryDiffEditor.ts b/src/vs/workbench/browser/parts/editor/binaryDiffEditor.ts index 9d285cea910..1e3ebf38f8f 100644 --- a/src/vs/workbench/browser/parts/editor/binaryDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/binaryDiffEditor.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { BINARY_DIFF_EDITOR_ID } from 'vs/workbench/common/editor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -33,7 +33,7 @@ export class BinaryResourceDiffEditor extends SideBySideEditor { const secondary = this.secondaryEditorPane; if (primary instanceof BaseBinaryResourceEditor && secondary instanceof BaseBinaryResourceEditor) { - return nls.localize('metadataDiff', "{0} ↔ {1}", secondary.getMetadata(), primary.getMetadata()); + return localize('metadataDiff', "{0} ↔ {1}", secondary.getMetadata(), primary.getMetadata()); } return undefined; diff --git a/src/vs/workbench/browser/parts/editor/binaryEditor.ts b/src/vs/workbench/browser/parts/editor/binaryEditor.ts index 0883f8f85ce..5129069e370 100644 --- a/src/vs/workbench/browser/parts/editor/binaryEditor.ts +++ b/src/vs/workbench/browser/parts/editor/binaryEditor.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/binaryeditor'; -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { Emitter } from 'vs/base/common/event'; import { EditorInput, EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; @@ -58,7 +58,7 @@ export abstract class BaseBinaryResourceEditor extends EditorPane { } getTitle(): string { - return this.input ? this.input.getName() : nls.localize('binaryEditor', "Binary Viewer"); + return this.input ? this.input.getName() : localize('binaryEditor', "Binary Viewer"); } protected createEditor(parent: HTMLElement): void { @@ -217,7 +217,7 @@ class FileTooLargeFileView { clearNode(container); const label = document.createElement('span'); - label.textContent = nls.localize('nativeFileTooLargeError', "The file is not displayed in the editor because it is too large ({0}).", size); + label.textContent = localize('nativeFileTooLargeError', "The file is not displayed in the editor because it is too large ({0}).", size); container.appendChild(label); scrollbar.scanDomNode(); @@ -240,12 +240,12 @@ class FileSeemsBinaryFileView { const disposables = new DisposableStore(); const label = document.createElement('p'); - label.textContent = nls.localize('nativeBinaryError', "The file is not displayed in the editor because it is either binary or uses an unsupported text encoding."); + label.textContent = localize('nativeBinaryError', "The file is not displayed in the editor because it is either binary or uses an unsupported text encoding."); container.appendChild(label); const link = append(label, $('a.embedded-link')); link.setAttribute('role', 'button'); - link.textContent = nls.localize('openAsText', "Do you want to open it anyway?"); + link.textContent = localize('openAsText', "Do you want to open it anyway?"); disposables.add(addDisposableListener(link, EventType.CLICK, () => delegate.openInternalClb(descriptor.resource))); diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 003527d67e0..dc5a50fa00d 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Registry } from 'vs/platform/registry/common/platform'; -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; import { EditorInput, IEditorInputFactory, SideBySideEditorInput, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, TextCompareEditorActiveContext, ActiveEditorPinnedContext, EditorGroupEditorsCountContext, ActiveEditorStickyContext, ActiveEditorAvailableEditorIdsContext, MultipleEditorGroupsContext, ActiveEditorDirtyContext } from 'vs/workbench/common/editor'; @@ -35,7 +35,12 @@ import { NewEditorGroupAboveAction, NewEditorGroupBelowAction, SplitEditorOrthogonalAction, CloseEditorInAllGroupsAction, NavigateToLastEditLocationAction, ToggleGroupSizesAction, ShowAllEditorsByMostRecentlyUsedAction, QuickAccessPreviousRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction, QuickAccessLeastRecentlyUsedEditorAction, QuickAccessLeastRecentlyUsedEditorInGroupAction, ReopenResourcesAction, ToggleEditorTypeAction, DuplicateGroupDownAction, DuplicateGroupLeftAction, DuplicateGroupRightAction, DuplicateGroupUpAction } from 'vs/workbench/browser/parts/editor/editorActions'; -import * as editorCommands from 'vs/workbench/browser/parts/editor/editorCommands'; +import { + CLOSE_EDITORS_AND_GROUP_COMMAND_ID, CLOSE_EDITORS_IN_GROUP_COMMAND_ID, CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, CLOSE_EDITOR_COMMAND_ID, CLOSE_EDITOR_GROUP_COMMAND_ID, + CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, CLOSE_PINNED_EDITOR_COMMAND_ID, CLOSE_SAVED_EDITORS_COMMAND_ID, GOTO_NEXT_CHANGE, GOTO_PREVIOUS_CHANGE, KEEP_EDITOR_COMMAND_ID, + PIN_EDITOR_COMMAND_ID, SHOW_EDITORS_IN_GROUP, SPLIT_EDITOR_DOWN, SPLIT_EDITOR_LEFT, SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE, + TOGGLE_DIFF_SIDE_BY_SIDE, TOGGLE_KEEP_EDITORS_COMMAND_ID, UNPIN_EDITOR_COMMAND_ID, setup +} from 'vs/workbench/browser/parts/editor/editorCommands'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { inQuickPickContext, getQuickNavigateHandler } from 'vs/workbench/browser/quickaccess'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -57,13 +62,14 @@ import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { FileAccess } from 'vs/base/common/network'; import { Codicon } from 'vs/base/common/codicons'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; +import { UntitledHintContribution } from 'vs/workbench/browser/parts/editor/untitledHint'; // Register String Editor Registry.as(EditorExtensions.Editors).registerEditor( EditorDescriptor.create( TextResourceEditor, TextResourceEditor.ID, - nls.localize('textEditor', "Text Editor"), + localize('textEditor', "Text Editor"), ), [ new SyncDescriptor(UntitledTextEditorInput), @@ -76,7 +82,7 @@ Registry.as(EditorExtensions.Editors).registerEditor( EditorDescriptor.create( TextDiffEditor, TextDiffEditor.ID, - nls.localize('textDiffEditor', "Text Diff Editor") + localize('textDiffEditor', "Text Diff Editor") ), [ new SyncDescriptor(DiffEditorInput) @@ -88,7 +94,7 @@ Registry.as(EditorExtensions.Editors).registerEditor( EditorDescriptor.create( BinaryResourceDiffEditor, BinaryResourceDiffEditor.ID, - nls.localize('binaryDiffEditor', "Binary Diff Editor") + localize('binaryDiffEditor', "Binary Diff Editor") ), [ new SyncDescriptor(DiffEditorInput) @@ -99,7 +105,7 @@ Registry.as(EditorExtensions.Editors).registerEditor( EditorDescriptor.create( SideBySideEditor, SideBySideEditor.ID, - nls.localize('sideBySideEditor', "Side by Side Editor") + localize('sideBySideEditor', "Side by Side Editor") ), [ new SyncDescriptor(SideBySideEditorInput) @@ -276,6 +282,9 @@ Registry.as(WorkbenchExtensions.Workbench).regi // Register Editor Auto Save Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(EditorAutoSave, LifecyclePhase.Ready); +// Register Untitled Hint +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(UntitledHintContribution, LifecyclePhase.Ready); + // Register Status Actions const registry = Registry.as(ActionExtensions.WorkbenchActions); registry.registerWorkbenchAction(SyncActionDescriptor.from(ChangeModeAction, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_M) }), 'Change Language Mode'); @@ -291,24 +300,24 @@ quickAccessRegistry.registerQuickAccessProvider({ ctor: ActiveGroupEditorsByMostRecentlyUsedQuickAccess, prefix: ActiveGroupEditorsByMostRecentlyUsedQuickAccess.PREFIX, contextKey: editorPickerContextKey, - placeholder: nls.localize('editorQuickAccessPlaceholder', "Type the name of an editor to open it."), - helpEntries: [{ description: nls.localize('activeGroupEditorsByMostRecentlyUsedQuickAccess', "Show Editors in Active Group by Most Recently Used"), needsEditor: false }] + placeholder: localize('editorQuickAccessPlaceholder', "Type the name of an editor to open it."), + helpEntries: [{ description: localize('activeGroupEditorsByMostRecentlyUsedQuickAccess', "Show Editors in Active Group by Most Recently Used"), needsEditor: false }] }); quickAccessRegistry.registerQuickAccessProvider({ ctor: AllEditorsByAppearanceQuickAccess, prefix: AllEditorsByAppearanceQuickAccess.PREFIX, contextKey: editorPickerContextKey, - placeholder: nls.localize('editorQuickAccessPlaceholder', "Type the name of an editor to open it."), - helpEntries: [{ description: nls.localize('allEditorsByAppearanceQuickAccess', "Show All Opened Editors By Appearance"), needsEditor: false }] + placeholder: localize('editorQuickAccessPlaceholder', "Type the name of an editor to open it."), + helpEntries: [{ description: localize('allEditorsByAppearanceQuickAccess', "Show All Opened Editors By Appearance"), needsEditor: false }] }); quickAccessRegistry.registerQuickAccessProvider({ ctor: AllEditorsByMostRecentlyUsedQuickAccess, prefix: AllEditorsByMostRecentlyUsedQuickAccess.PREFIX, contextKey: editorPickerContextKey, - placeholder: nls.localize('editorQuickAccessPlaceholder', "Type the name of an editor to open it."), - helpEntries: [{ description: nls.localize('allEditorsByMostRecentlyUsedQuickAccess', "Show All Opened Editors By Most Recently Used"), needsEditor: false }] + placeholder: localize('editorQuickAccessPlaceholder', "Type the name of an editor to open it."), + helpEntries: [{ description: localize('allEditorsByMostRecentlyUsedQuickAccess', "Show All Opened Editors By Most Recently Used"), needsEditor: false }] }); // Register Editor Actions @@ -326,7 +335,7 @@ registry.registerWorkbenchAction(SyncActionDescriptor.from(ReopenClosedEditorAct registry.registerWorkbenchAction(SyncActionDescriptor.from(ShowAllEditorsByAppearanceAction, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_P), mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Tab } }), 'View: Show All Editors By Appearance', CATEGORIES.View.value); registry.registerWorkbenchAction(SyncActionDescriptor.from(ShowAllEditorsByMostRecentlyUsedAction), 'View: Show All Editors By Most Recently Used', CATEGORIES.View.value); registry.registerWorkbenchAction(SyncActionDescriptor.from(ShowEditorsInActiveGroupByMostRecentlyUsedAction), 'View: Show Editors in Active Group By Most Recently Used', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(ClearRecentFilesAction), 'File: Clear Recently Opened', nls.localize('file', "File")); +registry.registerWorkbenchAction(SyncActionDescriptor.from(ClearRecentFilesAction), 'File: Clear Recently Opened', localize('file', "File")); registry.registerWorkbenchAction(SyncActionDescriptor.from(CloseAllEditorsAction, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_W) }), 'View: Close All Editors', CATEGORIES.View.value); registry.registerWorkbenchAction(SyncActionDescriptor.from(CloseAllEditorGroupsAction, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_W) }), 'View: Close All Editor Groups', CATEGORIES.View.value); registry.registerWorkbenchAction(SyncActionDescriptor.from(CloseLeftEditorsInGroupAction), 'View: Close Editors to the Left in Group', CATEGORIES.View.value); @@ -424,7 +433,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ }); // Editor Commands -editorCommands.setup(); +setup(); // Touch Bar if (isMacintosh) { @@ -442,34 +451,34 @@ if (isMacintosh) { } // Empty Editor Group Context Menu -MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: editorCommands.SPLIT_EDITOR_UP, title: nls.localize('splitUp', "Split Up") }, group: '2_split', order: 10 }); -MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: editorCommands.SPLIT_EDITOR_DOWN, title: nls.localize('splitDown', "Split Down") }, group: '2_split', order: 20 }); -MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: editorCommands.SPLIT_EDITOR_LEFT, title: nls.localize('splitLeft', "Split Left") }, group: '2_split', order: 30 }); -MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: editorCommands.SPLIT_EDITOR_RIGHT, title: nls.localize('splitRight', "Split Right") }, group: '2_split', order: 40 }); -MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: editorCommands.CLOSE_EDITOR_GROUP_COMMAND_ID, title: nls.localize('close', "Close") }, group: '3_close', order: 10, when: ContextKeyExpr.has('multipleEditorGroups') }); +MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: SPLIT_EDITOR_UP, title: localize('splitUp', "Split Up") }, group: '2_split', order: 10 }); +MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: SPLIT_EDITOR_DOWN, title: localize('splitDown', "Split Down") }, group: '2_split', order: 20 }); +MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: SPLIT_EDITOR_LEFT, title: localize('splitLeft', "Split Left") }, group: '2_split', order: 30 }); +MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: SPLIT_EDITOR_RIGHT, title: localize('splitRight', "Split Right") }, group: '2_split', order: 40 }); +MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: CLOSE_EDITOR_GROUP_COMMAND_ID, title: localize('close', "Close") }, group: '3_close', order: 10, when: ContextKeyExpr.has('multipleEditorGroups') }); // Editor Title Context Menu -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_EDITOR_COMMAND_ID, title: nls.localize('close', "Close") }, group: '1_close', order: 10 }); -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeOthers', "Close Others"), precondition: EditorGroupEditorsCountContext.notEqualsTo('1') }, group: '1_close', order: 20 }); -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, title: nls.localize('closeRight', "Close to the Right"), precondition: EditorGroupEditorsCountContext.notEqualsTo('1') }, group: '1_close', order: 30, when: ContextKeyExpr.has('config.workbench.editor.showTabs') }); -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeAllSaved', "Close Saved") }, group: '1_close', order: 40 }); -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All") }, group: '1_close', order: 50 }); +MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: CLOSE_EDITOR_COMMAND_ID, title: localize('close', "Close") }, group: '1_close', order: 10 }); +MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, title: localize('closeOthers', "Close Others"), precondition: EditorGroupEditorsCountContext.notEqualsTo('1') }, group: '1_close', order: 20 }); +MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, title: localize('closeRight', "Close to the Right"), precondition: EditorGroupEditorsCountContext.notEqualsTo('1') }, group: '1_close', order: 30, when: ContextKeyExpr.has('config.workbench.editor.showTabs') }); +MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: CLOSE_SAVED_EDITORS_COMMAND_ID, title: localize('closeAllSaved', "Close Saved") }, group: '1_close', order: 40 }); +MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: localize('closeAll', "Close All") }, group: '1_close', order: 50 }); MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: ReopenResourcesAction.ID, title: ReopenResourcesAction.LABEL }, group: '1_open', order: 10, when: ActiveEditorAvailableEditorIdsContext }); -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.KEEP_EDITOR_COMMAND_ID, title: nls.localize('keepOpen', "Keep Open"), precondition: ActiveEditorPinnedContext.toNegated() }, group: '3_preview', order: 10, when: ContextKeyExpr.has('config.workbench.editor.enablePreview') }); -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.PIN_EDITOR_COMMAND_ID, title: nls.localize('pin', "Pin") }, group: '3_preview', order: 20, when: ActiveEditorStickyContext.toNegated() }); -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.UNPIN_EDITOR_COMMAND_ID, title: nls.localize('unpin', "Unpin") }, group: '3_preview', order: 20, when: ActiveEditorStickyContext }); -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.SPLIT_EDITOR_UP, title: nls.localize('splitUp', "Split Up") }, group: '5_split', order: 10 }); -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.SPLIT_EDITOR_DOWN, title: nls.localize('splitDown', "Split Down") }, group: '5_split', order: 20 }); -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.SPLIT_EDITOR_LEFT, title: nls.localize('splitLeft', "Split Left") }, group: '5_split', order: 30 }); -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.SPLIT_EDITOR_RIGHT, title: nls.localize('splitRight', "Split Right") }, group: '5_split', order: 40 }); +MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: KEEP_EDITOR_COMMAND_ID, title: localize('keepOpen', "Keep Open"), precondition: ActiveEditorPinnedContext.toNegated() }, group: '3_preview', order: 10, when: ContextKeyExpr.has('config.workbench.editor.enablePreview') }); +MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: PIN_EDITOR_COMMAND_ID, title: localize('pin', "Pin") }, group: '3_preview', order: 20, when: ActiveEditorStickyContext.toNegated() }); +MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: UNPIN_EDITOR_COMMAND_ID, title: localize('unpin', "Unpin") }, group: '3_preview', order: 20, when: ActiveEditorStickyContext }); +MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: SPLIT_EDITOR_UP, title: localize('splitUp', "Split Up") }, group: '5_split', order: 10 }); +MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: SPLIT_EDITOR_DOWN, title: localize('splitDown', "Split Down") }, group: '5_split', order: 20 }); +MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: SPLIT_EDITOR_LEFT, title: localize('splitLeft', "Split Left") }, group: '5_split', order: 30 }); +MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: SPLIT_EDITOR_RIGHT, title: 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.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 }); -MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.TOGGLE_KEEP_EDITORS_COMMAND_ID, title: nls.localize('toggleKeepEditors', "Keep Editors Open"), toggled: ContextKeyExpr.not('config.workbench.editor.enablePreview') }, group: '7_settings', order: 10 }); -MenuRegistry.appendMenuItem(MenuId.EditorTitle, { submenu: MenuId.EditorTitleRun, title: { value: nls.localize('run', "Run"), original: 'Run', }, icon: Codicon.run, group: 'navigation', order: -1 }); +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_DIFF_SIDE_BY_SIDE, title: 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: SHOW_EDITORS_IN_GROUP, title: localize('showOpenedEditors', "Show Opened Editors") }, group: '3_open', order: 10 }); +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: localize('closeAll', "Close All") }, group: '5_close', order: 10 }); +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: CLOSE_SAVED_EDITORS_COMMAND_ID, title: localize('closeAllSaved', "Close Saved") }, group: '5_close', order: 20 }); +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_KEEP_EDITORS_COMMAND_ID, title: localize('toggleKeepEditors', "Keep Editors Open"), toggled: ContextKeyExpr.not('config.workbench.editor.enablePreview') }, group: '7_settings', order: 10 }); +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { submenu: MenuId.EditorTitleRun, title: { value: localize('run', "Run"), original: 'Run', }, icon: Codicon.run, group: 'navigation', order: -1 }); interface IEditorToolItem { id: string; title: string; icon?: { dark?: URI; light?: URI; } | ThemeIcon; } @@ -501,14 +510,14 @@ function appendEditorToolItem(primary: IEditorToolItem, when: ContextKeyExpressi appendEditorToolItem( { id: SplitEditorAction.ID, - title: nls.localize('splitEditorRight', "Split Editor Right"), + title: localize('splitEditorRight', "Split Editor Right"), icon: Codicon.splitHorizontal }, ContextKeyExpr.not('splitEditorsVertically'), 100000, // towards the end { - id: editorCommands.SPLIT_EDITOR_DOWN, - title: nls.localize('splitEditorDown', "Split Editor Down"), + id: SPLIT_EDITOR_DOWN, + title: localize('splitEditorDown', "Split Editor Down"), icon: Codicon.splitVertical } ); @@ -516,14 +525,14 @@ appendEditorToolItem( appendEditorToolItem( { id: SplitEditorAction.ID, - title: nls.localize('splitEditorDown', "Split Editor Down"), + title: localize('splitEditorDown', "Split Editor Down"), icon: Codicon.splitVertical }, ContextKeyExpr.has('splitEditorsVertically'), 100000, // towards the end { - id: editorCommands.SPLIT_EDITOR_RIGHT, - title: nls.localize('splitEditorRight', "Split Editor Right"), + id: SPLIT_EDITOR_RIGHT, + title: localize('splitEditorRight', "Split Editor Right"), icon: Codicon.splitHorizontal } ); @@ -531,15 +540,15 @@ appendEditorToolItem( // Editor Title Menu: Close (tabs disabled, normal editor) appendEditorToolItem( { - id: editorCommands.CLOSE_EDITOR_COMMAND_ID, - title: nls.localize('close', "Close"), + id: CLOSE_EDITOR_COMMAND_ID, + title: localize('close', "Close"), icon: Codicon.close }, ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ActiveEditorDirtyContext.toNegated(), ActiveEditorStickyContext.toNegated()), 1000000, // towards the far end { - id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, - title: nls.localize('closeAll', "Close All"), + id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID, + title: localize('closeAll', "Close All"), icon: Codicon.closeAll } ); @@ -547,15 +556,15 @@ appendEditorToolItem( // Editor Title Menu: Close (tabs disabled, dirty editor) appendEditorToolItem( { - id: editorCommands.CLOSE_EDITOR_COMMAND_ID, - title: nls.localize('close', "Close"), + id: CLOSE_EDITOR_COMMAND_ID, + title: localize('close', "Close"), icon: Codicon.closeDirty }, ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ActiveEditorDirtyContext, ActiveEditorStickyContext.toNegated()), 1000000, // towards the far end { - id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, - title: nls.localize('closeAll', "Close All"), + id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID, + title: localize('closeAll', "Close All"), icon: Codicon.closeAll } ); @@ -563,15 +572,15 @@ appendEditorToolItem( // Editor Title Menu: Close (tabs disabled, sticky editor) appendEditorToolItem( { - id: editorCommands.UNPIN_EDITOR_COMMAND_ID, - title: nls.localize('unpin', "Unpin"), + id: UNPIN_EDITOR_COMMAND_ID, + title: localize('unpin', "Unpin"), icon: Codicon.pinned }, ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ActiveEditorDirtyContext.toNegated(), ActiveEditorStickyContext), 1000000, // towards the far end { - id: editorCommands.CLOSE_EDITOR_COMMAND_ID, - title: nls.localize('close', "Close"), + id: CLOSE_EDITOR_COMMAND_ID, + title: localize('close', "Close"), icon: Codicon.close } ); @@ -579,29 +588,29 @@ appendEditorToolItem( // Editor Title Menu: Close (tabs disabled, dirty & sticky editor) appendEditorToolItem( { - id: editorCommands.UNPIN_EDITOR_COMMAND_ID, - title: nls.localize('unpin', "Unpin"), + id: UNPIN_EDITOR_COMMAND_ID, + title: localize('unpin', "Unpin"), icon: Codicon.pinnedDirty }, ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ActiveEditorDirtyContext, ActiveEditorStickyContext), 1000000, // towards the far end { - id: editorCommands.CLOSE_EDITOR_COMMAND_ID, - title: nls.localize('close', "Close"), + id: CLOSE_EDITOR_COMMAND_ID, + title: localize('close', "Close"), icon: Codicon.close } ); -const previousChangeIcon = registerIcon('diff-editor-previous-change', Codicon.arrowUp, nls.localize('previousChangeIcon', 'Icon for the previous change action in the diff editor.')); -const nextChangeIcon = registerIcon('diff-editor-next-change', Codicon.arrowDown, nls.localize('nextChangeIcon', 'Icon for the next change action in the diff editor.')); -const toggleWhitespace = registerIcon('diff-editor-toggle-whitespace', Codicon.whitespace, nls.localize('toggleWhitespace', 'Icon for the toggle whitespace action in the diff editor.')); +const previousChangeIcon = registerIcon('diff-editor-previous-change', Codicon.arrowUp, localize('previousChangeIcon', 'Icon for the previous change action in the diff editor.')); +const nextChangeIcon = registerIcon('diff-editor-next-change', Codicon.arrowDown, localize('nextChangeIcon', 'Icon for the next change action in the diff editor.')); +const toggleWhitespace = registerIcon('diff-editor-toggle-whitespace', Codicon.whitespace, localize('toggleWhitespace', 'Icon for the toggle whitespace action in the diff editor.')); // Diff Editor Title Menu: Previous Change appendEditorToolItem( { - id: editorCommands.GOTO_PREVIOUS_CHANGE, - title: nls.localize('navigate.prev.label', "Previous Change"), + id: GOTO_PREVIOUS_CHANGE, + title: localize('navigate.prev.label', "Previous Change"), icon: previousChangeIcon }, TextCompareEditorActiveContext, @@ -611,8 +620,8 @@ appendEditorToolItem( // Diff Editor Title Menu: Next Change appendEditorToolItem( { - id: editorCommands.GOTO_NEXT_CHANGE, - title: nls.localize('navigate.next.label', "Next Change"), + id: GOTO_NEXT_CHANGE, + title: localize('navigate.next.label', "Next Change"), icon: nextChangeIcon }, TextCompareEditorActiveContext, @@ -622,8 +631,8 @@ appendEditorToolItem( // Diff Editor Title Menu: Toggle Ignore Trim Whitespace (Enabled) appendEditorToolItem( { - id: editorCommands.TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE, - title: nls.localize('ignoreTrimWhitespace.label', "Ignore Leading/Trailing Whitespace Differences"), + id: TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE, + title: localize('ignoreTrimWhitespace.label', "Ignore Leading/Trailing Whitespace Differences"), icon: toggleWhitespace }, ContextKeyExpr.and(TextCompareEditorActiveContext, ContextKeyExpr.notEquals('config.diffEditor.ignoreTrimWhitespace', true)), @@ -633,8 +642,8 @@ appendEditorToolItem( // Diff Editor Title Menu: Toggle Ignore Trim Whitespace (Disabled) appendEditorToolItem( { - id: editorCommands.TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE, - title: nls.localize('showTrimWhitespace.label', "Show Leading/Trailing Whitespace Differences"), + id: TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE, + title: localize('showTrimWhitespace.label', "Show Leading/Trailing Whitespace Differences"), icon: ThemeIcon.modify(toggleWhitespace, 'disabled') }, ContextKeyExpr.and(TextCompareEditorActiveContext, ContextKeyExpr.notEquals('config.diffEditor.ignoreTrimWhitespace', false)), @@ -642,23 +651,23 @@ appendEditorToolItem( ); // Editor Commands for Command Palette -MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.KEEP_EDITOR_COMMAND_ID, title: { value: nls.localize('keepEditor', "Keep Editor"), original: 'Keep Editor' }, category: CATEGORIES.View }, when: ContextKeyExpr.has('config.workbench.editor.enablePreview') }); -MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.PIN_EDITOR_COMMAND_ID, title: { value: nls.localize('pinEditor', "Pin Editor"), original: 'Pin Editor' }, category: CATEGORIES.View } }); -MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.UNPIN_EDITOR_COMMAND_ID, title: { value: nls.localize('unpinEditor', "Unpin Editor"), original: 'Unpin Editor' }, category: CATEGORIES.View } }); -MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_EDITOR_COMMAND_ID, title: { value: nls.localize('closeEditor', "Close Editor"), original: 'Close Editor' }, category: CATEGORIES.View } }); -MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_PINNED_EDITOR_COMMAND_ID, title: { value: nls.localize('closePinnedEditor', "Close Pinned Editor"), original: 'Close Pinned Editor' }, category: CATEGORIES.View } }); -MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: { value: nls.localize('closeEditorsInGroup', "Close All Editors in Group"), original: 'Close All Editors in Group' }, category: CATEGORIES.View } }); -MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: { value: nls.localize('closeSavedEditors', "Close Saved Editors in Group"), original: 'Close Saved Editors in Group' }, category: CATEGORIES.View } }); -MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, title: { value: nls.localize('closeOtherEditors', "Close Other Editors in Group"), original: 'Close Other Editors in Group' }, category: CATEGORIES.View } }); -MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, title: { value: nls.localize('closeRightEditors', "Close Editors to the Right in Group"), original: 'Close Editors to the Right in Group' }, category: CATEGORIES.View } }); -MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_EDITORS_AND_GROUP_COMMAND_ID, title: { value: nls.localize('closeEditorGroup', "Close Editor Group"), original: 'Close Editor Group' }, category: CATEGORIES.View }, when: MultipleEditorGroupsContext }); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: KEEP_EDITOR_COMMAND_ID, title: { value: localize('keepEditor', "Keep Editor"), original: 'Keep Editor' }, category: CATEGORIES.View }, when: ContextKeyExpr.has('config.workbench.editor.enablePreview') }); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: PIN_EDITOR_COMMAND_ID, title: { value: localize('pinEditor', "Pin Editor"), original: 'Pin Editor' }, category: CATEGORIES.View } }); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: UNPIN_EDITOR_COMMAND_ID, title: { value: localize('unpinEditor', "Unpin Editor"), original: 'Unpin Editor' }, category: CATEGORIES.View } }); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: CLOSE_EDITOR_COMMAND_ID, title: { value: localize('closeEditor', "Close Editor"), original: 'Close Editor' }, category: CATEGORIES.View } }); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: CLOSE_PINNED_EDITOR_COMMAND_ID, title: { value: localize('closePinnedEditor', "Close Pinned Editor"), original: 'Close Pinned Editor' }, category: CATEGORIES.View } }); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: { value: localize('closeEditorsInGroup', "Close All Editors in Group"), original: 'Close All Editors in Group' }, category: CATEGORIES.View } }); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: CLOSE_SAVED_EDITORS_COMMAND_ID, title: { value: localize('closeSavedEditors', "Close Saved Editors in Group"), original: 'Close Saved Editors in Group' }, category: CATEGORIES.View } }); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, title: { value: localize('closeOtherEditors', "Close Other Editors in Group"), original: 'Close Other Editors in Group' }, category: CATEGORIES.View } }); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, title: { value: localize('closeRightEditors', "Close Editors to the Right in Group"), original: 'Close Editors to the Right in Group' }, category: CATEGORIES.View } }); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: CLOSE_EDITORS_AND_GROUP_COMMAND_ID, title: { value: localize('closeEditorGroup', "Close Editor Group"), original: 'Close Editor Group' }, category: CATEGORIES.View }, when: MultipleEditorGroupsContext }); // File menu MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, { group: '1_editor', command: { id: ReopenClosedEditorAction.ID, - title: nls.localize({ key: 'miReopenClosedEditor', comment: ['&& denotes a mnemonic'] }, "&&Reopen Closed Editor"), + title: localize({ key: 'miReopenClosedEditor', comment: ['&& denotes a mnemonic'] }, "&&Reopen Closed Editor"), precondition: ContextKeyExpr.has('canReopenClosedEditor') }, order: 1 @@ -668,7 +677,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, { group: 'z_clear', command: { id: ClearRecentFilesAction.ID, - title: nls.localize({ key: 'miClearRecentOpen', comment: ['&& denotes a mnemonic'] }, "&&Clear Recently Opened") + title: localize({ key: 'miClearRecentOpen', comment: ['&& denotes a mnemonic'] }, "&&Clear Recently Opened") }, order: 1 }); @@ -676,7 +685,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, { // Layout menu MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { group: '2_appearance', - title: nls.localize({ key: 'miEditorLayout', comment: ['&& denotes a mnemonic'] }, "Editor &&Layout"), + title: localize({ key: 'miEditorLayout', comment: ['&& denotes a mnemonic'] }, "Editor &&Layout"), submenu: MenuId.MenubarLayoutMenu, order: 2 }); @@ -684,8 +693,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { group: '1_split', command: { - id: editorCommands.SPLIT_EDITOR_UP, - title: nls.localize({ key: 'miSplitEditorUp', comment: ['&& denotes a mnemonic'] }, "Split &&Up") + id: SPLIT_EDITOR_UP, + title: localize({ key: 'miSplitEditorUp', comment: ['&& denotes a mnemonic'] }, "Split &&Up") }, order: 1 }); @@ -693,8 +702,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { group: '1_split', command: { - id: editorCommands.SPLIT_EDITOR_DOWN, - title: nls.localize({ key: 'miSplitEditorDown', comment: ['&& denotes a mnemonic'] }, "Split &&Down") + id: SPLIT_EDITOR_DOWN, + title: localize({ key: 'miSplitEditorDown', comment: ['&& denotes a mnemonic'] }, "Split &&Down") }, order: 2 }); @@ -702,8 +711,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { group: '1_split', command: { - id: editorCommands.SPLIT_EDITOR_LEFT, - title: nls.localize({ key: 'miSplitEditorLeft', comment: ['&& denotes a mnemonic'] }, "Split &&Left") + id: SPLIT_EDITOR_LEFT, + title: localize({ key: 'miSplitEditorLeft', comment: ['&& denotes a mnemonic'] }, "Split &&Left") }, order: 3 }); @@ -711,8 +720,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { group: '1_split', command: { - id: editorCommands.SPLIT_EDITOR_RIGHT, - title: nls.localize({ key: 'miSplitEditorRight', comment: ['&& denotes a mnemonic'] }, "Split &&Right") + id: SPLIT_EDITOR_RIGHT, + title: localize({ key: 'miSplitEditorRight', comment: ['&& denotes a mnemonic'] }, "Split &&Right") }, order: 4 }); @@ -721,7 +730,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { group: '2_layouts', command: { id: EditorLayoutSingleAction.ID, - title: nls.localize({ key: 'miSingleColumnEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Single") + title: localize({ key: 'miSingleColumnEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Single") }, order: 1 }); @@ -730,7 +739,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { group: '2_layouts', command: { id: EditorLayoutTwoColumnsAction.ID, - title: nls.localize({ key: 'miTwoColumnsEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Two Columns") + title: localize({ key: 'miTwoColumnsEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Two Columns") }, order: 3 }); @@ -739,7 +748,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { group: '2_layouts', command: { id: EditorLayoutThreeColumnsAction.ID, - title: nls.localize({ key: 'miThreeColumnsEditorLayout', comment: ['&& denotes a mnemonic'] }, "T&&hree Columns") + title: localize({ key: 'miThreeColumnsEditorLayout', comment: ['&& denotes a mnemonic'] }, "T&&hree Columns") }, order: 4 }); @@ -748,7 +757,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { group: '2_layouts', command: { id: EditorLayoutTwoRowsAction.ID, - title: nls.localize({ key: 'miTwoRowsEditorLayout', comment: ['&& denotes a mnemonic'] }, "T&&wo Rows") + title: localize({ key: 'miTwoRowsEditorLayout', comment: ['&& denotes a mnemonic'] }, "T&&wo Rows") }, order: 5 }); @@ -757,7 +766,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { group: '2_layouts', command: { id: EditorLayoutThreeRowsAction.ID, - title: nls.localize({ key: 'miThreeRowsEditorLayout', comment: ['&& denotes a mnemonic'] }, "Three &&Rows") + title: localize({ key: 'miThreeRowsEditorLayout', comment: ['&& denotes a mnemonic'] }, "Three &&Rows") }, order: 6 }); @@ -766,7 +775,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { group: '2_layouts', command: { id: EditorLayoutTwoByTwoGridAction.ID, - title: nls.localize({ key: 'miTwoByTwoGridEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Grid (2x2)") + title: localize({ key: 'miTwoByTwoGridEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Grid (2x2)") }, order: 7 }); @@ -775,7 +784,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { group: '2_layouts', command: { id: EditorLayoutTwoRowsRightAction.ID, - title: nls.localize({ key: 'miTwoRowsRightEditorLayout', comment: ['&& denotes a mnemonic'] }, "Two R&&ows Right") + title: localize({ key: 'miTwoRowsRightEditorLayout', comment: ['&& denotes a mnemonic'] }, "Two R&&ows Right") }, order: 8 }); @@ -784,7 +793,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { group: '2_layouts', command: { id: EditorLayoutTwoColumnsBottomAction.ID, - title: nls.localize({ key: 'miTwoColumnsBottomEditorLayout', comment: ['&& denotes a mnemonic'] }, "Two &&Columns Bottom") + title: localize({ key: 'miTwoColumnsBottomEditorLayout', comment: ['&& denotes a mnemonic'] }, "Two &&Columns Bottom") }, order: 9 }); @@ -796,7 +805,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { group: '1_history_nav', command: { id: 'workbench.action.navigateBack', - title: nls.localize({ key: 'miBack', comment: ['&& denotes a mnemonic'] }, "&&Back"), + title: localize({ key: 'miBack', comment: ['&& denotes a mnemonic'] }, "&&Back"), precondition: ContextKeyExpr.has('canNavigateBack') }, order: 1 @@ -806,7 +815,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { group: '1_history_nav', command: { id: 'workbench.action.navigateForward', - title: nls.localize({ key: 'miForward', comment: ['&& denotes a mnemonic'] }, "&&Forward"), + title: localize({ key: 'miForward', comment: ['&& denotes a mnemonic'] }, "&&Forward"), precondition: ContextKeyExpr.has('canNavigateForward') }, order: 2 @@ -816,7 +825,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { group: '1_history_nav', command: { id: 'workbench.action.navigateToLastEditLocation', - title: nls.localize({ key: 'miLastEditLocation', comment: ['&& denotes a mnemonic'] }, "&&Last Edit Location"), + title: localize({ key: 'miLastEditLocation', comment: ['&& denotes a mnemonic'] }, "&&Last Edit Location"), precondition: ContextKeyExpr.has('canNavigateToLastEditLocation') }, order: 3 @@ -827,7 +836,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSwitchEditorMenu, { group: '1_any', command: { id: 'workbench.action.nextEditor', - title: nls.localize({ key: 'miNextEditor', comment: ['&& denotes a mnemonic'] }, "&&Next Editor") + title: localize({ key: 'miNextEditor', comment: ['&& denotes a mnemonic'] }, "&&Next Editor") }, order: 1 }); @@ -836,7 +845,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSwitchEditorMenu, { group: '1_any', command: { id: 'workbench.action.previousEditor', - title: nls.localize({ key: 'miPreviousEditor', comment: ['&& denotes a mnemonic'] }, "&&Previous Editor") + title: localize({ key: 'miPreviousEditor', comment: ['&& denotes a mnemonic'] }, "&&Previous Editor") }, order: 2 }); @@ -845,7 +854,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSwitchEditorMenu, { group: '2_any_used', command: { id: 'workbench.action.openNextRecentlyUsedEditor', - title: nls.localize({ key: 'miNextRecentlyUsedEditor', comment: ['&& denotes a mnemonic'] }, "&&Next Used Editor") + title: localize({ key: 'miNextRecentlyUsedEditor', comment: ['&& denotes a mnemonic'] }, "&&Next Used Editor") }, order: 1 }); @@ -854,7 +863,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSwitchEditorMenu, { group: '2_any_used', command: { id: 'workbench.action.openPreviousRecentlyUsedEditor', - title: nls.localize({ key: 'miPreviousRecentlyUsedEditor', comment: ['&& denotes a mnemonic'] }, "&&Previous Used Editor") + title: localize({ key: 'miPreviousRecentlyUsedEditor', comment: ['&& denotes a mnemonic'] }, "&&Previous Used Editor") }, order: 2 }); @@ -863,7 +872,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSwitchEditorMenu, { group: '3_group', command: { id: 'workbench.action.nextEditorInGroup', - title: nls.localize({ key: 'miNextEditorInGroup', comment: ['&& denotes a mnemonic'] }, "&&Next Editor in Group") + title: localize({ key: 'miNextEditorInGroup', comment: ['&& denotes a mnemonic'] }, "&&Next Editor in Group") }, order: 1 }); @@ -872,7 +881,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSwitchEditorMenu, { group: '3_group', command: { id: 'workbench.action.previousEditorInGroup', - title: nls.localize({ key: 'miPreviousEditorInGroup', comment: ['&& denotes a mnemonic'] }, "&&Previous Editor in Group") + title: localize({ key: 'miPreviousEditorInGroup', comment: ['&& denotes a mnemonic'] }, "&&Previous Editor in Group") }, order: 2 }); @@ -881,7 +890,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSwitchEditorMenu, { group: '4_group_used', command: { id: 'workbench.action.openNextRecentlyUsedEditorInGroup', - title: nls.localize({ key: 'miNextUsedEditorInGroup', comment: ['&& denotes a mnemonic'] }, "&&Next Used Editor in Group") + title: localize({ key: 'miNextUsedEditorInGroup', comment: ['&& denotes a mnemonic'] }, "&&Next Used Editor in Group") }, order: 1 }); @@ -890,14 +899,14 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSwitchEditorMenu, { group: '4_group_used', command: { id: 'workbench.action.openPreviousRecentlyUsedEditorInGroup', - title: nls.localize({ key: 'miPreviousUsedEditorInGroup', comment: ['&& denotes a mnemonic'] }, "&&Previous Used Editor in Group") + title: localize({ key: 'miPreviousUsedEditorInGroup', comment: ['&& denotes a mnemonic'] }, "&&Previous Used Editor in Group") }, order: 2 }); MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { group: '2_editor_nav', - title: nls.localize({ key: 'miSwitchEditor', comment: ['&& denotes a mnemonic'] }, "Switch &&Editor"), + title: localize({ key: 'miSwitchEditor', comment: ['&& denotes a mnemonic'] }, "Switch &&Editor"), submenu: MenuId.MenubarSwitchEditorMenu, order: 1 }); @@ -907,7 +916,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, { group: '1_focus_index', command: { id: 'workbench.action.focusFirstEditorGroup', - title: nls.localize({ key: 'miFocusFirstGroup', comment: ['&& denotes a mnemonic'] }, "Group &&1") + title: localize({ key: 'miFocusFirstGroup', comment: ['&& denotes a mnemonic'] }, "Group &&1") }, order: 1 }); @@ -916,7 +925,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, { group: '1_focus_index', command: { id: 'workbench.action.focusSecondEditorGroup', - title: nls.localize({ key: 'miFocusSecondGroup', comment: ['&& denotes a mnemonic'] }, "Group &&2") + title: localize({ key: 'miFocusSecondGroup', comment: ['&& denotes a mnemonic'] }, "Group &&2") }, order: 2 }); @@ -925,7 +934,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, { group: '1_focus_index', command: { id: 'workbench.action.focusThirdEditorGroup', - title: nls.localize({ key: 'miFocusThirdGroup', comment: ['&& denotes a mnemonic'] }, "Group &&3"), + title: localize({ key: 'miFocusThirdGroup', comment: ['&& denotes a mnemonic'] }, "Group &&3"), precondition: ContextKeyExpr.has('multipleEditorGroups') }, order: 3 @@ -935,7 +944,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, { group: '1_focus_index', command: { id: 'workbench.action.focusFourthEditorGroup', - title: nls.localize({ key: 'miFocusFourthGroup', comment: ['&& denotes a mnemonic'] }, "Group &&4"), + title: localize({ key: 'miFocusFourthGroup', comment: ['&& denotes a mnemonic'] }, "Group &&4"), precondition: ContextKeyExpr.has('multipleEditorGroups') }, order: 4 @@ -945,7 +954,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, { group: '1_focus_index', command: { id: 'workbench.action.focusFifthEditorGroup', - title: nls.localize({ key: 'miFocusFifthGroup', comment: ['&& denotes a mnemonic'] }, "Group &&5"), + title: localize({ key: 'miFocusFifthGroup', comment: ['&& denotes a mnemonic'] }, "Group &&5"), precondition: ContextKeyExpr.has('multipleEditorGroups') }, order: 5 @@ -955,7 +964,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, { group: '2_next_prev', command: { id: 'workbench.action.focusNextGroup', - title: nls.localize({ key: 'miNextGroup', comment: ['&& denotes a mnemonic'] }, "&&Next Group"), + title: localize({ key: 'miNextGroup', comment: ['&& denotes a mnemonic'] }, "&&Next Group"), precondition: ContextKeyExpr.has('multipleEditorGroups') }, order: 1 @@ -965,7 +974,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, { group: '2_next_prev', command: { id: 'workbench.action.focusPreviousGroup', - title: nls.localize({ key: 'miPreviousGroup', comment: ['&& denotes a mnemonic'] }, "&&Previous Group"), + title: localize({ key: 'miPreviousGroup', comment: ['&& denotes a mnemonic'] }, "&&Previous Group"), precondition: ContextKeyExpr.has('multipleEditorGroups') }, order: 2 @@ -975,7 +984,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, { group: '3_directional', command: { id: 'workbench.action.focusLeftGroup', - title: nls.localize({ key: 'miFocusLeftGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Left"), + title: localize({ key: 'miFocusLeftGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Left"), precondition: ContextKeyExpr.has('multipleEditorGroups') }, order: 1 @@ -985,7 +994,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, { group: '3_directional', command: { id: 'workbench.action.focusRightGroup', - title: nls.localize({ key: 'miFocusRightGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Right"), + title: localize({ key: 'miFocusRightGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Right"), precondition: ContextKeyExpr.has('multipleEditorGroups') }, order: 2 @@ -995,7 +1004,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, { group: '3_directional', command: { id: 'workbench.action.focusAboveGroup', - title: nls.localize({ key: 'miFocusAboveGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Above"), + title: localize({ key: 'miFocusAboveGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Above"), precondition: ContextKeyExpr.has('multipleEditorGroups') }, order: 3 @@ -1005,7 +1014,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, { group: '3_directional', command: { id: 'workbench.action.focusBelowGroup', - title: nls.localize({ key: 'miFocusBelowGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Below"), + title: localize({ key: 'miFocusBelowGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Below"), precondition: ContextKeyExpr.has('multipleEditorGroups') }, order: 4 @@ -1013,7 +1022,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, { MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { group: '2_editor_nav', - title: nls.localize({ key: 'miSwitchGroup', comment: ['&& denotes a mnemonic'] }, "Switch &&Group"), + title: localize({ key: 'miSwitchGroup', comment: ['&& denotes a mnemonic'] }, "Switch &&Group"), submenu: MenuId.MenubarSwitchGroupMenu, order: 2 }); diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index db48f417148..df7238d6ef2 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { IEditorInput, IEditorIdentifier, IEditorCommandsContext, CloseDirection, SaveReason, EditorsOrder, SideBySideEditorInput } from 'vs/workbench/common/editor'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; @@ -79,7 +79,7 @@ export class BaseSplitEditorAction extends Action { export class SplitEditorAction extends BaseSplitEditorAction { static readonly ID = 'workbench.action.splitEditor'; - static readonly LABEL = nls.localize('splitEditor', "Split Editor"); + static readonly LABEL = localize('splitEditor', "Split Editor"); constructor( id: string, @@ -94,7 +94,7 @@ export class SplitEditorAction extends BaseSplitEditorAction { export class SplitEditorOrthogonalAction extends BaseSplitEditorAction { static readonly ID = 'workbench.action.splitEditorOrthogonal'; - static readonly LABEL = nls.localize('splitEditorOrthogonal', "Split Editor Orthogonal"); + static readonly LABEL = localize('splitEditorOrthogonal', "Split Editor Orthogonal"); constructor( id: string, @@ -115,7 +115,7 @@ export class SplitEditorOrthogonalAction extends BaseSplitEditorAction { export class SplitEditorLeftAction extends ExecuteCommandAction { static readonly ID = SPLIT_EDITOR_LEFT; - static readonly LABEL = nls.localize('splitEditorGroupLeft', "Split Editor Left"); + static readonly LABEL = localize('splitEditorGroupLeft', "Split Editor Left"); constructor( id: string, @@ -129,7 +129,7 @@ export class SplitEditorLeftAction extends ExecuteCommandAction { export class SplitEditorRightAction extends ExecuteCommandAction { static readonly ID = SPLIT_EDITOR_RIGHT; - static readonly LABEL = nls.localize('splitEditorGroupRight', "Split Editor Right"); + static readonly LABEL = localize('splitEditorGroupRight', "Split Editor Right"); constructor( id: string, @@ -143,7 +143,7 @@ export class SplitEditorRightAction extends ExecuteCommandAction { export class SplitEditorUpAction extends ExecuteCommandAction { static readonly ID = SPLIT_EDITOR_UP; - static readonly LABEL = nls.localize('splitEditorGroupUp', "Split Editor Up"); + static readonly LABEL = localize('splitEditorGroupUp', "Split Editor Up"); constructor( id: string, @@ -157,7 +157,7 @@ export class SplitEditorUpAction extends ExecuteCommandAction { export class SplitEditorDownAction extends ExecuteCommandAction { static readonly ID = SPLIT_EDITOR_DOWN; - static readonly LABEL = nls.localize('splitEditorGroupDown', "Split Editor Down"); + static readonly LABEL = localize('splitEditorGroupDown', "Split Editor Down"); constructor( id: string, @@ -171,7 +171,7 @@ export class SplitEditorDownAction extends ExecuteCommandAction { export class JoinTwoGroupsAction extends Action { static readonly ID = 'workbench.action.joinTwoGroups'; - static readonly LABEL = nls.localize('joinTwoGroups', "Join Editor Group with Next Group"); + static readonly LABEL = localize('joinTwoGroups', "Join Editor Group with Next Group"); constructor( id: string, @@ -206,7 +206,7 @@ export class JoinTwoGroupsAction extends Action { export class JoinAllGroupsAction extends Action { static readonly ID = 'workbench.action.joinAllGroups'; - static readonly LABEL = nls.localize('joinAllGroups', "Join All Editor Groups"); + static readonly LABEL = localize('joinAllGroups', "Join All Editor Groups"); constructor( id: string, @@ -224,7 +224,7 @@ export class JoinAllGroupsAction extends Action { export class NavigateBetweenGroupsAction extends Action { static readonly ID = 'workbench.action.navigateEditorGroups'; - static readonly LABEL = nls.localize('navigateEditorGroups', "Navigate Between Editor Groups"); + static readonly LABEL = localize('navigateEditorGroups', "Navigate Between Editor Groups"); constructor( id: string, @@ -243,7 +243,7 @@ export class NavigateBetweenGroupsAction extends Action { export class FocusActiveGroupAction extends Action { static readonly ID = 'workbench.action.focusActiveEditorGroup'; - static readonly LABEL = nls.localize('focusActiveEditorGroup', "Focus Active Editor Group"); + static readonly LABEL = localize('focusActiveEditorGroup', "Focus Active Editor Group"); constructor( id: string, @@ -280,7 +280,7 @@ export abstract class BaseFocusGroupAction extends Action { export class FocusFirstGroupAction extends BaseFocusGroupAction { static readonly ID = 'workbench.action.focusFirstEditorGroup'; - static readonly LABEL = nls.localize('focusFirstEditorGroup', "Focus First Editor Group"); + static readonly LABEL = localize('focusFirstEditorGroup', "Focus First Editor Group"); constructor( id: string, @@ -294,7 +294,7 @@ export class FocusFirstGroupAction extends BaseFocusGroupAction { export class FocusLastGroupAction extends BaseFocusGroupAction { static readonly ID = 'workbench.action.focusLastEditorGroup'; - static readonly LABEL = nls.localize('focusLastEditorGroup', "Focus Last Editor Group"); + static readonly LABEL = localize('focusLastEditorGroup', "Focus Last Editor Group"); constructor( id: string, @@ -308,7 +308,7 @@ export class FocusLastGroupAction extends BaseFocusGroupAction { export class FocusNextGroup extends BaseFocusGroupAction { static readonly ID = 'workbench.action.focusNextGroup'; - static readonly LABEL = nls.localize('focusNextGroup', "Focus Next Editor Group"); + static readonly LABEL = localize('focusNextGroup', "Focus Next Editor Group"); constructor( id: string, @@ -322,7 +322,7 @@ export class FocusNextGroup extends BaseFocusGroupAction { export class FocusPreviousGroup extends BaseFocusGroupAction { static readonly ID = 'workbench.action.focusPreviousGroup'; - static readonly LABEL = nls.localize('focusPreviousGroup', "Focus Previous Editor Group"); + static readonly LABEL = localize('focusPreviousGroup', "Focus Previous Editor Group"); constructor( id: string, @@ -336,7 +336,7 @@ export class FocusPreviousGroup extends BaseFocusGroupAction { export class FocusLeftGroup extends BaseFocusGroupAction { static readonly ID = 'workbench.action.focusLeftGroup'; - static readonly LABEL = nls.localize('focusLeftGroup', "Focus Left Editor Group"); + static readonly LABEL = localize('focusLeftGroup', "Focus Left Editor Group"); constructor( id: string, @@ -350,7 +350,7 @@ export class FocusLeftGroup extends BaseFocusGroupAction { export class FocusRightGroup extends BaseFocusGroupAction { static readonly ID = 'workbench.action.focusRightGroup'; - static readonly LABEL = nls.localize('focusRightGroup', "Focus Right Editor Group"); + static readonly LABEL = localize('focusRightGroup', "Focus Right Editor Group"); constructor( id: string, @@ -364,7 +364,7 @@ export class FocusRightGroup extends BaseFocusGroupAction { export class FocusAboveGroup extends BaseFocusGroupAction { static readonly ID = 'workbench.action.focusAboveGroup'; - static readonly LABEL = nls.localize('focusAboveGroup', "Focus Above Editor Group"); + static readonly LABEL = localize('focusAboveGroup', "Focus Above Editor Group"); constructor( id: string, @@ -378,7 +378,7 @@ export class FocusAboveGroup extends BaseFocusGroupAction { export class FocusBelowGroup extends BaseFocusGroupAction { static readonly ID = 'workbench.action.focusBelowGroup'; - static readonly LABEL = nls.localize('focusBelowGroup', "Focus Below Editor Group"); + static readonly LABEL = localize('focusBelowGroup', "Focus Below Editor Group"); constructor( id: string, @@ -392,7 +392,7 @@ export class FocusBelowGroup extends BaseFocusGroupAction { export class CloseEditorAction extends Action { static readonly ID = 'workbench.action.closeActiveEditor'; - static readonly LABEL = nls.localize('closeEditor', "Close Editor"); + static readonly LABEL = localize('closeEditor', "Close Editor"); constructor( id: string, @@ -410,7 +410,7 @@ export class CloseEditorAction extends Action { export class UnpinEditorAction extends Action { static readonly ID = 'workbench.action.unpinActiveEditor'; - static readonly LABEL = nls.localize('unpinEditor', "Unpin Editor"); + static readonly LABEL = localize('unpinEditor', "Unpin Editor"); constructor( id: string, @@ -428,7 +428,7 @@ export class UnpinEditorAction extends Action { export class CloseOneEditorAction extends Action { static readonly ID = 'workbench.action.closeActiveEditor'; - static readonly LABEL = nls.localize('closeOneEditor', "Close"); + static readonly LABEL = localize('closeOneEditor', "Close"); constructor( id: string, @@ -471,7 +471,7 @@ export class CloseOneEditorAction extends Action { export class RevertAndCloseEditorAction extends Action { static readonly ID = 'workbench.action.revertAndCloseActiveEditor'; - static readonly LABEL = nls.localize('revertAndCloseActiveEditor', "Revert and Close Editor"); + static readonly LABEL = localize('revertAndCloseActiveEditor', "Revert and Close Editor"); constructor( id: string, @@ -506,7 +506,7 @@ export class RevertAndCloseEditorAction extends Action { export class CloseLeftEditorsInGroupAction extends Action { static readonly ID = 'workbench.action.closeEditorsToTheLeft'; - static readonly LABEL = nls.localize('closeEditorsToTheLeft', "Close Editors to the Left in Group"); + static readonly LABEL = localize('closeEditorsToTheLeft', "Close Editors to the Left in Group"); constructor( id: string, @@ -649,7 +649,7 @@ abstract class BaseCloseAllAction extends Action { export class CloseAllEditorsAction extends BaseCloseAllAction { static readonly ID = 'workbench.action.closeAllEditors'; - static readonly LABEL = nls.localize('closeAllEditors', "Close All Editors"); + static readonly LABEL = localize('closeAllEditors', "Close All Editors"); constructor( id: string, @@ -675,7 +675,7 @@ export class CloseAllEditorsAction extends BaseCloseAllAction { export class CloseAllEditorGroupsAction extends BaseCloseAllAction { static readonly ID = 'workbench.action.closeAllGroups'; - static readonly LABEL = nls.localize('closeAllGroups', "Close All Editor Groups"); + static readonly LABEL = localize('closeAllGroups', "Close All Editor Groups"); constructor( id: string, @@ -703,7 +703,7 @@ export class CloseAllEditorGroupsAction extends BaseCloseAllAction { export class CloseEditorsInOtherGroupsAction extends Action { static readonly ID = 'workbench.action.closeEditorsInOtherGroups'; - static readonly LABEL = nls.localize('closeEditorsInOtherGroups', "Close Editors in Other Groups"); + static readonly LABEL = localize('closeEditorsInOtherGroups', "Close Editors in Other Groups"); constructor( id: string, @@ -728,7 +728,7 @@ export class CloseEditorsInOtherGroupsAction extends Action { export class CloseEditorInAllGroupsAction extends Action { static readonly ID = 'workbench.action.closeEditorInAllGroups'; - static readonly LABEL = nls.localize('closeEditorInAllGroups', "Close Editor in All Groups"); + static readonly LABEL = localize('closeEditorInAllGroups', "Close Editor in All Groups"); constructor( id: string, @@ -827,7 +827,7 @@ class BaseMoveGroupAction extends BaseMoveCopyGroupAction { export class MoveGroupLeftAction extends BaseMoveGroupAction { static readonly ID = 'workbench.action.moveActiveEditorGroupLeft'; - static readonly LABEL = nls.localize('moveActiveGroupLeft', "Move Editor Group Left"); + static readonly LABEL = localize('moveActiveGroupLeft', "Move Editor Group Left"); constructor( id: string, @@ -841,7 +841,7 @@ export class MoveGroupLeftAction extends BaseMoveGroupAction { export class MoveGroupRightAction extends BaseMoveGroupAction { static readonly ID = 'workbench.action.moveActiveEditorGroupRight'; - static readonly LABEL = nls.localize('moveActiveGroupRight', "Move Editor Group Right"); + static readonly LABEL = localize('moveActiveGroupRight', "Move Editor Group Right"); constructor( id: string, @@ -855,7 +855,7 @@ export class MoveGroupRightAction extends BaseMoveGroupAction { export class MoveGroupUpAction extends BaseMoveGroupAction { static readonly ID = 'workbench.action.moveActiveEditorGroupUp'; - static readonly LABEL = nls.localize('moveActiveGroupUp', "Move Editor Group Up"); + static readonly LABEL = localize('moveActiveGroupUp', "Move Editor Group Up"); constructor( id: string, @@ -869,7 +869,7 @@ export class MoveGroupUpAction extends BaseMoveGroupAction { export class MoveGroupDownAction extends BaseMoveGroupAction { static readonly ID = 'workbench.action.moveActiveEditorGroupDown'; - static readonly LABEL = nls.localize('moveActiveGroupDown', "Move Editor Group Down"); + static readonly LABEL = localize('moveActiveGroupDown', "Move Editor Group Down"); constructor( id: string, @@ -895,7 +895,7 @@ class BaseDuplicateGroupAction extends BaseMoveCopyGroupAction { export class DuplicateGroupLeftAction extends BaseDuplicateGroupAction { static readonly ID = 'workbench.action.duplicateActiveEditorGroupLeft'; - static readonly LABEL = nls.localize('duplicateActiveGroupLeft', "Duplicate Editor Group Left"); + static readonly LABEL = localize('duplicateActiveGroupLeft', "Duplicate Editor Group Left"); constructor( id: string, @@ -909,7 +909,7 @@ export class DuplicateGroupLeftAction extends BaseDuplicateGroupAction { export class DuplicateGroupRightAction extends BaseDuplicateGroupAction { static readonly ID = 'workbench.action.duplicateActiveEditorGroupRight'; - static readonly LABEL = nls.localize('duplicateActiveGroupRight', "Duplicate Editor Group Right"); + static readonly LABEL = localize('duplicateActiveGroupRight', "Duplicate Editor Group Right"); constructor( id: string, @@ -923,7 +923,7 @@ export class DuplicateGroupRightAction extends BaseDuplicateGroupAction { export class DuplicateGroupUpAction extends BaseDuplicateGroupAction { static readonly ID = 'workbench.action.duplicateActiveEditorGroupUp'; - static readonly LABEL = nls.localize('duplicateActiveGroupUp', "Duplicate Editor Group Up"); + static readonly LABEL = localize('duplicateActiveGroupUp', "Duplicate Editor Group Up"); constructor( id: string, @@ -937,7 +937,7 @@ export class DuplicateGroupUpAction extends BaseDuplicateGroupAction { export class DuplicateGroupDownAction extends BaseDuplicateGroupAction { static readonly ID = 'workbench.action.duplicateActiveEditorGroupDown'; - static readonly LABEL = nls.localize('duplicateActiveGroupDown', "Duplicate Editor Group Down"); + static readonly LABEL = localize('duplicateActiveGroupDown', "Duplicate Editor Group Down"); constructor( id: string, @@ -951,7 +951,7 @@ export class DuplicateGroupDownAction extends BaseDuplicateGroupAction { export class MinimizeOtherGroupsAction extends Action { static readonly ID = 'workbench.action.minimizeOtherEditors'; - static readonly LABEL = nls.localize('minimizeOtherEditorGroups', "Maximize Editor Group"); + static readonly LABEL = localize('minimizeOtherEditorGroups', "Maximize Editor Group"); constructor(id: string, label: string, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService) { super(id, label); @@ -965,7 +965,7 @@ export class MinimizeOtherGroupsAction extends Action { export class ResetGroupSizesAction extends Action { static readonly ID = 'workbench.action.evenEditorWidths'; - static readonly LABEL = nls.localize('evenEditorGroups', "Reset Editor Group Sizes"); + static readonly LABEL = localize('evenEditorGroups', "Reset Editor Group Sizes"); constructor(id: string, label: string, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService) { super(id, label); @@ -979,7 +979,7 @@ export class ResetGroupSizesAction extends Action { export class ToggleGroupSizesAction extends Action { static readonly ID = 'workbench.action.toggleEditorWidths'; - static readonly LABEL = nls.localize('toggleEditorWidths', "Toggle Editor Group Sizes"); + static readonly LABEL = localize('toggleEditorWidths', "Toggle Editor Group Sizes"); constructor(id: string, label: string, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService) { super(id, label); @@ -993,7 +993,7 @@ export class ToggleGroupSizesAction extends Action { export class MaximizeGroupAction extends Action { static readonly ID = 'workbench.action.maximizeEditor'; - static readonly LABEL = nls.localize('maximizeEditor', "Maximize Editor Group and Hide Side Bar"); + static readonly LABEL = localize('maximizeEditor', "Maximize Editor Group and Hide Side Bar"); constructor( id: string, @@ -1047,7 +1047,7 @@ export abstract class BaseNavigateEditorAction extends Action { export class OpenNextEditor extends BaseNavigateEditorAction { static readonly ID = 'workbench.action.nextEditor'; - static readonly LABEL = nls.localize('openNextEditor', "Open Next Editor"); + static readonly LABEL = localize('openNextEditor', "Open Next Editor"); constructor( id: string, @@ -1082,7 +1082,7 @@ export class OpenNextEditor extends BaseNavigateEditorAction { export class OpenPreviousEditor extends BaseNavigateEditorAction { static readonly ID = 'workbench.action.previousEditor'; - static readonly LABEL = nls.localize('openPreviousEditor', "Open Previous Editor"); + static readonly LABEL = localize('openPreviousEditor', "Open Previous Editor"); constructor( id: string, @@ -1117,7 +1117,7 @@ export class OpenPreviousEditor extends BaseNavigateEditorAction { export class OpenNextEditorInGroup extends BaseNavigateEditorAction { static readonly ID = 'workbench.action.nextEditorInGroup'; - static readonly LABEL = nls.localize('nextEditorInGroup', "Open Next Editor in Group"); + static readonly LABEL = localize('nextEditorInGroup', "Open Next Editor in Group"); constructor( id: string, @@ -1140,7 +1140,7 @@ export class OpenNextEditorInGroup extends BaseNavigateEditorAction { export class OpenPreviousEditorInGroup extends BaseNavigateEditorAction { static readonly ID = 'workbench.action.previousEditorInGroup'; - static readonly LABEL = nls.localize('openPreviousEditorInGroup', "Open Previous Editor in Group"); + static readonly LABEL = localize('openPreviousEditorInGroup', "Open Previous Editor in Group"); constructor( id: string, @@ -1163,7 +1163,7 @@ export class OpenPreviousEditorInGroup extends BaseNavigateEditorAction { export class OpenFirstEditorInGroup extends BaseNavigateEditorAction { static readonly ID = 'workbench.action.firstEditorInGroup'; - static readonly LABEL = nls.localize('firstEditorInGroup', "Open First Editor in Group"); + static readonly LABEL = localize('firstEditorInGroup', "Open First Editor in Group"); constructor( id: string, @@ -1185,7 +1185,7 @@ export class OpenFirstEditorInGroup extends BaseNavigateEditorAction { export class OpenLastEditorInGroup extends BaseNavigateEditorAction { static readonly ID = 'workbench.action.lastEditorInGroup'; - static readonly LABEL = nls.localize('lastEditorInGroup', "Open Last Editor in Group"); + static readonly LABEL = localize('lastEditorInGroup', "Open Last Editor in Group"); constructor( id: string, @@ -1207,7 +1207,7 @@ export class OpenLastEditorInGroup extends BaseNavigateEditorAction { export class NavigateForwardAction extends Action { static readonly ID = 'workbench.action.navigateForward'; - static readonly LABEL = nls.localize('navigateNext', "Go Forward"); + static readonly LABEL = localize('navigateNext', "Go Forward"); constructor(id: string, label: string, @IHistoryService private readonly historyService: IHistoryService) { super(id, label); @@ -1221,7 +1221,7 @@ export class NavigateForwardAction extends Action { export class NavigateBackwardsAction extends Action { static readonly ID = 'workbench.action.navigateBack'; - static readonly LABEL = nls.localize('navigatePrevious', "Go Back"); + static readonly LABEL = localize('navigatePrevious', "Go Back"); constructor(id: string, label: string, @IHistoryService private readonly historyService: IHistoryService) { super(id, label); @@ -1235,7 +1235,7 @@ export class NavigateBackwardsAction extends Action { export class NavigateToLastEditLocationAction extends Action { static readonly ID = 'workbench.action.navigateToLastEditLocation'; - static readonly LABEL = nls.localize('navigateToLastEditLocation', "Go to Last Edit Location"); + static readonly LABEL = localize('navigateToLastEditLocation', "Go to Last Edit Location"); constructor(id: string, label: string, @IHistoryService private readonly historyService: IHistoryService) { super(id, label); @@ -1249,7 +1249,7 @@ export class NavigateToLastEditLocationAction extends Action { export class NavigateLastAction extends Action { static readonly ID = 'workbench.action.navigateLast'; - static readonly LABEL = nls.localize('navigateLast', "Go Last"); + static readonly LABEL = localize('navigateLast', "Go Last"); constructor(id: string, label: string, @IHistoryService private readonly historyService: IHistoryService) { super(id, label); @@ -1263,7 +1263,7 @@ export class NavigateLastAction extends Action { export class ReopenClosedEditorAction extends Action { static readonly ID = 'workbench.action.reopenClosedEditor'; - static readonly LABEL = nls.localize('reopenClosedEditor', "Reopen Closed Editor"); + static readonly LABEL = localize('reopenClosedEditor', "Reopen Closed Editor"); constructor( id: string, @@ -1281,7 +1281,7 @@ export class ReopenClosedEditorAction extends Action { export class ClearRecentFilesAction extends Action { static readonly ID = 'workbench.action.clearRecentFiles'; - static readonly LABEL = nls.localize('clearRecentFiles', "Clear Recently Opened"); + static readonly LABEL = localize('clearRecentFiles', "Clear Recently Opened"); constructor( id: string, @@ -1305,7 +1305,7 @@ export class ClearRecentFilesAction extends Action { export class ShowEditorsInActiveGroupByMostRecentlyUsedAction extends Action { static readonly ID = 'workbench.action.showEditorsInActiveGroup'; - static readonly LABEL = nls.localize('showEditorsInActiveGroup', "Show Editors in Active Group By Most Recently Used"); + static readonly LABEL = localize('showEditorsInActiveGroup', "Show Editors in Active Group By Most Recently Used"); constructor( id: string, @@ -1323,7 +1323,7 @@ export class ShowEditorsInActiveGroupByMostRecentlyUsedAction extends Action { export class ShowAllEditorsByAppearanceAction extends Action { static readonly ID = 'workbench.action.showAllEditors'; - static readonly LABEL = nls.localize('showAllEditors', "Show All Editors By Appearance"); + static readonly LABEL = localize('showAllEditors', "Show All Editors By Appearance"); constructor( id: string, @@ -1341,7 +1341,7 @@ export class ShowAllEditorsByAppearanceAction extends Action { export class ShowAllEditorsByMostRecentlyUsedAction extends Action { static readonly ID = 'workbench.action.showAllEditorsByMostRecentlyUsed'; - static readonly LABEL = nls.localize('showAllEditorsByMostRecentlyUsed', "Show All Editors By Most Recently Used"); + static readonly LABEL = localize('showAllEditorsByMostRecentlyUsed', "Show All Editors By Most Recently Used"); constructor( id: string, @@ -1382,7 +1382,7 @@ export class BaseQuickAccessEditorAction extends Action { export class QuickAccessPreviousRecentlyUsedEditorAction extends BaseQuickAccessEditorAction { static readonly ID = 'workbench.action.quickOpenPreviousRecentlyUsedEditor'; - static readonly LABEL = nls.localize('quickOpenPreviousRecentlyUsedEditor', "Quick Open Previous Recently Used Editor"); + static readonly LABEL = localize('quickOpenPreviousRecentlyUsedEditor', "Quick Open Previous Recently Used Editor"); constructor( id: string, @@ -1397,7 +1397,7 @@ export class QuickAccessPreviousRecentlyUsedEditorAction extends BaseQuickAccess export class QuickAccessLeastRecentlyUsedEditorAction extends BaseQuickAccessEditorAction { static readonly ID = 'workbench.action.quickOpenLeastRecentlyUsedEditor'; - static readonly LABEL = nls.localize('quickOpenLeastRecentlyUsedEditor', "Quick Open Least Recently Used Editor"); + static readonly LABEL = localize('quickOpenLeastRecentlyUsedEditor', "Quick Open Least Recently Used Editor"); constructor( id: string, @@ -1412,7 +1412,7 @@ export class QuickAccessLeastRecentlyUsedEditorAction extends BaseQuickAccessEdi export class QuickAccessPreviousRecentlyUsedEditorInGroupAction extends BaseQuickAccessEditorAction { static readonly ID = 'workbench.action.quickOpenPreviousRecentlyUsedEditorInGroup'; - static readonly LABEL = nls.localize('quickOpenPreviousRecentlyUsedEditorInGroup', "Quick Open Previous Recently Used Editor in Group"); + static readonly LABEL = localize('quickOpenPreviousRecentlyUsedEditorInGroup', "Quick Open Previous Recently Used Editor in Group"); constructor( id: string, @@ -1427,7 +1427,7 @@ export class QuickAccessPreviousRecentlyUsedEditorInGroupAction extends BaseQuic export class QuickAccessLeastRecentlyUsedEditorInGroupAction extends BaseQuickAccessEditorAction { static readonly ID = 'workbench.action.quickOpenLeastRecentlyUsedEditorInGroup'; - static readonly LABEL = nls.localize('quickOpenLeastRecentlyUsedEditorInGroup', "Quick Open Least Recently Used Editor in Group"); + static readonly LABEL = localize('quickOpenLeastRecentlyUsedEditorInGroup', "Quick Open Least Recently Used Editor in Group"); constructor( id: string, @@ -1442,7 +1442,7 @@ export class QuickAccessLeastRecentlyUsedEditorInGroupAction extends BaseQuickAc export class QuickAccessPreviousEditorFromHistoryAction extends Action { static readonly ID = 'workbench.action.openPreviousEditorFromHistory'; - static readonly LABEL = nls.localize('navigateEditorHistoryByInput', "Quick Open Previous Editor from History"); + static readonly LABEL = localize('navigateEditorHistoryByInput', "Quick Open Previous Editor from History"); constructor( id: string, @@ -1471,7 +1471,7 @@ export class QuickAccessPreviousEditorFromHistoryAction extends Action { export class OpenNextRecentlyUsedEditorAction extends Action { static readonly ID = 'workbench.action.openNextRecentlyUsedEditor'; - static readonly LABEL = nls.localize('openNextRecentlyUsedEditor', "Open Next Recently Used Editor"); + static readonly LABEL = localize('openNextRecentlyUsedEditor', "Open Next Recently Used Editor"); constructor( id: string, @@ -1489,7 +1489,7 @@ export class OpenNextRecentlyUsedEditorAction extends Action { export class OpenPreviousRecentlyUsedEditorAction extends Action { static readonly ID = 'workbench.action.openPreviousRecentlyUsedEditor'; - static readonly LABEL = nls.localize('openPreviousRecentlyUsedEditor', "Open Previous Recently Used Editor"); + static readonly LABEL = localize('openPreviousRecentlyUsedEditor', "Open Previous Recently Used Editor"); constructor( id: string, @@ -1507,7 +1507,7 @@ export class OpenPreviousRecentlyUsedEditorAction extends Action { export class OpenNextRecentlyUsedEditorInGroupAction extends Action { static readonly ID = 'workbench.action.openNextRecentlyUsedEditorInGroup'; - static readonly LABEL = nls.localize('openNextRecentlyUsedEditorInGroup', "Open Next Recently Used Editor In Group"); + static readonly LABEL = localize('openNextRecentlyUsedEditorInGroup', "Open Next Recently Used Editor In Group"); constructor( id: string, @@ -1526,7 +1526,7 @@ export class OpenNextRecentlyUsedEditorInGroupAction extends Action { export class OpenPreviousRecentlyUsedEditorInGroupAction extends Action { static readonly ID = 'workbench.action.openPreviousRecentlyUsedEditorInGroup'; - static readonly LABEL = nls.localize('openPreviousRecentlyUsedEditorInGroup', "Open Previous Recently Used Editor In Group"); + static readonly LABEL = localize('openPreviousRecentlyUsedEditorInGroup', "Open Previous Recently Used Editor In Group"); constructor( id: string, @@ -1545,7 +1545,7 @@ export class OpenPreviousRecentlyUsedEditorInGroupAction extends Action { export class ClearEditorHistoryAction extends Action { static readonly ID = 'workbench.action.clearEditorHistory'; - static readonly LABEL = nls.localize('clearEditorHistory', "Clear Editor History"); + static readonly LABEL = localize('clearEditorHistory', "Clear Editor History"); constructor( id: string, @@ -1565,7 +1565,7 @@ export class ClearEditorHistoryAction extends Action { export class MoveEditorLeftInGroupAction extends ExecuteCommandAction { static readonly ID = 'workbench.action.moveEditorLeftInGroup'; - static readonly LABEL = nls.localize('moveEditorLeft', "Move Editor Left"); + static readonly LABEL = localize('moveEditorLeft', "Move Editor Left"); constructor( id: string, @@ -1579,7 +1579,7 @@ export class MoveEditorLeftInGroupAction extends ExecuteCommandAction { export class MoveEditorRightInGroupAction extends ExecuteCommandAction { static readonly ID = 'workbench.action.moveEditorRightInGroup'; - static readonly LABEL = nls.localize('moveEditorRight', "Move Editor Right"); + static readonly LABEL = localize('moveEditorRight', "Move Editor Right"); constructor( id: string, @@ -1593,7 +1593,7 @@ export class MoveEditorRightInGroupAction extends ExecuteCommandAction { export class MoveEditorToPreviousGroupAction extends ExecuteCommandAction { static readonly ID = 'workbench.action.moveEditorToPreviousGroup'; - static readonly LABEL = nls.localize('moveEditorToPreviousGroup', "Move Editor into Previous Group"); + static readonly LABEL = localize('moveEditorToPreviousGroup', "Move Editor into Previous Group"); constructor( id: string, @@ -1607,7 +1607,7 @@ export class MoveEditorToPreviousGroupAction extends ExecuteCommandAction { export class MoveEditorToNextGroupAction extends ExecuteCommandAction { static readonly ID = 'workbench.action.moveEditorToNextGroup'; - static readonly LABEL = nls.localize('moveEditorToNextGroup', "Move Editor into Next Group"); + static readonly LABEL = localize('moveEditorToNextGroup', "Move Editor into Next Group"); constructor( id: string, @@ -1621,7 +1621,7 @@ export class MoveEditorToNextGroupAction extends ExecuteCommandAction { export class MoveEditorToAboveGroupAction extends ExecuteCommandAction { static readonly ID = 'workbench.action.moveEditorToAboveGroup'; - static readonly LABEL = nls.localize('moveEditorToAboveGroup', "Move Editor into Above Group"); + static readonly LABEL = localize('moveEditorToAboveGroup', "Move Editor into Above Group"); constructor( id: string, @@ -1635,7 +1635,7 @@ export class MoveEditorToAboveGroupAction extends ExecuteCommandAction { export class MoveEditorToBelowGroupAction extends ExecuteCommandAction { static readonly ID = 'workbench.action.moveEditorToBelowGroup'; - static readonly LABEL = nls.localize('moveEditorToBelowGroup', "Move Editor into Below Group"); + static readonly LABEL = localize('moveEditorToBelowGroup', "Move Editor into Below Group"); constructor( id: string, @@ -1649,7 +1649,7 @@ export class MoveEditorToBelowGroupAction extends ExecuteCommandAction { export class MoveEditorToLeftGroupAction extends ExecuteCommandAction { static readonly ID = 'workbench.action.moveEditorToLeftGroup'; - static readonly LABEL = nls.localize('moveEditorToLeftGroup', "Move Editor into Left Group"); + static readonly LABEL = localize('moveEditorToLeftGroup', "Move Editor into Left Group"); constructor( id: string, @@ -1663,7 +1663,7 @@ export class MoveEditorToLeftGroupAction extends ExecuteCommandAction { export class MoveEditorToRightGroupAction extends ExecuteCommandAction { static readonly ID = 'workbench.action.moveEditorToRightGroup'; - static readonly LABEL = nls.localize('moveEditorToRightGroup', "Move Editor into Right Group"); + static readonly LABEL = localize('moveEditorToRightGroup', "Move Editor into Right Group"); constructor( id: string, @@ -1677,7 +1677,7 @@ export class MoveEditorToRightGroupAction extends ExecuteCommandAction { export class MoveEditorToFirstGroupAction extends ExecuteCommandAction { static readonly ID = 'workbench.action.moveEditorToFirstGroup'; - static readonly LABEL = nls.localize('moveEditorToFirstGroup', "Move Editor into First Group"); + static readonly LABEL = localize('moveEditorToFirstGroup', "Move Editor into First Group"); constructor( id: string, @@ -1691,7 +1691,7 @@ export class MoveEditorToFirstGroupAction extends ExecuteCommandAction { export class MoveEditorToLastGroupAction extends ExecuteCommandAction { static readonly ID = 'workbench.action.moveEditorToLastGroup'; - static readonly LABEL = nls.localize('moveEditorToLastGroup', "Move Editor into Last Group"); + static readonly LABEL = localize('moveEditorToLastGroup', "Move Editor into Last Group"); constructor( id: string, @@ -1705,7 +1705,7 @@ export class MoveEditorToLastGroupAction extends ExecuteCommandAction { export class EditorLayoutSingleAction extends ExecuteCommandAction { static readonly ID = 'workbench.action.editorLayoutSingle'; - static readonly LABEL = nls.localize('editorLayoutSingle', "Single Column Editor Layout"); + static readonly LABEL = localize('editorLayoutSingle', "Single Column Editor Layout"); constructor( id: string, @@ -1719,7 +1719,7 @@ export class EditorLayoutSingleAction extends ExecuteCommandAction { export class EditorLayoutTwoColumnsAction extends ExecuteCommandAction { static readonly ID = 'workbench.action.editorLayoutTwoColumns'; - static readonly LABEL = nls.localize('editorLayoutTwoColumns', "Two Columns Editor Layout"); + static readonly LABEL = localize('editorLayoutTwoColumns', "Two Columns Editor Layout"); constructor( id: string, @@ -1733,7 +1733,7 @@ export class EditorLayoutTwoColumnsAction extends ExecuteCommandAction { export class EditorLayoutThreeColumnsAction extends ExecuteCommandAction { static readonly ID = 'workbench.action.editorLayoutThreeColumns'; - static readonly LABEL = nls.localize('editorLayoutThreeColumns', "Three Columns Editor Layout"); + static readonly LABEL = localize('editorLayoutThreeColumns', "Three Columns Editor Layout"); constructor( id: string, @@ -1747,7 +1747,7 @@ export class EditorLayoutThreeColumnsAction extends ExecuteCommandAction { export class EditorLayoutTwoRowsAction extends ExecuteCommandAction { static readonly ID = 'workbench.action.editorLayoutTwoRows'; - static readonly LABEL = nls.localize('editorLayoutTwoRows', "Two Rows Editor Layout"); + static readonly LABEL = localize('editorLayoutTwoRows', "Two Rows Editor Layout"); constructor( id: string, @@ -1761,7 +1761,7 @@ export class EditorLayoutTwoRowsAction extends ExecuteCommandAction { export class EditorLayoutThreeRowsAction extends ExecuteCommandAction { static readonly ID = 'workbench.action.editorLayoutThreeRows'; - static readonly LABEL = nls.localize('editorLayoutThreeRows', "Three Rows Editor Layout"); + static readonly LABEL = localize('editorLayoutThreeRows', "Three Rows Editor Layout"); constructor( id: string, @@ -1775,7 +1775,7 @@ export class EditorLayoutThreeRowsAction extends ExecuteCommandAction { export class EditorLayoutTwoByTwoGridAction extends ExecuteCommandAction { static readonly ID = 'workbench.action.editorLayoutTwoByTwoGrid'; - static readonly LABEL = nls.localize('editorLayoutTwoByTwoGrid', "Grid Editor Layout (2x2)"); + static readonly LABEL = localize('editorLayoutTwoByTwoGrid', "Grid Editor Layout (2x2)"); constructor( id: string, @@ -1789,7 +1789,7 @@ export class EditorLayoutTwoByTwoGridAction extends ExecuteCommandAction { export class EditorLayoutTwoColumnsBottomAction extends ExecuteCommandAction { static readonly ID = 'workbench.action.editorLayoutTwoColumnsBottom'; - static readonly LABEL = nls.localize('editorLayoutTwoColumnsBottom', "Two Columns Bottom Editor Layout"); + static readonly LABEL = localize('editorLayoutTwoColumnsBottom', "Two Columns Bottom Editor Layout"); constructor( id: string, @@ -1803,7 +1803,7 @@ export class EditorLayoutTwoColumnsBottomAction extends ExecuteCommandAction { export class EditorLayoutTwoRowsRightAction extends ExecuteCommandAction { static readonly ID = 'workbench.action.editorLayoutTwoRowsRight'; - static readonly LABEL = nls.localize('editorLayoutTwoRowsRight', "Two Rows Right Editor Layout"); + static readonly LABEL = localize('editorLayoutTwoRowsRight', "Two Rows Right Editor Layout"); constructor( id: string, @@ -1833,7 +1833,7 @@ export class BaseCreateEditorGroupAction extends Action { export class NewEditorGroupLeftAction extends BaseCreateEditorGroupAction { static readonly ID = 'workbench.action.newGroupLeft'; - static readonly LABEL = nls.localize('newEditorLeft', "New Editor Group to the Left"); + static readonly LABEL = localize('newEditorLeft', "New Editor Group to the Left"); constructor( id: string, @@ -1847,7 +1847,7 @@ export class NewEditorGroupLeftAction extends BaseCreateEditorGroupAction { export class NewEditorGroupRightAction extends BaseCreateEditorGroupAction { static readonly ID = 'workbench.action.newGroupRight'; - static readonly LABEL = nls.localize('newEditorRight', "New Editor Group to the Right"); + static readonly LABEL = localize('newEditorRight', "New Editor Group to the Right"); constructor( id: string, @@ -1861,7 +1861,7 @@ export class NewEditorGroupRightAction extends BaseCreateEditorGroupAction { export class NewEditorGroupAboveAction extends BaseCreateEditorGroupAction { static readonly ID = 'workbench.action.newGroupAbove'; - static readonly LABEL = nls.localize('newEditorAbove', "New Editor Group Above"); + static readonly LABEL = localize('newEditorAbove', "New Editor Group Above"); constructor( id: string, @@ -1875,7 +1875,7 @@ export class NewEditorGroupAboveAction extends BaseCreateEditorGroupAction { export class NewEditorGroupBelowAction extends BaseCreateEditorGroupAction { static readonly ID = 'workbench.action.newGroupBelow'; - static readonly LABEL = nls.localize('newEditorBelow', "New Editor Group Below"); + static readonly LABEL = localize('newEditorBelow', "New Editor Group Below"); constructor( id: string, @@ -1889,7 +1889,7 @@ export class NewEditorGroupBelowAction extends BaseCreateEditorGroupAction { export class ReopenResourcesAction extends Action { static readonly ID = 'workbench.action.reopenWithEditor'; - static readonly LABEL = nls.localize('workbench.action.reopenWithEditor', "Reopen Editor With..."); + static readonly LABEL = localize('workbench.action.reopenWithEditor', "Reopen Editor With..."); constructor( id: string, @@ -1920,7 +1920,7 @@ export class ReopenResourcesAction extends Action { export class ToggleEditorTypeAction extends Action { static readonly ID = 'workbench.action.toggleEditorType'; - static readonly LABEL = nls.localize('workbench.action.toggleEditorType', "Toggle Editor Type"); + static readonly LABEL = localize('workbench.action.toggleEditorType', "Toggle Editor Type"); constructor( id: string, diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index f9fd2d57164..1bf46e607df 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { isObject, isString, isUndefined, isNumber, withNullAsUndefined } from 'vs/base/common/types'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -103,11 +103,11 @@ function registerActiveEditorMoveCommand(): void { primary: 0, handler: (accessor, args) => moveActiveEditor(args, accessor), description: { - description: nls.localize('editorCommand.activeEditorMove.description', "Move the active editor by tabs or groups"), + description: localize('editorCommand.activeEditorMove.description', "Move the active editor by tabs or groups"), args: [ { - name: nls.localize('editorCommand.activeEditorMove.arg.name', "Active editor move argument"), - description: nls.localize('editorCommand.activeEditorMove.arg.description', "Argument Properties:\n\t* 'to': String value providing where to move.\n\t* 'by': String value providing the unit for move (by tab or by group).\n\t* 'value': Number value providing how many positions or an absolute position to move."), + name: localize('editorCommand.activeEditorMove.arg.name', "Active editor move argument"), + description: localize('editorCommand.activeEditorMove.arg.description', "Argument Properties:\n\t* 'to': String value providing where to move.\n\t* 'by': String value providing the unit for move (by tab or by group).\n\t* 'value': Number value providing how many positions or an absolute position to move."), constraint: isActiveEditorMoveArg, schema: { 'type': 'object', @@ -408,10 +408,10 @@ function registerDiffEditorCommands(): void { command: { id: TOGGLE_DIFF_SIDE_BY_SIDE, title: { - value: nls.localize('toggleInlineView', "Toggle Inline View"), + value: localize('toggleInlineView', "Toggle Inline View"), original: 'Compare: Toggle Inline View' }, - category: nls.localize('compare', "Compare") + category: localize('compare', "Compare") }, when: ContextKeyExpr.has('textCompareEditorActive') }); diff --git a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts index 90d33b124a2..efd6650f5be 100644 --- a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts +++ b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as DOM from 'vs/base/browser/dom'; +import { Dimension, $, clearNode } from 'vs/base/browser/dom'; import { Registry } from 'vs/platform/registry/common/platform'; import { EditorInput, EditorOptions, SideBySideEditorInput, IEditorControl, IEditorPane, IEditorOpenContext } from 'vs/workbench/common/editor'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; @@ -51,7 +51,7 @@ export class SideBySideEditor extends EditorPane { private secondaryEditorContainer: HTMLElement | undefined; private splitview: SplitView | undefined; - private dimension: DOM.Dimension = new DOM.Dimension(0, 0); + private dimension: Dimension = new Dimension(0, 0); private onDidCreateEditors = this._register(new Emitter<{ width: number; height: number; } | undefined>()); @@ -73,19 +73,19 @@ export class SideBySideEditor extends EditorPane { const splitview = this.splitview = this._register(new SplitView(parent, { orientation: Orientation.HORIZONTAL })); this._register(this.splitview.onDidSashReset(() => splitview.distributeViewSizes())); - this.secondaryEditorContainer = DOM.$('.secondary-editor-container'); + this.secondaryEditorContainer = $('.secondary-editor-container'); this.splitview.addView({ element: this.secondaryEditorContainer, - layout: size => this.secondaryEditorPane?.layout(new DOM.Dimension(size, this.dimension.height)), + layout: size => this.secondaryEditorPane?.layout(new Dimension(size, this.dimension.height)), minimumSize: 220, maximumSize: Number.POSITIVE_INFINITY, onDidChange: Event.None }, Sizing.Distribute); - this.primaryEditorContainer = DOM.$('.primary-editor-container'); + this.primaryEditorContainer = $('.primary-editor-container'); this.splitview.addView({ element: this.primaryEditorContainer, - layout: size => this.primaryEditorPane?.layout(new DOM.Dimension(size, this.dimension.height)), + layout: size => this.primaryEditorPane?.layout(new Dimension(size, this.dimension.height)), minimumSize: 220, maximumSize: Number.POSITIVE_INFINITY, onDidChange: Event.None @@ -139,7 +139,7 @@ export class SideBySideEditor extends EditorPane { } } - layout(dimension: DOM.Dimension): void { + layout(dimension: Dimension): void { this.dimension = dimension; const splitview = assertIsDefined(this.splitview); @@ -238,11 +238,11 @@ export class SideBySideEditor extends EditorPane { } if (this.secondaryEditorContainer) { - DOM.clearNode(this.secondaryEditorContainer); + clearNode(this.secondaryEditorContainer); } if (this.primaryEditorContainer) { - DOM.clearNode(this.primaryEditorContainer); + clearNode(this.primaryEditorContainer); } } diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index d07cc642e0a..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 { @@ -606,7 +606,6 @@ export class TabsTitleControl extends TitleControl { // Tab Container const tabContainer = document.createElement('div'); tabContainer.draggable = true; - tabContainer.tabIndex = 0; tabContainer.setAttribute('role', 'tab'); tabContainer.classList.add('tab'); @@ -1120,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 { @@ -1178,15 +1177,15 @@ 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 { + private doRedrawTabActive(isGroupActive: boolean, allowBorderTop: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabActionBar: ActionBar): void { // Tab is active if (this.group.isActive(editor)) { @@ -1194,6 +1193,7 @@ export class TabsTitleControl extends TitleControl { // Container tabContainer.classList.add('active'); tabContainer.setAttribute('aria-selected', '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); @@ -1216,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 @@ -1224,11 +1227,15 @@ export class TabsTitleControl extends TitleControl { // Container tabContainer.classList.remove('active'); tabContainer.setAttribute('aria-selected', '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/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index 7ae3677e182..54eca9b3349 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; -import * as objects from 'vs/base/common/objects'; +import { localize } from 'vs/nls'; +import { deepClone } from 'vs/base/common/objects'; import { isFunction, isObject, isArray, assertIsDefined, withUndefinedAsNull } from 'vs/base/common/types'; import { IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { IDiffEditorOptions, IEditorOptions as ICodeEditorOptions } from 'vs/editor/common/config/editorOptions'; @@ -99,7 +99,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan return this.input.getName(); } - return nls.localize('textDiffEditor', "Text Diff Editor"); + return localize('textDiffEditor', "Text Diff Editor"); } createEditorControl(parent: HTMLElement, configuration: ICodeEditorOptions): IDiffEditor { @@ -236,7 +236,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan // Handle diff editor specially by merging in diffEditor configuration if (isObject(configuration.diffEditor)) { - const diffEditorConfiguration = objects.deepClone(configuration.diffEditor); + const diffEditorConfiguration: IDiffEditorOptions = deepClone(configuration.diffEditor); // User settings defines `diffEditor.codeLens`, but here we rename that to `diffEditor.diffCodeLens` to avoid collisions with `editor.codeLens`. diffEditorConfiguration.diffCodeLens = diffEditorConfiguration.codeLens; diff --git a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts index 2bb2640dc79..63135239171 100644 --- a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { assertIsDefined, isFunction, withNullAsUndefined } from 'vs/base/common/types'; import { ICodeEditor, getCodeEditor, IPasteEvent } from 'vs/editor/browser/editorBrowser'; import { TextEditorOptions, EditorInput, EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; @@ -50,7 +50,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor { return this.input.getName(); } - return nls.localize('textEditor', "Text Editor"); + return localize('textEditor', "Text Editor"); } async setInput(input: EditorInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { diff --git a/src/vs/workbench/browser/parts/editor/titleControl.ts b/src/vs/workbench/browser/parts/editor/titleControl.ts index f45f9ed0a6f..8677017e740 100644 --- a/src/vs/workbench/browser/parts/editor/titleControl.ts +++ b/src/vs/workbench/browser/parts/editor/titleControl.ts @@ -7,9 +7,9 @@ import 'vs/css!./media/titlecontrol'; import { applyDragImage, DataTransfers } from 'vs/base/browser/dnd'; import { addDisposableListener, Dimension, EventType } from 'vs/base/browser/dom'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; -import { ActionsOrientation, prepareActions } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionsOrientation, IActionViewItem, prepareActions } from 'vs/base/browser/ui/actionbar/actionbar'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; -import { IAction, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification, IActionViewItem, SubmenuAction } from 'vs/base/common/actions'; +import { IAction, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification, SubmenuAction } from 'vs/base/common/actions'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { dispose, DisposableStore } from 'vs/base/common/lifecycle'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; diff --git a/src/vs/workbench/browser/parts/editor/untitledHint.ts b/src/vs/workbench/browser/parts/editor/untitledHint.ts new file mode 100644 index 00000000000..daa2d1ca59d --- /dev/null +++ b/src/vs/workbench/browser/parts/editor/untitledHint.ts @@ -0,0 +1,150 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as dom from 'vs/base/browser/dom'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition, isCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { localize } from 'vs/nls'; +import { DEFAULT_FONT_FAMILY } from 'vs/workbench/browser/style'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { inputPlaceholderForeground } from 'vs/platform/theme/common/colorRegistry'; +import { ChangeModeAction } from 'vs/workbench/browser/parts/editor/editorStatus'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +const $ = dom.$; + +const UNTITLED_HINT_VISIBILITY_STORAGE_KEY = 'untitledHint.visible'; +export class UntitledHintContribution implements IWorkbenchContribution { + + private toDispose: IDisposable[]; + private untitledHintContentWidget: UntitledHintContentWidget | undefined; + + constructor( + @IEditorService private readonly editorService: IEditorService, + @ICommandService private readonly commandService: ICommandService, + @IStorageService private readonly storageService: IStorageService, + ) { + this.toDispose = []; + this.toDispose.push(this.editorService.onDidActiveEditorChange(() => this.onActiveEditorChange())); + this.onActiveEditorChange(); + } + + private onActiveEditorChange(): void { + const activeEditor = this.editorService.activeEditor; + this.untitledHintContentWidget?.dispose(); + + const activeCodeEditor = this.editorService.activeTextEditorControl; + const untitledHintVisible = this.storageService.getBoolean(UNTITLED_HINT_VISIBILITY_STORAGE_KEY, StorageScope.GLOBAL, true); + if (untitledHintVisible && activeEditor && activeEditor.isUntitled() && isCodeEditor(activeCodeEditor)) { + this.untitledHintContentWidget = new UntitledHintContentWidget(activeCodeEditor, this.commandService, this.storageService); + } + } + + dispose(): void { + dispose(this.toDispose); + this.untitledHintContentWidget?.dispose(); + } +} + +class UntitledHintContentWidget implements IContentWidget { + + private static readonly ID = 'editor.widget.untitledHint'; + + private domNode: HTMLElement | undefined; + private toDispose: IDisposable[]; + + constructor( + private readonly editor: ICodeEditor, + private readonly commandService: ICommandService, + private readonly storageService: IStorageService, + ) { + this.toDispose = []; + this.toDispose.push(editor.onDidChangeModelContent(() => this.onDidChangeModelContent())); + this.toDispose.push(editor.onDidChangeModelLanguage(() => this.onDidChangeModelContent())); + this.onDidChangeModelContent(); + } + + private onDidChangeModelContent(): void { + if (this.editor.getValue() === '' && this.editor.getModel()?.getModeId() === PLAINTEXT_MODE_ID) { + this.editor.addContentWidget(this); + } else { + this.editor.removeContentWidget(this); + } + } + + getId(): string { + return UntitledHintContentWidget.ID; + } + + + // Select a language to get started. Start typing to dismiss, or don't show this again. + getDomNode(): HTMLElement { + if (!this.domNode) { + this.domNode = $('.untitled-hint'); + this.domNode.style.width = 'max-content'; + const select = $('span'); + select.innerText = localize('select', "Select a "); + this.domNode.appendChild(select); + const language = $('span.language-mode.detected-link-active'); + language.innerText = localize('language', "language"); + this.domNode.appendChild(language); + const toGetStarted = $('span'); + toGetStarted.innerText = localize('toGetStarted', " to get started. Start typing to dismiss, or "); + this.domNode.appendChild(toGetStarted); + + const dontShow = $('span.detected-link-active'); + dontShow.innerText = localize('dontshow', "don't show"); + this.domNode.appendChild(dontShow); + + const thisAgain = $('span'); + thisAgain.innerText = localize('thisAgain', " this again."); + this.domNode.appendChild(thisAgain); + + this.toDispose.push(dom.addDisposableListener(language, 'click', async e => { + e.stopPropagation(); + await this.commandService.executeCommand(ChangeModeAction.ID); + this.editor.focus(); + })); + + this.toDispose.push(dom.addDisposableListener(dontShow, 'click', () => { + this.storageService.store(UNTITLED_HINT_VISIBILITY_STORAGE_KEY, false, StorageScope.GLOBAL, StorageTarget.USER); + this.dispose(); + this.editor.focus(); + })); + + this.toDispose.push(dom.addDisposableListener(this.domNode, 'click', () => { + this.editor.focus(); + })); + + this.domNode.style.fontFamily = DEFAULT_FONT_FAMILY; + this.domNode.style.fontStyle = 'italic'; + this.domNode.style.paddingLeft = '4px'; + } + + return this.domNode; + } + + getPosition(): IContentWidgetPosition | null { + return { + position: { lineNumber: 1, column: 1 }, + preference: [ContentWidgetPositionPreference.EXACT] + }; + } + + dispose(): void { + this.editor.removeContentWidget(this); + dispose(this.toDispose); + } +} + +registerThemingParticipant((theme, collector) => { + const inputPlaceholderForegroundColor = theme.getColor(inputPlaceholderForeground); + if (inputPlaceholderForegroundColor) { + collector.addRule(`.monaco-editor .contentWidgets .untitled-hint { color: ${inputPlaceholderForegroundColor}; }`); + } +}); diff --git a/src/vs/workbench/browser/parts/panel/panelActions.ts b/src/vs/workbench/browser/parts/panel/panelActions.ts index 372e78f8a50..0d447ee9d3d 100644 --- a/src/vs/workbench/browser/parts/panel/panelActions.ts +++ b/src/vs/workbench/browser/parts/panel/panelActions.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/panelpart'; -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { Action } from 'vs/base/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -21,14 +21,14 @@ import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { ViewContainerLocationToString, ViewContainerLocation } from 'vs/workbench/common/views'; -const maximizeIcon = registerIcon('panel-maximize', Codicon.chevronUp, nls.localize('maximizeIcon', 'Icon to maximize a panel.')); -const restoreIcon = registerIcon('panel-restore', Codicon.chevronDown, nls.localize('restoreIcon', 'Icon to restore a panel.')); -const closeIcon = registerIcon('panel-close', Codicon.close, nls.localize('closeIcon', 'Icon to close a panel.')); +const maximizeIcon = registerIcon('panel-maximize', Codicon.chevronUp, localize('maximizeIcon', 'Icon to maximize a panel.')); +const restoreIcon = registerIcon('panel-restore', Codicon.chevronDown, localize('restoreIcon', 'Icon to restore a panel.')); +const closeIcon = registerIcon('panel-close', Codicon.close, localize('closeIcon', 'Icon to close a panel.')); export class TogglePanelAction extends Action { static readonly ID = 'workbench.action.togglePanel'; - static readonly LABEL = nls.localize('togglePanel', "Toggle Panel"); + static readonly LABEL = localize('togglePanel', "Toggle Panel"); constructor( id: string, @@ -46,7 +46,7 @@ export class TogglePanelAction extends Action { class FocusPanelAction extends Action { static readonly ID = 'workbench.action.focusPanel'; - static readonly LABEL = nls.localize('focusPanel', "Focus into Panel"); + static readonly LABEL = localize('focusPanel', "Focus into Panel"); constructor( id: string, @@ -97,9 +97,9 @@ function createPositionPanelActionConfig(id: string, alias: string, label: strin } export const PositionPanelActionConfigs: PanelActionConfig[] = [ - createPositionPanelActionConfig(PositionPanelActionId.LEFT, 'View: Move Panel Left', nls.localize('positionPanelLeft', 'Move Panel Left'), Position.LEFT), - createPositionPanelActionConfig(PositionPanelActionId.RIGHT, 'View: Move Panel Right', nls.localize('positionPanelRight', 'Move Panel Right'), Position.RIGHT), - createPositionPanelActionConfig(PositionPanelActionId.BOTTOM, 'View: Move Panel To Bottom', nls.localize('positionPanelBottom', 'Move Panel To Bottom'), Position.BOTTOM), + createPositionPanelActionConfig(PositionPanelActionId.LEFT, 'View: Move Panel Left', localize('positionPanelLeft', 'Move Panel Left'), Position.LEFT), + createPositionPanelActionConfig(PositionPanelActionId.RIGHT, 'View: Move Panel Right', localize('positionPanelRight', 'Move Panel Right'), Position.RIGHT), + createPositionPanelActionConfig(PositionPanelActionId.BOTTOM, 'View: Move Panel To Bottom', localize('positionPanelBottom', 'Move Panel To Bottom'), Position.BOTTOM), ]; const positionByActionId = new Map(PositionPanelActionConfigs.map(config => [config.id, config.value])); @@ -191,7 +191,7 @@ export class SwitchPanelViewAction extends Action { export class PreviousPanelViewAction extends SwitchPanelViewAction { static readonly ID = 'workbench.action.previousPanelView'; - static readonly LABEL = nls.localize('previousPanelView', 'Previous Panel View'); + static readonly LABEL = localize('previousPanelView', 'Previous Panel View'); constructor( id: string, @@ -209,7 +209,7 @@ export class PreviousPanelViewAction extends SwitchPanelViewAction { export class NextPanelViewAction extends SwitchPanelViewAction { static readonly ID = 'workbench.action.nextPanelView'; - static readonly LABEL = nls.localize('nextPanelView', 'Next Panel View'); + static readonly LABEL = localize('nextPanelView', 'Next Panel View'); constructor( id: string, @@ -234,12 +234,12 @@ registerAction2(class extends Action2 { constructor() { super({ id: 'workbench.action.toggleMaximizedPanel', - title: { value: nls.localize('toggleMaximizedPanel', "Toggle Maximized Panel"), original: 'Toggle Maximized Panel' }, - tooltip: nls.localize('maximizePanel', "Maximize Panel Size"), + title: { value: localize('toggleMaximizedPanel', "Toggle Maximized Panel"), original: 'Toggle Maximized Panel' }, + tooltip: localize('maximizePanel', "Maximize Panel Size"), category: CATEGORIES.View, f1: true, icon: maximizeIcon, - toggled: { condition: PanelMaximizedContext, icon: restoreIcon, tooltip: nls.localize('minimizePanel', "Restore Panel Size") }, + toggled: { condition: PanelMaximizedContext, icon: restoreIcon, tooltip: localize('minimizePanel', "Restore Panel Size") }, menu: [{ id: MenuId.PanelTitle, group: 'navigation', @@ -266,7 +266,7 @@ registerAction2(class extends Action2 { constructor() { super({ id: 'workbench.action.closePanel', - title: { value: nls.localize('closePanel', "Close Panel"), original: 'Close Panel' }, + title: { value: localize('closePanel', "Close Panel"), original: 'Close Panel' }, category: CATEGORIES.View, icon: closeIcon, menu: [{ @@ -291,7 +291,7 @@ MenuRegistry.appendMenuItems([ group: '2_workbench_layout', command: { id: TogglePanelAction.ID, - title: nls.localize({ key: 'miShowPanel', comment: ['&& denotes a mnemonic'] }, "Show &&Panel"), + title: localize({ key: 'miShowPanel', comment: ['&& denotes a mnemonic'] }, "Show &&Panel"), toggled: ActivePanelContext }, order: 5 @@ -302,7 +302,7 @@ MenuRegistry.appendMenuItems([ group: '3_workbench_layout_move', command: { id: TogglePanelAction.ID, - title: { value: nls.localize('hidePanel', "Hide Panel"), original: 'Hide Panel' }, + title: { value: localize('hidePanel', "Hide Panel"), original: 'Hide Panel' }, }, when: ContextKeyExpr.and(PanelVisibleContext, ContextKeyExpr.equals('viewLocation', ViewContainerLocationToString(ViewContainerLocation.Panel))), order: 2 diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts index 671a05822eb..ed77ad7b157 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/sidebarpart'; -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { CompositePart } from 'vs/workbench/browser/parts/compositePart'; import { Viewlet, ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor } from 'vs/workbench/browser/viewlet'; @@ -307,7 +307,7 @@ class FocusSideBarAction extends Action2 { constructor() { super({ id: 'workbench.action.focusSideBar', - title: { value: nls.localize('focusSideBar', "Focus into Side Bar"), original: 'Focus into Side Bar' }, + title: { value: localize('focusSideBar', "Focus into Side Bar"), original: 'Focus into Side Bar' }, category: CATEGORIES.View, f1: true, keybinding: { diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts index aa9d9f05f9a..b339140fdd9 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/statusbarpart'; -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { dispose, IDisposable, Disposable, toDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { SimpleIconLabel } from 'vs/base/browser/ui/iconLabel/simpleIconLabel'; @@ -364,7 +364,7 @@ class ToggleStatusbarEntryVisibilityAction extends Action { class HideStatusbarEntryAction extends Action { constructor(id: string, name: string, private model: StatusbarViewModel) { - super(id, nls.localize('hide', "Hide '{0}'", name), undefined, true); + super(id, localize('hide', "Hide '{0}'", name), undefined, true); } async run(): Promise { @@ -613,7 +613,7 @@ export class StatusbarPart extends Part implements IStatusbarService { const actions: IAction[] = []; // Provide an action to hide the status bar at last - actions.push(this.instantiationService.createInstance(ToggleStatusbarVisibilityAction, ToggleStatusbarVisibilityAction.ID, nls.localize('hideStatusBar', "Hide Status Bar"))); + actions.push(this.instantiationService.createInstance(ToggleStatusbarVisibilityAction, ToggleStatusbarVisibilityAction.ID, localize('hideStatusBar', "Hide Status Bar"))); actions.push(new Separator()); // Show an entry per known status entry diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index f3587a51a51..0600e3ae769 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { IMenuService, MenuId, IMenu, SubmenuItemAction, registerAction2, Action2, MenuItemAction } from 'vs/platform/actions/common/actions'; import { registerThemingParticipant, IThemeService } from 'vs/platform/theme/common/themeService'; import { MenuBarVisibility, getTitleBarStyle, IWindowOpenable, getMenuBarVisibility } from 'vs/platform/windows/common/windows'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IAction, Action, SubmenuAction, Separator } from 'vs/base/common/actions'; -import * as DOM from 'vs/base/browser/dom'; +import { addDisposableListener, Dimension, EventType } from 'vs/base/browser/dom'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { isMacintosh, isWeb, isIOS, isNative } from 'vs/base/common/platform'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; @@ -63,14 +63,14 @@ export abstract class MenubarControl extends Disposable { }; protected topLevelTitles: { [menu: string]: string } = { - 'File': nls.localize({ key: 'mFile', comment: ['&& denotes a mnemonic'] }, "&&File"), - 'Edit': nls.localize({ key: 'mEdit', comment: ['&& denotes a mnemonic'] }, "&&Edit"), - 'Selection': nls.localize({ key: 'mSelection', comment: ['&& denotes a mnemonic'] }, "&&Selection"), - 'View': nls.localize({ key: 'mView', comment: ['&& denotes a mnemonic'] }, "&&View"), - 'Go': nls.localize({ key: 'mGoto', comment: ['&& denotes a mnemonic'] }, "&&Go"), - 'Run': nls.localize({ key: 'mRun', comment: ['&& denotes a mnemonic'] }, "&&Run"), - 'Terminal': nls.localize({ key: 'mTerminal', comment: ['&& denotes a mnemonic'] }, "&&Terminal"), - 'Help': nls.localize({ key: 'mHelp', comment: ['&& denotes a mnemonic'] }, "&&Help") + 'File': localize({ key: 'mFile', comment: ['&& denotes a mnemonic'] }, "&&File"), + 'Edit': localize({ key: 'mEdit', comment: ['&& denotes a mnemonic'] }, "&&Edit"), + 'Selection': localize({ key: 'mSelection', comment: ['&& denotes a mnemonic'] }, "&&Selection"), + 'View': localize({ key: 'mView', comment: ['&& denotes a mnemonic'] }, "&&View"), + 'Go': localize({ key: 'mGoto', comment: ['&& denotes a mnemonic'] }, "&&Go"), + 'Run': localize({ key: 'mRun', comment: ['&& denotes a mnemonic'] }, "&&Run"), + 'Terminal': localize({ key: 'mTerminal', comment: ['&& denotes a mnemonic'] }, "&&Terminal"), + 'Help': localize({ key: 'mHelp', comment: ['&& denotes a mnemonic'] }, "&&Help") }; protected recentlyOpened: IRecentlyOpened = { files: [], workspaces: [] }; @@ -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) { @@ -271,10 +271,10 @@ export abstract class MenubarControl extends Disposable { return; } - const message = nls.localize('menubar.customTitlebarAccessibilityNotification', "Accessibility support is enabled for you. For the most accessible experience, we recommend the custom title bar style."); + const message = localize('menubar.customTitlebarAccessibilityNotification', "Accessibility support is enabled for you. For the most accessible experience, we recommend the custom title bar style."); this.notificationService.prompt(Severity.Info, message, [ { - label: nls.localize('goToSetting', "Open Settings"), + label: localize('goToSetting', "Open Settings"), run: () => { return this.preferencesService.openGlobalSettings(undefined, { query: 'window.titleBarStyle' }); } @@ -432,7 +432,7 @@ export class CustomMenubarControl extends MenubarControl { constructor() { super({ id: `workbench.actions.menubar.focus`, - title: { value: nls.localize('focusMenu', "Focus Application Menu"), original: 'Focus Application Menu' }, + title: { value: localize('focusMenu', "Focus Application Menu"), original: 'Focus Application Menu' }, keybinding: { primary: KeyCode.F10, weight: KeybindingWeight.WorkbenchContrib, @@ -459,28 +459,28 @@ export class CustomMenubarControl extends MenubarControl { return null; case StateType.Idle: - return new Action('update.check', nls.localize({ key: 'checkForUpdates', comment: ['&& denotes a mnemonic'] }, "Check for &&Updates..."), undefined, true, () => + return new Action('update.check', localize({ key: 'checkForUpdates', comment: ['&& denotes a mnemonic'] }, "Check for &&Updates..."), undefined, true, () => this.updateService.checkForUpdates(this.environmentService.sessionId)); case StateType.CheckingForUpdates: - return new Action('update.checking', nls.localize('checkingForUpdates', "Checking for Updates..."), undefined, false); + return new Action('update.checking', localize('checkingForUpdates', "Checking for Updates..."), undefined, false); case StateType.AvailableForDownload: - return new Action('update.downloadNow', nls.localize({ key: 'download now', comment: ['&& denotes a mnemonic'] }, "D&&ownload Update"), undefined, true, () => + return new Action('update.downloadNow', localize({ key: 'download now', comment: ['&& denotes a mnemonic'] }, "D&&ownload Update"), undefined, true, () => this.updateService.downloadUpdate()); case StateType.Downloading: - return new Action('update.downloading', nls.localize('DownloadingUpdate', "Downloading Update..."), undefined, false); + return new Action('update.downloading', localize('DownloadingUpdate', "Downloading Update..."), undefined, false); case StateType.Downloaded: - return new Action('update.install', nls.localize({ key: 'installUpdate...', comment: ['&& denotes a mnemonic'] }, "Install &&Update..."), undefined, true, () => + return new Action('update.install', localize({ key: 'installUpdate...', comment: ['&& denotes a mnemonic'] }, "Install &&Update..."), undefined, true, () => this.updateService.applyUpdate()); case StateType.Updating: - return new Action('update.updating', nls.localize('installingUpdate', "Installing Update..."), undefined, false); + return new Action('update.updating', localize('installingUpdate', "Installing Update..."), undefined, false); case StateType.Ready: - return new Action('update.restart', nls.localize({ key: 'restartToUpdate', comment: ['&& denotes a mnemonic'] }, "Restart to &&Update"), undefined, true, () => + return new Action('update.restart', localize({ key: 'restartToUpdate', comment: ['&& denotes a mnemonic'] }, "Restart to &&Update"), undefined, true, () => this.updateService.quitAndInstall()); } } @@ -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); } @@ -574,11 +574,11 @@ export class CustomMenubarControl extends MenubarControl { this._register(this.menubar.onVisibilityChange(e => this.onDidVisibilityChange(e))); // Before we focus the menubar, stop updates to it so that focus-related context keys will work - this._register(DOM.addDisposableListener(this.container, DOM.EventType.FOCUS_IN, () => { + this._register(addDisposableListener(this.container, EventType.FOCUS_IN, () => { this.focusInsideMenubar = true; })); - this._register(DOM.addDisposableListener(this.container, DOM.EventType.FOCUS_OUT, () => { + this._register(addDisposableListener(this.container, EventType.FOCUS_OUT, () => { this.focusInsideMenubar = false; })); @@ -722,7 +722,7 @@ export class CustomMenubarControl extends MenubarControl { const webNavigationActions: IAction[] = []; const href = this.environmentService.options?.homeIndicator?.href; if (href) { - webNavigationActions.push(new Action('goHome', nls.localize('goHome', "Go Home"), undefined, true, + webNavigationActions.push(new Action('goHome', localize('goHome', "Go Home"), undefined, true, async (event?: MouseEvent) => { if ((!isMacintosh && event?.ctrlKey) || (isMacintosh && event?.metaKey)) { window.open(href, '_blank'); @@ -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 { @@ -784,7 +784,7 @@ export class CustomMenubarControl extends MenubarControl { protected registerListeners(): void { super.registerListeners(); - this._register(DOM.addDisposableListener(window, DOM.EventType.RESIZE, () => { + this._register(addDisposableListener(window, EventType.RESIZE, () => { if (this.menubar && !(isIOS && BrowserFeatures.pointerEvents)) { this.menubar.blur(); } @@ -805,12 +805,12 @@ export class CustomMenubarControl extends MenubarControl { return this._onFocusStateChange.event; } - getMenubarItemsDimensions(): DOM.Dimension { + getMenubarItemsDimensions(): Dimension { if (this.menubar) { - return new DOM.Dimension(this.menubar.getWidth(), this.menubar.getHeight()); + return new Dimension(this.menubar.getWidth(), this.menubar.getHeight()); } - return new DOM.Dimension(0, 0); + return new Dimension(0, 0); } create(parent: HTMLElement): HTMLElement { @@ -824,7 +824,7 @@ export class CustomMenubarControl extends MenubarControl { return this.container; } - layout(dimension: DOM.Dimension) { + layout(dimension: Dimension) { if (this.container) { this.container.style.height = `${dimension.height}px`; } diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index e5e388f6102..04482e7d7f1 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -19,7 +19,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { Event, Emitter } from 'vs/base/common/event'; -import { IAction, ActionRunner, IActionViewItemProvider } from 'vs/base/common/actions'; +import { IAction, ActionRunner } from 'vs/base/common/actions'; import { createAndFillInContextMenuActions, createActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IProgressService } from 'vs/platform/progress/common/progress'; @@ -27,7 +27,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { ICommandService } from 'vs/platform/commands/common/commands'; import * as DOM from 'vs/base/browser/dom'; import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels'; -import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionBar, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; import { URI } from 'vs/base/common/uri'; import { dirname, basename } from 'vs/base/common/resources'; import { FileKind } from 'vs/platform/files/common/files'; @@ -1123,18 +1123,18 @@ class TreeMenus extends Disposable implements IDisposable { if (!this.contextKeyService) { return { primary: [], secondary: [] }; } - const contextKeyService = this.contextKeyService.createScoped(); - contextKeyService.createKey('view', this.id); - contextKeyService.createKey(context.key, context.value); + + const contextKeyService = this.contextKeyService.createOverlay([ + ['view', this.id], + [context.key, context.value] + ]); const menu = this.menuService.createMenu(menuId, contextKeyService); const primary: IAction[] = []; const secondary: IAction[] = []; const result = { primary, secondary }; createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result, g => /^inline/.test(g)); - menu.dispose(); - contextKeyService.dispose(); return result; } diff --git a/src/vs/workbench/browser/parts/views/viewPane.ts b/src/vs/workbench/browser/parts/views/viewPane.ts index 4df6b15d299..835620f11a9 100644 --- a/src/vs/workbench/browser/parts/views/viewPane.ts +++ b/src/vs/workbench/browser/parts/views/viewPane.ts @@ -11,8 +11,8 @@ import { attachButtonStyler, attachLinkStyler, attachProgressBarStyler } from 'v import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { after, append, $, trackFocus, EventType, addDisposableListener, createCSSRule, asCSSUrl } from 'vs/base/browser/dom'; import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { IAction, IActionViewItem } from 'vs/base/common/actions'; -import { ActionsOrientation, prepareActions } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IAction } from 'vs/base/common/actions'; +import { ActionsOrientation, IActionViewItem, prepareActions } from 'vs/base/browser/ui/actionbar/actionbar'; import { Registry } from 'vs/platform/registry/common/platform'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -80,16 +80,12 @@ class ViewWelcomeController { return visibleItems.map(v => v.descriptor); } - private contextKeyService: IContextKeyService; private disposables = new DisposableStore(); constructor( private id: string, - @IContextKeyService contextKeyService: IContextKeyService, + @IContextKeyService private contextKeyService: IContextKeyService, ) { - this.contextKeyService = contextKeyService.createScoped(); - this.disposables.add(this.contextKeyService); - contextKeyService.onDidChangeContext(this.onDidChangeContext, this, this.disposables); Event.filter(viewsRegistry.onDidChangeViewWelcomeContent, id => id === this.id)(this.onDidChangeViewWelcomeContent, this, this.disposables); this.onDidChangeViewWelcomeContent(); diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index f4b875cd080..32db51de439 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -11,7 +11,7 @@ import { attachStyler, IColorMapping } from 'vs/platform/theme/common/styler'; import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND, SIDE_BAR_SECTION_HEADER_FOREGROUND, SIDE_BAR_SECTION_HEADER_BACKGROUND, SIDE_BAR_SECTION_HEADER_BORDER, PANEL_SECTION_HEADER_FOREGROUND, PANEL_SECTION_HEADER_BACKGROUND, PANEL_SECTION_HEADER_BORDER, PANEL_SECTION_DRAG_AND_DROP_BACKGROUND, PANEL_SECTION_BORDER } from 'vs/workbench/common/theme'; import { EventType, Dimension, addDisposableListener, isAncestor } from 'vs/base/browser/dom'; import { IDisposable, combinedDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; -import { IAction, IActionViewItem, Separator } from 'vs/base/common/actions'; +import { IAction } from 'vs/base/common/actions'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService, Themable } from 'vs/platform/theme/common/themeService'; @@ -28,7 +28,7 @@ import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewl import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { Component } from 'vs/workbench/common/component'; -import { registerAction2, Action2, IAction2Options, MenuId, MenuRegistry, ISubmenuItem, SubmenuItemAction, IMenuService } from 'vs/platform/actions/common/actions'; +import { registerAction2, Action2, IAction2Options, MenuId, MenuRegistry, ISubmenuItem, IMenuService } from 'vs/platform/actions/common/actions'; import { CompositeDragAndDropObserver, DragAndDropObserver, toggleDropEffect } from 'vs/workbench/browser/dnd'; import { Orientation } from 'vs/base/browser/ui/sash/sash'; import { RunOnceScheduler } from 'vs/base/common/async'; @@ -37,6 +37,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { ViewPane } from 'vs/workbench/browser/parts/views/viewPane'; import { CompositeMenuActions } from 'vs/workbench/browser/menuActions'; import { createActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; export const ViewsSubMenu = new MenuId('Views'); MenuRegistry.appendMenuItem(MenuId.ViewContainerTitle, { @@ -354,7 +355,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { return assertIsDefined(this.paneview).onDidSashChange; } - protected get panes(): ViewPane[] { + get panes(): ViewPane[] { return this.paneItems.map(i => i.pane); } @@ -366,7 +367,10 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { return this.paneItems.length; } - private menuActions!: ViewContainerMenuActions; + private _menuActions?: ViewContainerMenuActions; + get menuActions(): CompositeMenuActions | undefined { + return this._menuActions; + } constructor( id: string, @@ -405,8 +409,8 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { this._register(this.paneview.onDidDrop(({ from, to }) => this.movePane(from as ViewPane, to as ViewPane))); this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(e)))); - this.menuActions = this._register(this.instantiationService.createInstance(ViewContainerMenuActions, this.paneview.element, this.viewContainer)); - this._register(this.menuActions.onDidChange(() => this.updateTitleArea())); + this._menuActions = this._register(this.instantiationService.createInstance(ViewContainerMenuActions, this.paneview.element, this.viewContainer)); + this._register(this._menuActions.onDidChange(() => this.updateTitleArea())); let overlay: ViewPaneDropOverlay | undefined; const getOverlayBounds: () => BoundingRect = () => { @@ -571,53 +575,10 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { let anchor: { x: number, y: number; } = { x: event.posx, y: event.posy }; this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => [...this.getContextMenuActions()] + getActions: () => this.menuActions?.getContextMenuActions() ?? [] }); } - getContextMenuActions(): ReadonlyArray { - return this.menuActions.getContextMenuActions(); - } - - getActions(): IAction[] { - const result = []; - result.push(...this.menuActions.getPrimaryActions()); - if (this.isViewMergedWithContainer()) { - result.push(...this.paneItems[0].pane.menuActions.getPrimaryActions()); - } - return result; - } - - getSecondaryActions(): IAction[] { - const viewPaneActions = this.isViewMergedWithContainer() ? this.paneItems[0].pane.menuActions.getSecondaryActions() : []; - let menuActions = this.menuActions.getSecondaryActions(); - - const viewsSubmenuActionIndex = menuActions.findIndex(action => action instanceof SubmenuItemAction && action.item.submenu === ViewsSubMenu); - if (viewsSubmenuActionIndex !== -1) { - const viewsSubmenuAction = menuActions[viewsSubmenuActionIndex]; - if (viewsSubmenuAction.actions.some(({ enabled }) => enabled)) { - if (menuActions.length === 1 && viewPaneActions.length === 0) { - menuActions = viewsSubmenuAction.actions.slice(); - } else if (viewsSubmenuActionIndex !== 0) { - menuActions = [viewsSubmenuAction, ...menuActions.slice(0, viewsSubmenuActionIndex), ...menuActions.slice(viewsSubmenuActionIndex + 1)]; - } - } else { - // Remove views submenu if none of the actions are enabled - menuActions.splice(viewsSubmenuActionIndex, 1); - } - } - - if (menuActions.length && viewPaneActions.length) { - return [ - ...menuActions, - new Separator(), - ...viewPaneActions - ]; - } - - return menuActions.length ? menuActions : viewPaneActions; - } - getActionsContext(): unknown { return undefined; } diff --git a/src/vs/workbench/browser/style.ts b/src/vs/workbench/browser/style.ts index 4f1ecbcd076..5028f89cc5a 100644 --- a/src/vs/workbench/browser/style.ts +++ b/src/vs/workbench/browser/style.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/style'; - import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { iconForeground, foreground, selectionBackground, focusBorder, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, listHighlightForeground, inputPlaceholderForeground } from 'vs/platform/theme/common/colorRegistry'; import { WORKBENCH_BACKGROUND, TITLE_BAR_ACTIVE_BACKGROUND } from 'vs/workbench/common/theme'; 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/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index e1353541362..99f76cbf138 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Registry } from 'vs/platform/registry/common/platform'; -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { isMacintosh, isWindows, isLinux, isWeb, isNative } from 'vs/base/common/platform'; import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; @@ -22,53 +22,53 @@ import { isStandalone } from 'vs/base/browser/browser'; type: 'string', enum: ['default', 'large'], enumDescriptions: [ - nls.localize('workbench.editor.titleScrollbarSizing.default', "The default size."), - nls.localize('workbench.editor.titleScrollbarSizing.large', "Increases the size, so it can be grabbed more easily with the mouse") + localize('workbench.editor.titleScrollbarSizing.default', "The default size."), + localize('workbench.editor.titleScrollbarSizing.large', "Increases the size, so it can be grabbed more easily with the mouse") ], - description: nls.localize('tabScrollbarHeight', "Controls the height of the scrollbars used for tabs and breadcrumbs in the editor title area."), + description: localize('tabScrollbarHeight', "Controls the height of the scrollbars used for tabs and breadcrumbs in the editor title area."), default: 'default', }, 'workbench.editor.showTabs': { 'type': 'boolean', - 'description': nls.localize('showEditorTabs', "Controls whether opened editors should show in tabs or not."), + 'description': localize('showEditorTabs', "Controls whether opened editors should show in tabs or not."), 'default': true }, 'workbench.editor.wrapTabs': { 'type': 'boolean', - 'markdownDescription': nls.localize('wrapTabs', "Controls whether tabs should be wrapped over multiple lines when exceeding available space or whether a scrollbar should appear instead. This value is ignored when `#workbench.editor.showTabs#` is disabled."), + 'markdownDescription': localize('wrapTabs', "Controls whether tabs should be wrapped over multiple lines when exceeding available space or whether a scrollbar should appear instead. This value is ignored when `#workbench.editor.showTabs#` is disabled."), 'default': false }, 'workbench.editor.scrollToSwitchTabs': { 'type': 'boolean', - 'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'scrollToSwitchTabs' }, "Controls whether scrolling over tabs will open them or not. By default tabs will only reveal upon scrolling, but not open. You can press and hold the Shift-key while scrolling to change this behaviour for that duration. This value is ignored when `#workbench.editor.showTabs#` is disabled."), + 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'scrollToSwitchTabs' }, "Controls whether scrolling over tabs will open them or not. By default tabs will only reveal upon scrolling, but not open. You can press and hold the Shift-key while scrolling to change this behaviour for that duration. This value is ignored when `#workbench.editor.showTabs#` is disabled."), 'default': false }, 'workbench.editor.highlightModifiedTabs': { 'type': 'boolean', - 'markdownDescription': nls.localize('highlightModifiedTabs', "Controls whether a top border is drawn on modified (dirty) editor tabs or not. This value is ignored when `#workbench.editor.showTabs#` is disabled."), + 'markdownDescription': localize('highlightModifiedTabs', "Controls whether a top border is drawn on modified (dirty) editor tabs or not. This value is ignored when `#workbench.editor.showTabs#` is disabled."), 'default': false }, 'workbench.editor.decorations.badges': { 'type': 'boolean', - 'markdownDescription': nls.localize('decorations.badges', "Controls whether editor file decorations should use badges."), + 'markdownDescription': localize('decorations.badges', "Controls whether editor file decorations should use badges."), 'default': false }, 'workbench.editor.decorations.colors': { 'type': 'boolean', - 'markdownDescription': nls.localize('decorations.colors', "Controls whether editor file decorations should use colors."), + 'markdownDescription': localize('decorations.colors', "Controls whether editor file decorations should use colors."), 'default': false }, 'workbench.editor.labelFormat': { 'type': 'string', 'enum': ['default', 'short', 'medium', 'long'], 'enumDescriptions': [ - nls.localize('workbench.editor.labelFormat.default', "Show the name of the file. When tabs are enabled and two files have the same name in one group the distinguishing sections of each file's path are added. When tabs are disabled, the path relative to the workspace folder is shown if the editor is active."), - nls.localize('workbench.editor.labelFormat.short', "Show the name of the file followed by its directory name."), - nls.localize('workbench.editor.labelFormat.medium', "Show the name of the file followed by its path relative to the workspace folder."), - nls.localize('workbench.editor.labelFormat.long', "Show the name of the file followed by its absolute path.") + localize('workbench.editor.labelFormat.default', "Show the name of the file. When tabs are enabled and two files have the same name in one group the distinguishing sections of each file's path are added. When tabs are disabled, the path relative to the workspace folder is shown if the editor is active."), + localize('workbench.editor.labelFormat.short', "Show the name of the file followed by its directory name."), + localize('workbench.editor.labelFormat.medium', "Show the name of the file followed by its path relative to the workspace folder."), + localize('workbench.editor.labelFormat.long', "Show the name of the file followed by its absolute path.") ], 'default': 'default', - 'description': nls.localize({ + 'description': localize({ comment: ['This is the description for a setting. Values surrounded by parenthesis are not to be translated.'], key: 'tabDescription' }, "Controls the format of the label for an editor."), @@ -77,11 +77,11 @@ import { isStandalone } from 'vs/base/browser/browser'; 'type': 'string', 'enum': ['content', 'name'], 'enumDescriptions': [ - nls.localize('workbench.editor.untitled.labelFormat.content', "The name of the untitled file is derived from the contents of its first line unless it has an associated file path. It will fallback to the name in case the line is empty or contains no word characters."), - nls.localize('workbench.editor.untitled.labelFormat.name', "The name of the untitled file is not derived from the contents of the file."), + localize('workbench.editor.untitled.labelFormat.content', "The name of the untitled file is derived from the contents of its first line unless it has an associated file path. It will fallback to the name in case the line is empty or contains no word characters."), + localize('workbench.editor.untitled.labelFormat.name', "The name of the untitled file is not derived from the contents of the file."), ], 'default': 'content', - 'description': nls.localize({ + 'description': localize({ comment: ['This is the description for a setting. Values surrounded by parenthesis are not to be translated.'], key: 'untitledLabelFormat' }, "Controls the format of the label for an untitled editor."), @@ -90,222 +90,222 @@ import { isStandalone } from 'vs/base/browser/browser'; 'type': 'string', 'enum': ['left', 'right', 'off'], 'default': 'right', - 'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'editorTabCloseButton' }, "Controls the position of the editor's tabs close buttons, or disables them when set to 'off'. This value is ignored when `#workbench.editor.showTabs#` is disabled.") + 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'editorTabCloseButton' }, "Controls the position of the editor's tabs close buttons, or disables them when set to 'off'. This value is ignored when `#workbench.editor.showTabs#` is disabled.") }, 'workbench.editor.tabSizing': { 'type': 'string', 'enum': ['fit', 'shrink'], 'default': 'fit', 'enumDescriptions': [ - nls.localize('workbench.editor.tabSizing.fit', "Always keep tabs large enough to show the full editor label."), - nls.localize('workbench.editor.tabSizing.shrink', "Allow tabs to get smaller when the available space is not enough to show all tabs at once.") + localize('workbench.editor.tabSizing.fit', "Always keep tabs large enough to show the full editor label."), + localize('workbench.editor.tabSizing.shrink', "Allow tabs to get smaller when the available space is not enough to show all tabs at once.") ], - 'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'tabSizing' }, "Controls the sizing of editor tabs. This value is ignored when `#workbench.editor.showTabs#` is disabled.") + 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'tabSizing' }, "Controls the sizing of editor tabs. This value is ignored when `#workbench.editor.showTabs#` is disabled.") }, 'workbench.editor.pinnedTabSizing': { 'type': 'string', 'enum': ['normal', 'compact', 'shrink'], 'default': 'normal', 'enumDescriptions': [ - nls.localize('workbench.editor.pinnedTabSizing.normal', "A pinned tab inherits the look of non pinned tabs."), - nls.localize('workbench.editor.pinnedTabSizing.compact', "A pinned tab will show in a compact form with only icon or first letter of the editor name."), - nls.localize('workbench.editor.pinnedTabSizing.shrink', "A pinned tab shrinks to a compact fixed size showing parts of the editor name.") + localize('workbench.editor.pinnedTabSizing.normal', "A pinned tab inherits the look of non pinned tabs."), + localize('workbench.editor.pinnedTabSizing.compact', "A pinned tab will show in a compact form with only icon or first letter of the editor name."), + localize('workbench.editor.pinnedTabSizing.shrink', "A pinned tab shrinks to a compact fixed size showing parts of the editor name.") ], - 'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'pinnedTabSizing' }, "Controls the sizing of pinned editor tabs. Pinned tabs are sorted to the beginning of all opened tabs and typically do not close until unpinned. This value is ignored when `#workbench.editor.showTabs#` is disabled.") + 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'pinnedTabSizing' }, "Controls the sizing of pinned editor tabs. Pinned tabs are sorted to the beginning of all opened tabs and typically do not close until unpinned. This value is ignored when `#workbench.editor.showTabs#` is disabled.") }, 'workbench.editor.splitSizing': { 'type': 'string', 'enum': ['distribute', 'split'], 'default': 'distribute', 'enumDescriptions': [ - nls.localize('workbench.editor.splitSizingDistribute', "Splits all the editor groups to equal parts."), - nls.localize('workbench.editor.splitSizingSplit', "Splits the active editor group to equal parts.") + localize('workbench.editor.splitSizingDistribute', "Splits all the editor groups to equal parts."), + localize('workbench.editor.splitSizingSplit', "Splits the active editor group to equal parts.") ], - 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'splitSizing' }, "Controls the sizing of editor groups when splitting them.") + 'description': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'splitSizing' }, "Controls the sizing of editor groups when splitting them.") }, 'workbench.editor.splitOnDragAndDrop': { 'type': 'boolean', 'default': true, - 'description': nls.localize('splitOnDragAndDrop', "Controls if editor groups can be split from drag and drop operations by dropping an editor or file on the edges of the editor area.") + 'description': localize('splitOnDragAndDrop', "Controls if editor groups can be split from drag and drop operations by dropping an editor or file on the edges of the editor area.") }, 'workbench.editor.focusRecentEditorAfterClose': { 'type': 'boolean', - 'description': nls.localize('focusRecentEditorAfterClose', "Controls whether tabs are closed in most recently used order or from left to right."), + 'description': localize('focusRecentEditorAfterClose', "Controls whether tabs are closed in most recently used order or from left to right."), 'default': true }, 'workbench.editor.showIcons': { 'type': 'boolean', - 'description': nls.localize('showIcons', "Controls whether opened editors should show with an icon or not. This requires a file icon theme to be enabled as well."), + 'description': localize('showIcons', "Controls whether opened editors should show with an icon or not. This requires a file icon theme to be enabled as well."), 'default': true }, 'workbench.editor.enablePreview': { 'type': 'boolean', - 'description': nls.localize('enablePreview', "Controls whether opened editors show as preview. Preview editors do not keep open and are reused until explicitly set to be kept open (e.g. via double click or editing) and show up with an italic font style."), + 'description': localize('enablePreview', "Controls whether opened editors show as preview. Preview editors do not keep open and are reused until explicitly set to be kept open (e.g. via double click or editing) and show up with an italic font style."), 'default': true }, 'workbench.editor.enablePreviewFromQuickOpen': { 'type': 'boolean', - 'markdownDescription': nls.localize('enablePreviewFromQuickOpen', "Controls whether editors opened from Quick Open show as preview. Preview editors do not keep open and are reused until explicitly set to be kept open (e.g. via double click or editing). This value is ignored when `#workbench.editor.enablePreview#` is disabled."), + 'markdownDescription': localize('enablePreviewFromQuickOpen', "Controls whether editors opened from Quick Open show as preview. Preview editors do not keep open and are reused until explicitly set to be kept open (e.g. via double click or editing). This value is ignored when `#workbench.editor.enablePreview#` is disabled."), 'default': false }, 'workbench.editor.enablePreviewFromCodeNavigation': { 'type': 'boolean', - 'markdownDescription': nls.localize('enablePreviewFromCodeNavigation', "Controls whether editors remain in preview when a code navigation is started from them. Preview editors do not keep open and are reused until explicitly set to be kept open (e.g. via double click or editing). This value is ignored when `#workbench.editor.enablePreview#` is disabled."), + 'markdownDescription': localize('enablePreviewFromCodeNavigation', "Controls whether editors remain in preview when a code navigation is started from them. Preview editors do not keep open and are reused until explicitly set to be kept open (e.g. via double click or editing). This value is ignored when `#workbench.editor.enablePreview#` is disabled."), 'default': false }, 'workbench.editor.closeOnFileDelete': { 'type': 'boolean', - 'description': nls.localize('closeOnFileDelete', "Controls whether editors showing a file that was opened during the session should close automatically when getting deleted or renamed by some other process. Disabling this will keep the editor open on such an event. Note that deleting from within the application will always close the editor and that dirty files will never close to preserve your data."), + 'description': localize('closeOnFileDelete', "Controls whether editors showing a file that was opened during the session should close automatically when getting deleted or renamed by some other process. Disabling this will keep the editor open on such an event. Note that deleting from within the application will always close the editor and that dirty files will never close to preserve your data."), 'default': false }, 'workbench.editor.openPositioning': { 'type': 'string', 'enum': ['left', 'right', 'first', 'last'], 'default': 'right', - 'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'editorOpenPositioning' }, "Controls where editors open. Select `left` or `right` to open editors to the left or right of the currently active one. Select `first` or `last` to open editors independently from the currently active one.") + 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'editorOpenPositioning' }, "Controls where editors open. Select `left` or `right` to open editors to the left or right of the currently active one. Select `first` or `last` to open editors independently from the currently active one.") }, 'workbench.editor.openSideBySideDirection': { 'type': 'string', 'enum': ['right', 'down'], 'default': 'right', - 'markdownDescription': nls.localize('sideBySideDirection', "Controls the default direction of editors that are opened side by side (e.g. from the explorer). By default, editors will open on the right hand side of the currently active one. If changed to `down`, the editors will open below the currently active one.") + 'markdownDescription': localize('sideBySideDirection', "Controls the default direction of editors that are opened side by side (e.g. from the explorer). By default, editors will open on the right hand side of the currently active one. If changed to `down`, the editors will open below the currently active one.") }, 'workbench.editor.closeEmptyGroups': { 'type': 'boolean', - 'description': nls.localize('closeEmptyGroups', "Controls the behavior of empty editor groups when the last tab in the group is closed. When enabled, empty groups will automatically close. When disabled, empty groups will remain part of the grid."), + 'description': localize('closeEmptyGroups', "Controls the behavior of empty editor groups when the last tab in the group is closed. When enabled, empty groups will automatically close. When disabled, empty groups will remain part of the grid."), 'default': true }, 'workbench.editor.revealIfOpen': { 'type': 'boolean', - 'description': nls.localize('revealIfOpen', "Controls whether an editor is revealed in any of the visible groups if opened. If disabled, an editor will prefer to open in the currently active editor group. If enabled, an already opened editor will be revealed instead of opened again in the currently active editor group. Note that there are some cases where this setting is ignored, e.g. when forcing an editor to open in a specific group or to the side of the currently active group."), + 'description': localize('revealIfOpen', "Controls whether an editor is revealed in any of the visible groups if opened. If disabled, an editor will prefer to open in the currently active editor group. If enabled, an already opened editor will be revealed instead of opened again in the currently active editor group. Note that there are some cases where this setting is ignored, e.g. when forcing an editor to open in a specific group or to the side of the currently active group."), 'default': false }, 'workbench.editor.mouseBackForwardToNavigate': { 'type': 'boolean', - 'description': nls.localize('mouseBackForwardToNavigate', "Navigate between open files using mouse buttons four and five if provided."), + 'description': localize('mouseBackForwardToNavigate', "Navigate between open files using mouse buttons four and five if provided."), 'default': true }, 'workbench.editor.restoreViewState': { 'type': 'boolean', - 'description': nls.localize('restoreViewState', "Restores the last view state (e.g. scroll position) when re-opening textual editors after they have been closed."), + 'description': localize('restoreViewState', "Restores the last view state (e.g. scroll position) when re-opening textual editors after they have been closed."), 'default': true, 'scope': ConfigurationScope.LANGUAGE_OVERRIDABLE }, 'workbench.editor.centeredLayoutAutoResize': { 'type': 'boolean', 'default': true, - 'description': nls.localize('centeredLayoutAutoResize', "Controls if the centered layout should automatically resize to maximum width when more than one group is open. Once only one group is open it will resize back to the original centered width.") + 'description': localize('centeredLayoutAutoResize', "Controls if the centered layout should automatically resize to maximum width when more than one group is open. Once only one group is open it will resize back to the original centered width.") }, 'workbench.editor.limit.enabled': { 'type': 'boolean', 'default': false, - 'description': nls.localize('limitEditorsEnablement', "Controls if the number of opened editors should be limited or not. When enabled, less recently used editors that are not dirty will close to make space for newly opening editors.") + 'description': localize('limitEditorsEnablement', "Controls if the number of opened editors should be limited or not. When enabled, less recently used editors that are not dirty will close to make space for newly opening editors.") }, 'workbench.editor.limit.value': { 'type': 'number', 'default': 10, 'exclusiveMinimum': 0, - 'markdownDescription': nls.localize('limitEditorsMaximum', "Controls the maximum number of opened editors. Use the `#workbench.editor.limit.perEditorGroup#` setting to control this limit per editor group or across all groups.") + 'markdownDescription': localize('limitEditorsMaximum', "Controls the maximum number of opened editors. Use the `#workbench.editor.limit.perEditorGroup#` setting to control this limit per editor group or across all groups.") }, 'workbench.editor.limit.perEditorGroup': { 'type': 'boolean', 'default': false, - 'description': nls.localize('perEditorGroup', "Controls if the limit of maximum opened editors should apply per editor group or across all editor groups.") + 'description': localize('perEditorGroup', "Controls if the limit of maximum opened editors should apply per editor group or across all editor groups.") }, 'workbench.commandPalette.history': { 'type': 'number', - 'description': nls.localize('commandHistory', "Controls the number of recently used commands to keep in history for the command palette. Set to 0 to disable command history."), + 'description': localize('commandHistory', "Controls the number of recently used commands to keep in history for the command palette. Set to 0 to disable command history."), 'default': 50 }, 'workbench.commandPalette.preserveInput': { 'type': 'boolean', - 'description': nls.localize('preserveInput', "Controls whether the last typed input to the command palette should be restored when opening it the next time."), + 'description': localize('preserveInput', "Controls whether the last typed input to the command palette should be restored when opening it the next time."), 'default': false }, 'workbench.quickOpen.closeOnFocusLost': { 'type': 'boolean', - 'description': nls.localize('closeOnFocusLost', "Controls whether Quick Open should close automatically once it loses focus."), + 'description': localize('closeOnFocusLost', "Controls whether Quick Open should close automatically once it loses focus."), 'default': true }, 'workbench.quickOpen.preserveInput': { 'type': 'boolean', - 'description': nls.localize('workbench.quickOpen.preserveInput', "Controls whether the last typed input to Quick Open should be restored when opening it the next time."), + 'description': localize('workbench.quickOpen.preserveInput', "Controls whether the last typed input to Quick Open should be restored when opening it the next time."), 'default': false }, 'workbench.settings.openDefaultSettings': { 'type': 'boolean', - 'description': nls.localize('openDefaultSettings', "Controls whether opening settings also opens an editor showing all default settings."), + 'description': localize('openDefaultSettings', "Controls whether opening settings also opens an editor showing all default settings."), 'default': false }, 'workbench.settings.useSplitJSON': { 'type': 'boolean', - 'markdownDescription': nls.localize('useSplitJSON', "Controls whether to use the split JSON editor when editing settings as JSON."), + 'markdownDescription': localize('useSplitJSON', "Controls whether to use the split JSON editor when editing settings as JSON."), 'default': false }, 'workbench.settings.openDefaultKeybindings': { 'type': 'boolean', - 'description': nls.localize('openDefaultKeybindings', "Controls whether opening keybinding settings also opens an editor showing all default keybindings."), + 'description': localize('openDefaultKeybindings', "Controls whether opening keybinding settings also opens an editor showing all default keybindings."), 'default': false }, 'workbench.sideBar.location': { 'type': 'string', 'enum': ['left', 'right'], 'default': 'left', - 'description': nls.localize('sideBarLocation', "Controls the location of the sidebar and activity bar. They can either show on the left or right of the workbench.") + 'description': localize('sideBarLocation', "Controls the location of the sidebar and activity bar. They can either show on the left or right of the workbench.") }, 'workbench.panel.defaultLocation': { 'type': 'string', 'enum': ['left', 'bottom', 'right'], 'default': 'bottom', - 'description': nls.localize('panelDefaultLocation', "Controls the default location of the panel (terminal, debug console, output, problems). It can either show at the bottom, right, or left of the workbench.") + 'description': localize('panelDefaultLocation', "Controls the default location of the panel (terminal, debug console, output, problems). It can either show at the bottom, right, or left of the workbench.") }, 'workbench.panel.opensMaximized': { 'type': 'string', 'enum': ['always', 'never', 'preserve'], 'default': 'preserve', - 'description': nls.localize('panelOpensMaximized', "Controls whether the panel opens maximized. It can either always open maximized, never open maximized, or open to the last state it was in before being closed."), + 'description': localize('panelOpensMaximized', "Controls whether the panel opens maximized. It can either always open maximized, never open maximized, or open to the last state it was in before being closed."), 'enumDescriptions': [ - nls.localize('workbench.panel.opensMaximized.always', "Always maximize the panel when opening it."), - nls.localize('workbench.panel.opensMaximized.never', "Never maximize the panel when opening it. The panel will open un-maximized."), - nls.localize('workbench.panel.opensMaximized.preserve', "Open the panel to the state that it was in, before it was closed.") + localize('workbench.panel.opensMaximized.always', "Always maximize the panel when opening it."), + localize('workbench.panel.opensMaximized.never', "Never maximize the panel when opening it. The panel will open un-maximized."), + localize('workbench.panel.opensMaximized.preserve', "Open the panel to the state that it was in, before it was closed.") ] }, 'workbench.statusBar.visible': { 'type': 'boolean', 'default': true, - 'description': nls.localize('statusBarVisibility', "Controls the visibility of the status bar at the bottom of the workbench.") + 'description': localize('statusBarVisibility', "Controls the visibility of the status bar at the bottom of the workbench.") }, 'workbench.activityBar.visible': { 'type': 'boolean', 'default': true, - 'description': nls.localize('activityBarVisibility', "Controls the visibility of the activity bar in the workbench.") + 'description': localize('activityBarVisibility', "Controls the visibility of the activity bar in the workbench.") }, 'workbench.activityBar.iconClickBehavior': { 'type': 'string', 'enum': ['toggle', 'focus'], 'default': 'toggle', - 'description': nls.localize('activityBarIconClickBehavior', "Controls the behavior of clicking an activity bar icon in the workbench."), + 'description': localize('activityBarIconClickBehavior', "Controls the behavior of clicking an activity bar icon in the workbench."), 'enumDescriptions': [ - nls.localize('workbench.activityBar.iconClickBehavior.toggle', "Hide the side bar if the clicked item is already visible."), - nls.localize('workbench.activityBar.iconClickBehavior.focus', "Focus side bar if the clicked item is already visible.") + localize('workbench.activityBar.iconClickBehavior.toggle', "Hide the side bar if the clicked item is already visible."), + localize('workbench.activityBar.iconClickBehavior.focus', "Focus side bar if the clicked item is already visible.") ] }, 'workbench.view.alwaysShowHeaderActions': { 'type': 'boolean', 'default': false, - 'description': nls.localize('viewVisibility', "Controls the visibility of view header actions. View header actions may either be always visible, or only visible when that view is focused or hovered over.") + 'description': localize('viewVisibility', "Controls the visibility of view header actions. View header actions may either be always visible, or only visible when that view is focused or hovered over.") }, 'workbench.fontAliasing': { 'type': 'string', 'enum': ['default', 'antialiased', 'none', 'auto'], 'default': 'default', 'description': - nls.localize('fontAliasing', "Controls font aliasing method in the workbench."), + localize('fontAliasing', "Controls font aliasing method in the workbench."), 'enumDescriptions': [ - nls.localize('workbench.fontAliasing.default', "Sub-pixel font smoothing. On most non-retina displays this will give the sharpest text."), - nls.localize('workbench.fontAliasing.antialiased', "Smooth the font on the level of the pixel, as opposed to the subpixel. Can make the font appear lighter overall."), - nls.localize('workbench.fontAliasing.none', "Disables font smoothing. Text will show with jagged sharp edges."), - nls.localize('workbench.fontAliasing.auto', "Applies `default` or `antialiased` automatically based on the DPI of displays.") + localize('workbench.fontAliasing.default', "Sub-pixel font smoothing. On most non-retina displays this will give the sharpest text."), + localize('workbench.fontAliasing.antialiased', "Smooth the font on the level of the pixel, as opposed to the subpixel. Can make the font appear lighter overall."), + localize('workbench.fontAliasing.none', "Disables font smoothing. Text will show with jagged sharp edges."), + localize('workbench.fontAliasing.auto', "Applies `default` or `antialiased` automatically based on the DPI of displays.") ], 'included': isMacintosh }, @@ -313,10 +313,10 @@ import { isStandalone } from 'vs/base/browser/browser'; 'type': 'string', 'enum': ['ui', 'json'], 'enumDescriptions': [ - nls.localize('settings.editor.ui', "Use the settings UI editor."), - nls.localize('settings.editor.json', "Use the JSON file editor."), + localize('settings.editor.ui', "Use the settings UI editor."), + localize('settings.editor.json', "Use the JSON file editor."), ], - 'description': nls.localize('settings.editor.desc', "Determines which settings editor to use by default."), + 'description': localize('settings.editor.desc', "Determines which settings editor to use by default."), 'default': 'ui', 'scope': ConfigurationScope.WINDOW } @@ -325,28 +325,28 @@ import { isStandalone } from 'vs/base/browser/browser'; // Window - let windowTitleDescription = nls.localize('windowTitle', "Controls the window title based on the active editor. Variables are substituted based on the context:"); + let windowTitleDescription = localize('windowTitle', "Controls the window title based on the active editor. Variables are substituted based on the context:"); windowTitleDescription += '\n- ' + [ - nls.localize('activeEditorShort', "`\${activeEditorShort}`: the file name (e.g. myFile.txt)."), - nls.localize('activeEditorMedium', "`\${activeEditorMedium}`: the path of the file relative to the workspace folder (e.g. myFolder/myFileFolder/myFile.txt)."), - nls.localize('activeEditorLong', "`\${activeEditorLong}`: the full path of the file (e.g. /Users/Development/myFolder/myFileFolder/myFile.txt)."), - nls.localize('activeFolderShort', "`\${activeFolderShort}`: the name of the folder the file is contained in (e.g. myFileFolder)."), - nls.localize('activeFolderMedium', "`\${activeFolderMedium}`: the path of the folder the file is contained in, relative to the workspace folder (e.g. myFolder/myFileFolder)."), - nls.localize('activeFolderLong', "`\${activeFolderLong}`: the full path of the folder the file is contained in (e.g. /Users/Development/myFolder/myFileFolder)."), - nls.localize('folderName', "`\${folderName}`: name of the workspace folder the file is contained in (e.g. myFolder)."), - nls.localize('folderPath', "`\${folderPath}`: file path of the workspace folder the file is contained in (e.g. /Users/Development/myFolder)."), - nls.localize('rootName', "`\${rootName}`: name of the opened workspace or folder (e.g. myFolder or myWorkspace)."), - nls.localize('rootPath', "`\${rootPath}`: file path of the opened workspace or folder (e.g. /Users/Development/myWorkspace)."), - nls.localize('appName', "`\${appName}`: e.g. VS Code."), - nls.localize('remoteName', "`\${remoteName}`: e.g. SSH"), - nls.localize('dirty', "`\${dirty}`: a dirty indicator if the active editor is dirty."), - nls.localize('separator', "`\${separator}`: a conditional separator (\" - \") that only shows when surrounded by variables with values or static text.") + localize('activeEditorShort', "`\${activeEditorShort}`: the file name (e.g. myFile.txt)."), + localize('activeEditorMedium', "`\${activeEditorMedium}`: the path of the file relative to the workspace folder (e.g. myFolder/myFileFolder/myFile.txt)."), + localize('activeEditorLong', "`\${activeEditorLong}`: the full path of the file (e.g. /Users/Development/myFolder/myFileFolder/myFile.txt)."), + localize('activeFolderShort', "`\${activeFolderShort}`: the name of the folder the file is contained in (e.g. myFileFolder)."), + localize('activeFolderMedium', "`\${activeFolderMedium}`: the path of the folder the file is contained in, relative to the workspace folder (e.g. myFolder/myFileFolder)."), + localize('activeFolderLong', "`\${activeFolderLong}`: the full path of the folder the file is contained in (e.g. /Users/Development/myFolder/myFileFolder)."), + localize('folderName', "`\${folderName}`: name of the workspace folder the file is contained in (e.g. myFolder)."), + localize('folderPath', "`\${folderPath}`: file path of the workspace folder the file is contained in (e.g. /Users/Development/myFolder)."), + localize('rootName', "`\${rootName}`: name of the opened workspace or folder (e.g. myFolder or myWorkspace)."), + localize('rootPath', "`\${rootPath}`: file path of the opened workspace or folder (e.g. /Users/Development/myWorkspace)."), + localize('appName', "`\${appName}`: e.g. VS Code."), + localize('remoteName', "`\${remoteName}`: e.g. SSH"), + localize('dirty', "`\${dirty}`: a dirty indicator if the active editor is dirty."), + localize('separator', "`\${separator}`: a conditional separator (\" - \") that only shows when surrounded by variables with values or static text.") ].join('\n- '); // intentionally concatenated to not produce a string that is too long for translations registry.registerConfiguration({ 'id': 'window', 'order': 8, - 'title': nls.localize('windowConfigurationTitle', "Window"), + 'title': localize('windowConfigurationTitle', "Window"), 'type': 'object', 'properties': { 'window.title': { @@ -368,80 +368,80 @@ import { isStandalone } from 'vs/base/browser/browser'; 'window.titleSeparator': { 'type': 'string', 'default': isMacintosh ? ' — ' : ' - ', - 'markdownDescription': nls.localize("window.titleSeparator", "Separator used by `window.title`.") + 'markdownDescription': localize("window.titleSeparator", "Separator used by `window.title`.") }, 'window.menuBarVisibility': { 'type': 'string', 'enum': ['classic', 'visible', 'toggle', 'hidden', 'compact'], 'markdownEnumDescriptions': [ - nls.localize('window.menuBarVisibility.classic', "Menu is displayed at the top of the window and only hidden in full screen mode."), - nls.localize('window.menuBarVisibility.visible', "Menu is always visible at the top of the window even in full screen mode."), + localize('window.menuBarVisibility.classic', "Menu is displayed at the top of the window and only hidden in full screen mode."), + localize('window.menuBarVisibility.visible', "Menu is always visible at the top of the window even in full screen mode."), isMacintosh ? - nls.localize('window.menuBarVisibility.toggle.mac', "Menu is hidden but can be displayed at the top of the window by executing the `Focus Application Menu` command.") : - nls.localize('window.menuBarVisibility.toggle', "Menu is hidden but can be displayed at the top of the window via the Alt key."), - nls.localize('window.menuBarVisibility.hidden', "Menu is always hidden."), - nls.localize('window.menuBarVisibility.compact', "Menu is displayed as a compact button in the sidebar. This value is ignored when `#window.titleBarStyle#` is `native`.") + localize('window.menuBarVisibility.toggle.mac', "Menu is hidden but can be displayed at the top of the window by executing the `Focus Application Menu` command.") : + localize('window.menuBarVisibility.toggle', "Menu is hidden but can be displayed at the top of the window via the Alt key."), + localize('window.menuBarVisibility.hidden', "Menu is always hidden."), + localize('window.menuBarVisibility.compact', "Menu is displayed as a compact button in the sidebar. This value is ignored when `#window.titleBarStyle#` is `native`.") ], 'default': isWeb ? 'compact' : 'classic', 'scope': ConfigurationScope.APPLICATION, 'markdownDescription': isMacintosh ? - nls.localize('menuBarVisibility.mac', "Control the visibility of the menu bar. A setting of 'toggle' means that the menu bar is hidden and executing `Focus Application Menu` will show it. A setting of 'compact' will move the menu into the sidebar.") : - nls.localize('menuBarVisibility', "Control the visibility of the menu bar. A setting of 'toggle' means that the menu bar is hidden and a single press of the Alt key will show it. A setting of 'compact' will move the menu into the sidebar."), + localize('menuBarVisibility.mac', "Control the visibility of the menu bar. A setting of 'toggle' means that the menu bar is hidden and executing `Focus Application Menu` will show it. A setting of 'compact' will move the menu into the sidebar.") : + localize('menuBarVisibility', "Control the visibility of the menu bar. A setting of 'toggle' means that the menu bar is hidden and a single press of the Alt key will show it. A setting of 'compact' will move the menu into the sidebar."), 'included': isWindows || isLinux || isWeb }, 'window.enableMenuBarMnemonics': { 'type': 'boolean', 'default': true, 'scope': ConfigurationScope.APPLICATION, - 'description': nls.localize('enableMenuBarMnemonics', "Controls whether the main menus can be opened via Alt-key shortcuts. Disabling mnemonics allows to bind these Alt-key shortcuts to editor commands instead."), + 'description': localize('enableMenuBarMnemonics', "Controls whether the main menus can be opened via Alt-key shortcuts. Disabling mnemonics allows to bind these Alt-key shortcuts to editor commands instead."), 'included': isWindows || isLinux }, 'window.customMenuBarAltFocus': { 'type': 'boolean', 'default': true, 'scope': ConfigurationScope.APPLICATION, - 'markdownDescription': nls.localize('customMenuBarAltFocus', "Controls whether the menu bar will be focused by pressing the Alt-key. This setting has no effect on toggling the menu bar with the Alt-key."), + 'markdownDescription': localize('customMenuBarAltFocus', "Controls whether the menu bar will be focused by pressing the Alt-key. This setting has no effect on toggling the menu bar with the Alt-key."), 'included': isWindows || isLinux }, 'window.openFilesInNewWindow': { 'type': 'string', 'enum': ['on', 'off', 'default'], 'enumDescriptions': [ - nls.localize('window.openFilesInNewWindow.on', "Files will open in a new window."), - nls.localize('window.openFilesInNewWindow.off', "Files will open in the window with the files' folder open or the last active window."), + localize('window.openFilesInNewWindow.on', "Files will open in a new window."), + localize('window.openFilesInNewWindow.off', "Files will open in the window with the files' folder open or the last active window."), isMacintosh ? - nls.localize('window.openFilesInNewWindow.defaultMac', "Files will open in the window with the files' folder open or the last active window unless opened via the Dock or from Finder.") : - nls.localize('window.openFilesInNewWindow.default', "Files will open in a new window unless picked from within the application (e.g. via the File menu).") + localize('window.openFilesInNewWindow.defaultMac', "Files will open in the window with the files' folder open or the last active window unless opened via the Dock or from Finder.") : + localize('window.openFilesInNewWindow.default', "Files will open in a new window unless picked from within the application (e.g. via the File menu).") ], 'default': 'off', 'scope': ConfigurationScope.APPLICATION, 'markdownDescription': isMacintosh ? - nls.localize('openFilesInNewWindowMac', "Controls whether files should open in a new window. \nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).") : - nls.localize('openFilesInNewWindow', "Controls whether files should open in a new window.\nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).") + localize('openFilesInNewWindowMac', "Controls whether files should open in a new window. \nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).") : + localize('openFilesInNewWindow', "Controls whether files should open in a new window.\nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).") }, 'window.openFoldersInNewWindow': { 'type': 'string', 'enum': ['on', 'off', 'default'], 'enumDescriptions': [ - nls.localize('window.openFoldersInNewWindow.on', "Folders will open in a new window."), - nls.localize('window.openFoldersInNewWindow.off', "Folders will replace the last active window."), - nls.localize('window.openFoldersInNewWindow.default', "Folders will open in a new window unless a folder is picked from within the application (e.g. via the File menu).") + localize('window.openFoldersInNewWindow.on', "Folders will open in a new window."), + localize('window.openFoldersInNewWindow.off', "Folders will replace the last active window."), + localize('window.openFoldersInNewWindow.default', "Folders will open in a new window unless a folder is picked from within the application (e.g. via the File menu).") ], 'default': 'default', 'scope': ConfigurationScope.APPLICATION, - 'markdownDescription': nls.localize('openFoldersInNewWindow', "Controls whether folders should open in a new window or replace the last active window.\nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).") + 'markdownDescription': localize('openFoldersInNewWindow', "Controls whether folders should open in a new window or replace the last active window.\nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).") }, 'window.confirmBeforeClose': { 'type': 'string', 'enum': ['always', 'keyboardOnly', 'never'], 'enumDescriptions': [ - nls.localize('window.confirmBeforeClose.always', "Always try to ask for confirmation. Note that browsers may still decide to close a tab or window without confirmation."), - nls.localize('window.confirmBeforeClose.keyboardOnly', "Only ask for confirmation if a keybinding was detected. Note that detection may not be possible in some cases."), - nls.localize('window.confirmBeforeClose.never', "Never explicitly ask for confirmation unless data loss is imminent.") + localize('window.confirmBeforeClose.always', "Always try to ask for confirmation. Note that browsers may still decide to close a tab or window without confirmation."), + localize('window.confirmBeforeClose.keyboardOnly', "Only ask for confirmation if a keybinding was detected. Note that detection may not be possible in some cases."), + localize('window.confirmBeforeClose.never', "Never explicitly ask for confirmation unless data loss is imminent.") ], 'default': isWeb && !isStandalone ? 'keyboardOnly' : 'never', // on by default in web, unless PWA - 'description': nls.localize('confirmBeforeCloseWeb', "Controls whether to show a confirmation dialog before closing the browser tab or window. Note that even if enabled, browsers may still decide to close a tab or window without confirmation and that this setting is only a hint that may not work in all cases."), + 'description': localize('confirmBeforeCloseWeb', "Controls whether to show a confirmation dialog before closing the browser tab or window. Note that even if enabled, browsers may still decide to close a tab or window without confirmation and that this setting is only a hint that may not work in all cases."), 'scope': ConfigurationScope.APPLICATION, 'included': isWeb } @@ -452,48 +452,48 @@ import { isStandalone } from 'vs/base/browser/browser'; registry.registerConfiguration({ 'id': 'zenMode', 'order': 9, - 'title': nls.localize('zenModeConfigurationTitle', "Zen Mode"), + 'title': localize('zenModeConfigurationTitle', "Zen Mode"), 'type': 'object', 'properties': { 'zenMode.fullScreen': { 'type': 'boolean', 'default': true, - 'description': nls.localize('zenMode.fullScreen', "Controls whether turning on Zen Mode also puts the workbench into full screen mode.") + 'description': localize('zenMode.fullScreen', "Controls whether turning on Zen Mode also puts the workbench into full screen mode.") }, 'zenMode.centerLayout': { 'type': 'boolean', 'default': true, - 'description': nls.localize('zenMode.centerLayout', "Controls whether turning on Zen Mode also centers the layout.") + 'description': localize('zenMode.centerLayout', "Controls whether turning on Zen Mode also centers the layout.") }, 'zenMode.hideTabs': { 'type': 'boolean', 'default': true, - 'description': nls.localize('zenMode.hideTabs', "Controls whether turning on Zen Mode also hides workbench tabs.") + 'description': localize('zenMode.hideTabs', "Controls whether turning on Zen Mode also hides workbench tabs.") }, 'zenMode.hideStatusBar': { 'type': 'boolean', 'default': true, - 'description': nls.localize('zenMode.hideStatusBar', "Controls whether turning on Zen Mode also hides the status bar at the bottom of the workbench.") + 'description': localize('zenMode.hideStatusBar', "Controls whether turning on Zen Mode also hides the status bar at the bottom of the workbench.") }, 'zenMode.hideActivityBar': { 'type': 'boolean', 'default': true, - 'description': nls.localize('zenMode.hideActivityBar', "Controls whether turning on Zen Mode also hides the activity bar either at the left or right of the workbench.") + 'description': localize('zenMode.hideActivityBar', "Controls whether turning on Zen Mode also hides the activity bar either at the left or right of the workbench.") }, 'zenMode.hideLineNumbers': { 'type': 'boolean', 'default': true, - 'description': nls.localize('zenMode.hideLineNumbers', "Controls whether turning on Zen Mode also hides the editor line numbers.") + 'description': localize('zenMode.hideLineNumbers', "Controls whether turning on Zen Mode also hides the editor line numbers.") }, 'zenMode.restore': { 'type': 'boolean', 'default': true, - 'description': nls.localize('zenMode.restore', "Controls whether a window should restore to zen mode if it was exited in zen mode.") + 'description': localize('zenMode.restore', "Controls whether a window should restore to zen mode if it was exited in zen mode.") }, 'zenMode.silentNotifications': { 'type': 'boolean', 'default': true, - 'description': nls.localize('zenMode.silentNotifications', "Controls whether notifications are shown while in zen mode. If true, only error notifications will pop out.") + 'description': localize('zenMode.silentNotifications', "Controls whether notifications are shown while in zen mode. If true, only error notifications will pop out.") } } }); diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index 29c56c5202c..43d66630da8 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/workbench/browser/style'; - import { localize } from 'vs/nls'; import { Emitter, setGlobalLeakWarningThreshold } from 'vs/base/common/event'; import { runWhenIdle } from 'vs/base/common/async'; 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/composite.ts b/src/vs/workbench/common/composite.ts index d27f59b3627..b2bd447062c 100644 --- a/src/vs/workbench/common/composite.ts +++ b/src/vs/workbench/common/composite.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IAction, IActionViewItem } from 'vs/base/common/actions'; +import { IAction } from 'vs/base/common/actions'; import { Event } from 'vs/base/common/event'; export interface IComposite { @@ -48,11 +48,6 @@ export interface IComposite { */ getContextMenuActions(): ReadonlyArray; - /** - * Returns the action item for a specific action. - */ - getActionViewItem(action: IAction): IActionViewItem | undefined; - /** * Returns the underlying control of this composite. */ 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/common/resources.ts b/src/vs/workbench/common/resources.ts index 94e7e7a4bac..27e05b1d228 100644 --- a/src/vs/workbench/common/resources.ts +++ b/src/vs/workbench/common/resources.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import * as objects from 'vs/base/common/objects'; +import { deepClone, equals } from 'vs/base/common/objects'; import { Emitter } from 'vs/base/common/event'; import { basename, dirname, extname, relativePath } from 'vs/base/common/resources'; import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; @@ -159,11 +159,11 @@ export class ResourceGlobMatcher extends Disposable { // Add excludes per workspaces that got added this.contextService.getWorkspace().folders.forEach(folder => { const rootExcludes = this.globFn(folder.uri); - if (!this.mapRootToExpressionConfig.has(folder.uri.toString()) || !objects.equals(this.mapRootToExpressionConfig.get(folder.uri.toString()), rootExcludes)) { + if (!this.mapRootToExpressionConfig.has(folder.uri.toString()) || !equals(this.mapRootToExpressionConfig.get(folder.uri.toString()), rootExcludes)) { changed = true; this.mapRootToParsedExpression.set(folder.uri.toString(), parse(rootExcludes)); - this.mapRootToExpressionConfig.set(folder.uri.toString(), objects.deepClone(rootExcludes)); + this.mapRootToExpressionConfig.set(folder.uri.toString(), deepClone(rootExcludes)); } }); @@ -183,11 +183,11 @@ export class ResourceGlobMatcher extends Disposable { // Always set for resources outside root as well const globalExcludes = this.globFn(); - if (!this.mapRootToExpressionConfig.has(ResourceGlobMatcher.NO_ROOT) || !objects.equals(this.mapRootToExpressionConfig.get(ResourceGlobMatcher.NO_ROOT), globalExcludes)) { + if (!this.mapRootToExpressionConfig.has(ResourceGlobMatcher.NO_ROOT) || !equals(this.mapRootToExpressionConfig.get(ResourceGlobMatcher.NO_ROOT), globalExcludes)) { changed = true; this.mapRootToParsedExpression.set(ResourceGlobMatcher.NO_ROOT, parse(globalExcludes)); - this.mapRootToExpressionConfig.set(ResourceGlobMatcher.NO_ROOT, objects.deepClone(globalExcludes)); + this.mapRootToExpressionConfig.set(ResourceGlobMatcher.NO_ROOT, deepClone(globalExcludes)); } if (fromEvent && changed) { diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index 317cfd2fcd9..272f767d2e8 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { registerColor, editorBackground, contrastBorder, transparent, editorWidgetBackground, textLinkForeground, lighten, darken, focusBorder, activeContrastBorder, editorWidgetForeground, editorErrorForeground, editorWarningForeground, editorInfoForeground, treeIndentGuidesStroke, errorForeground } from 'vs/platform/theme/common/colorRegistry'; import { IColorTheme } from 'vs/platform/theme/common/themeService'; import { Color } from 'vs/base/common/color'; @@ -29,25 +29,25 @@ export const TAB_ACTIVE_BACKGROUND = registerColor('tab.activeBackground', { dark: editorBackground, light: editorBackground, hc: editorBackground -}, nls.localize('tabActiveBackground', "Active tab background color in an active group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +}, localize('tabActiveBackground', "Active tab background color in an active group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); export const TAB_UNFOCUSED_ACTIVE_BACKGROUND = registerColor('tab.unfocusedActiveBackground', { dark: TAB_ACTIVE_BACKGROUND, light: TAB_ACTIVE_BACKGROUND, hc: TAB_ACTIVE_BACKGROUND -}, nls.localize('tabUnfocusedActiveBackground', "Active tab background color in an unfocused group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +}, localize('tabUnfocusedActiveBackground', "Active tab background color in an unfocused group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); export const TAB_INACTIVE_BACKGROUND = registerColor('tab.inactiveBackground', { dark: '#2D2D2D', light: '#ECECEC', hc: null -}, nls.localize('tabInactiveBackground', "Inactive tab background color in an active group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +}, localize('tabInactiveBackground', "Inactive tab background color in an active group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); export const TAB_UNFOCUSED_INACTIVE_BACKGROUND = registerColor('tab.unfocusedInactiveBackground', { dark: TAB_INACTIVE_BACKGROUND, light: TAB_INACTIVE_BACKGROUND, hc: TAB_INACTIVE_BACKGROUND -}, nls.localize('tabUnfocusedInactiveBackground', "Inactive tab background color in an unfocused group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +}, localize('tabUnfocusedInactiveBackground', "Inactive tab background color in an unfocused group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); //#endregion @@ -57,25 +57,25 @@ export const TAB_ACTIVE_FOREGROUND = registerColor('tab.activeForeground', { dark: Color.white, light: '#333333', hc: Color.white -}, nls.localize('tabActiveForeground', "Active tab foreground color in an active group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +}, localize('tabActiveForeground', "Active tab foreground color in an active group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); export const TAB_INACTIVE_FOREGROUND = registerColor('tab.inactiveForeground', { dark: transparent(TAB_ACTIVE_FOREGROUND, 0.5), light: transparent(TAB_ACTIVE_FOREGROUND, 0.7), hc: Color.white -}, nls.localize('tabInactiveForeground', "Inactive tab foreground color in an active group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +}, localize('tabInactiveForeground', "Inactive tab foreground color in an active group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); export const TAB_UNFOCUSED_ACTIVE_FOREGROUND = registerColor('tab.unfocusedActiveForeground', { dark: transparent(TAB_ACTIVE_FOREGROUND, 0.5), light: transparent(TAB_ACTIVE_FOREGROUND, 0.7), hc: Color.white -}, nls.localize('tabUnfocusedActiveForeground', "Active tab foreground color in an unfocused group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +}, localize('tabUnfocusedActiveForeground', "Active tab foreground color in an unfocused group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); export const TAB_UNFOCUSED_INACTIVE_FOREGROUND = registerColor('tab.unfocusedInactiveForeground', { dark: transparent(TAB_INACTIVE_FOREGROUND, 0.5), light: transparent(TAB_INACTIVE_FOREGROUND, 0.5), hc: Color.white -}, nls.localize('tabUnfocusedInactiveForeground', "Inactive tab foreground color in an unfocused group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +}, localize('tabUnfocusedInactiveForeground', "Inactive tab foreground color in an unfocused group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); //#endregion @@ -85,25 +85,25 @@ export const TAB_HOVER_BACKGROUND = registerColor('tab.hoverBackground', { dark: null, light: null, hc: null -}, nls.localize('tabHoverBackground', "Tab background color when hovering. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +}, localize('tabHoverBackground', "Tab background color when hovering. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); export const TAB_UNFOCUSED_HOVER_BACKGROUND = registerColor('tab.unfocusedHoverBackground', { dark: transparent(TAB_HOVER_BACKGROUND, 0.5), light: transparent(TAB_HOVER_BACKGROUND, 0.7), hc: null -}, nls.localize('tabUnfocusedHoverBackground', "Tab background color in an unfocused group when hovering. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +}, localize('tabUnfocusedHoverBackground', "Tab background color in an unfocused group when hovering. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); export const TAB_HOVER_FOREGROUND = registerColor('tab.hoverForeground', { dark: null, light: null, hc: null -}, nls.localize('tabHoverForeground', "Tab foreground color when hovering. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +}, localize('tabHoverForeground', "Tab foreground color when hovering. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); export const TAB_UNFOCUSED_HOVER_FOREGROUND = registerColor('tab.unfocusedHoverForeground', { dark: transparent(TAB_HOVER_FOREGROUND, 0.5), light: transparent(TAB_HOVER_FOREGROUND, 0.5), hc: null -}, nls.localize('tabUnfocusedHoverForeground', "Tab foreground color in an unfocused group when hovering. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +}, localize('tabUnfocusedHoverForeground', "Tab foreground color in an unfocused group when hovering. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); //#endregion @@ -113,49 +113,49 @@ export const TAB_BORDER = registerColor('tab.border', { dark: '#252526', light: '#F3F3F3', hc: contrastBorder -}, nls.localize('tabBorder', "Border to separate tabs from each other. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +}, localize('tabBorder', "Border to separate tabs from each other. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); export const TAB_LAST_PINNED_BORDER = registerColor('tab.lastPinnedBorder', { dark: treeIndentGuidesStroke, light: treeIndentGuidesStroke, hc: contrastBorder -}, nls.localize('lastPinnedTabBorder', "Border to separate pinned tabs from other tabs. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +}, localize('lastPinnedTabBorder', "Border to separate pinned tabs from other tabs. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); export const TAB_ACTIVE_BORDER = registerColor('tab.activeBorder', { dark: null, light: null, hc: null -}, nls.localize('tabActiveBorder', "Border on the bottom of an active tab. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +}, localize('tabActiveBorder', "Border on the bottom of an active tab. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); export const TAB_UNFOCUSED_ACTIVE_BORDER = registerColor('tab.unfocusedActiveBorder', { dark: transparent(TAB_ACTIVE_BORDER, 0.5), light: transparent(TAB_ACTIVE_BORDER, 0.7), hc: null -}, nls.localize('tabActiveUnfocusedBorder', "Border on the bottom of an active tab in an unfocused group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +}, localize('tabActiveUnfocusedBorder', "Border on the bottom of an active tab in an unfocused group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); export const TAB_ACTIVE_BORDER_TOP = registerColor('tab.activeBorderTop', { dark: null, light: null, hc: null -}, nls.localize('tabActiveBorderTop', "Border to the top of an active tab. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +}, localize('tabActiveBorderTop', "Border to the top of an active tab. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); export const TAB_UNFOCUSED_ACTIVE_BORDER_TOP = registerColor('tab.unfocusedActiveBorderTop', { dark: transparent(TAB_ACTIVE_BORDER_TOP, 0.5), light: transparent(TAB_ACTIVE_BORDER_TOP, 0.7), hc: null -}, nls.localize('tabActiveUnfocusedBorderTop', "Border to the top of an active tab in an unfocused group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +}, localize('tabActiveUnfocusedBorderTop', "Border to the top of an active tab in an unfocused group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); export const TAB_HOVER_BORDER = registerColor('tab.hoverBorder', { dark: null, light: null, hc: null -}, nls.localize('tabHoverBorder', "Border to highlight tabs when hovering. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +}, localize('tabHoverBorder', "Border to highlight tabs when hovering. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); export const TAB_UNFOCUSED_HOVER_BORDER = registerColor('tab.unfocusedHoverBorder', { dark: transparent(TAB_HOVER_BORDER, 0.5), light: transparent(TAB_HOVER_BORDER, 0.7), hc: null -}, nls.localize('tabUnfocusedHoverBorder', "Border to highlight tabs in an unfocused group when hovering. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +}, localize('tabUnfocusedHoverBorder', "Border to highlight tabs in an unfocused group when hovering. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); //#endregion @@ -165,25 +165,25 @@ export const TAB_ACTIVE_MODIFIED_BORDER = registerColor('tab.activeModifiedBorde dark: '#3399CC', light: '#33AAEE', hc: null -}, nls.localize('tabActiveModifiedBorder', "Border on the top of modified (dirty) active tabs in an active group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +}, localize('tabActiveModifiedBorder', "Border on the top of modified (dirty) active tabs in an active group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); export const TAB_INACTIVE_MODIFIED_BORDER = registerColor('tab.inactiveModifiedBorder', { dark: transparent(TAB_ACTIVE_MODIFIED_BORDER, 0.5), light: transparent(TAB_ACTIVE_MODIFIED_BORDER, 0.5), hc: Color.white -}, nls.localize('tabInactiveModifiedBorder', "Border on the top of modified (dirty) inactive tabs in an active group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +}, localize('tabInactiveModifiedBorder', "Border on the top of modified (dirty) inactive tabs in an active group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); export const TAB_UNFOCUSED_ACTIVE_MODIFIED_BORDER = registerColor('tab.unfocusedActiveModifiedBorder', { dark: transparent(TAB_ACTIVE_MODIFIED_BORDER, 0.5), light: transparent(TAB_ACTIVE_MODIFIED_BORDER, 0.7), hc: Color.white -}, nls.localize('unfocusedActiveModifiedBorder', "Border on the top of modified (dirty) active tabs in an unfocused group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +}, localize('unfocusedActiveModifiedBorder', "Border on the top of modified (dirty) active tabs in an unfocused group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); export const TAB_UNFOCUSED_INACTIVE_MODIFIED_BORDER = registerColor('tab.unfocusedInactiveModifiedBorder', { dark: transparent(TAB_INACTIVE_MODIFIED_BORDER, 0.5), light: transparent(TAB_INACTIVE_MODIFIED_BORDER, 0.5), hc: Color.white -}, nls.localize('unfocusedINactiveModifiedBorder', "Border on the top of modified (dirty) inactive tabs in an unfocused group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +}, localize('unfocusedINactiveModifiedBorder', "Border on the top of modified (dirty) inactive tabs in an unfocused group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); //#endregion @@ -193,61 +193,61 @@ export const EDITOR_PANE_BACKGROUND = registerColor('editorPane.background', { dark: editorBackground, light: editorBackground, hc: editorBackground -}, nls.localize('editorPaneBackground', "Background color of the editor pane visible on the left and right side of the centered editor layout.")); +}, localize('editorPaneBackground', "Background color of the editor pane visible on the left and right side of the centered editor layout.")); registerColor('editorGroup.background', { dark: null, light: null, hc: null -}, nls.localize('editorGroupBackground', "Deprecated background color of an editor group."), false, nls.localize('deprecatedEditorGroupBackground', "Deprecated: Background color of an editor group is no longer being supported with the introduction of the grid editor layout. You can use editorGroup.emptyBackground to set the background color of empty editor groups.")); +}, localize('editorGroupBackground', "Deprecated background color of an editor group."), false, localize('deprecatedEditorGroupBackground', "Deprecated: Background color of an editor group is no longer being supported with the introduction of the grid editor layout. You can use editorGroup.emptyBackground to set the background color of empty editor groups.")); export const EDITOR_GROUP_EMPTY_BACKGROUND = registerColor('editorGroup.emptyBackground', { dark: null, light: null, hc: null -}, nls.localize('editorGroupEmptyBackground', "Background color of an empty editor group. Editor groups are the containers of editors.")); +}, localize('editorGroupEmptyBackground', "Background color of an empty editor group. Editor groups are the containers of editors.")); export const EDITOR_GROUP_FOCUSED_EMPTY_BORDER = registerColor('editorGroup.focusedEmptyBorder', { dark: null, light: null, hc: focusBorder -}, nls.localize('editorGroupFocusedEmptyBorder', "Border color of an empty editor group that is focused. Editor groups are the containers of editors.")); +}, localize('editorGroupFocusedEmptyBorder', "Border color of an empty editor group that is focused. Editor groups are the containers of editors.")); export const EDITOR_GROUP_HEADER_TABS_BACKGROUND = registerColor('editorGroupHeader.tabsBackground', { dark: '#252526', light: '#F3F3F3', hc: null -}, nls.localize('tabsContainerBackground', "Background color of the editor group title header when tabs are enabled. Editor groups are the containers of editors.")); +}, localize('tabsContainerBackground', "Background color of the editor group title header when tabs are enabled. Editor groups are the containers of editors.")); export const EDITOR_GROUP_HEADER_TABS_BORDER = registerColor('editorGroupHeader.tabsBorder', { dark: null, light: null, hc: null -}, nls.localize('tabsContainerBorder', "Border color of the editor group title header when tabs are enabled. Editor groups are the containers of editors.")); +}, localize('tabsContainerBorder', "Border color of the editor group title header when tabs are enabled. Editor groups are the containers of editors.")); export const EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND = registerColor('editorGroupHeader.noTabsBackground', { dark: editorBackground, light: editorBackground, hc: editorBackground -}, nls.localize('editorGroupHeaderBackground', "Background color of the editor group title header when tabs are disabled (`\"workbench.editor.showTabs\": false`). Editor groups are the containers of editors.")); +}, localize('editorGroupHeaderBackground', "Background color of the editor group title header when tabs are disabled (`\"workbench.editor.showTabs\": false`). Editor groups are the containers of editors.")); export const EDITOR_GROUP_HEADER_BORDER = registerColor('editorGroupHeader.border', { dark: null, light: null, hc: contrastBorder -}, nls.localize('editorTitleContainerBorder', "Border color of the editor group title header. Editor groups are the containers of editors.")); +}, localize('editorTitleContainerBorder', "Border color of the editor group title header. Editor groups are the containers of editors.")); export const EDITOR_GROUP_BORDER = registerColor('editorGroup.border', { dark: '#444444', light: '#E7E7E7', hc: contrastBorder -}, nls.localize('editorGroupBorder', "Color to separate multiple editor groups from each other. Editor groups are the containers of editors.")); +}, localize('editorGroupBorder', "Color to separate multiple editor groups from each other. Editor groups are the containers of editors.")); export const EDITOR_DRAG_AND_DROP_BACKGROUND = registerColor('editorGroup.dropBackground', { dark: Color.fromHex('#53595D').transparent(0.5), light: Color.fromHex('#2677CB').transparent(0.18), hc: null -}, nls.localize('editorDragAndDropBackground', "Background color when dragging editors around. The color should have transparency so that the editor contents can still shine through.")); +}, localize('editorDragAndDropBackground', "Background color when dragging editors around. The color should have transparency so that the editor contents can still shine through.")); // < --- Resource Viewer --- > @@ -255,7 +255,7 @@ export const IMAGE_PREVIEW_BORDER = registerColor('imagePreview.border', { dark: Color.fromHex('#808080').transparent(0.35), light: Color.fromHex('#808080').transparent(0.35), hc: contrastBorder -}, nls.localize('imagePreviewBorder', "Border color for image in image preview.")); +}, localize('imagePreviewBorder', "Border color for image in image preview.")); // < --- Panels --- > @@ -263,74 +263,74 @@ export const PANEL_BACKGROUND = registerColor('panel.background', { dark: editorBackground, light: editorBackground, hc: editorBackground -}, nls.localize('panelBackground', "Panel background color. Panels are shown below the editor area and contain views like output and integrated terminal.")); +}, localize('panelBackground', "Panel background color. Panels are shown below the editor area and contain views like output and integrated terminal.")); export const PANEL_BORDER = registerColor('panel.border', { dark: Color.fromHex('#808080').transparent(0.35), light: Color.fromHex('#808080').transparent(0.35), hc: contrastBorder -}, nls.localize('panelBorder', "Panel border color to separate the panel from the editor. Panels are shown below the editor area and contain views like output and integrated terminal.")); +}, localize('panelBorder', "Panel border color to separate the panel from the editor. Panels are shown below the editor area and contain views like output and integrated terminal.")); export const PANEL_ACTIVE_TITLE_FOREGROUND = registerColor('panelTitle.activeForeground', { dark: '#E7E7E7', light: '#424242', hc: Color.white -}, nls.localize('panelActiveTitleForeground', "Title color for the active panel. Panels are shown below the editor area and contain views like output and integrated terminal.")); +}, localize('panelActiveTitleForeground', "Title color for the active panel. Panels are shown below the editor area and contain views like output and integrated terminal.")); export const PANEL_INACTIVE_TITLE_FOREGROUND = registerColor('panelTitle.inactiveForeground', { dark: transparent(PANEL_ACTIVE_TITLE_FOREGROUND, 0.6), light: transparent(PANEL_ACTIVE_TITLE_FOREGROUND, 0.75), hc: Color.white -}, nls.localize('panelInactiveTitleForeground', "Title color for the inactive panel. Panels are shown below the editor area and contain views like output and integrated terminal.")); +}, localize('panelInactiveTitleForeground', "Title color for the inactive panel. Panels are shown below the editor area and contain views like output and integrated terminal.")); export const PANEL_ACTIVE_TITLE_BORDER = registerColor('panelTitle.activeBorder', { dark: PANEL_ACTIVE_TITLE_FOREGROUND, light: PANEL_ACTIVE_TITLE_FOREGROUND, hc: contrastBorder -}, nls.localize('panelActiveTitleBorder', "Border color for the active panel title. Panels are shown below the editor area and contain views like output and integrated terminal.")); +}, localize('panelActiveTitleBorder', "Border color for the active panel title. Panels are shown below the editor area and contain views like output and integrated terminal.")); export const PANEL_INPUT_BORDER = registerColor('panelInput.border', { dark: null, light: Color.fromHex('#ddd'), hc: null -}, nls.localize('panelInputBorder', "Input box border for inputs in the panel.")); +}, localize('panelInputBorder', "Input box border for inputs in the panel.")); export const PANEL_DRAG_AND_DROP_BORDER = registerColor('panel.dropBorder', { dark: PANEL_ACTIVE_TITLE_FOREGROUND, light: PANEL_ACTIVE_TITLE_FOREGROUND, hc: PANEL_ACTIVE_TITLE_FOREGROUND, -}, nls.localize('panelDragAndDropBorder', "Drag and drop feedback color for the panel titles. Panels are shown below the editor area and contain views like output and integrated terminal.")); +}, localize('panelDragAndDropBorder', "Drag and drop feedback color for the panel titles. Panels are shown below the editor area and contain views like output and integrated terminal.")); export const PANEL_SECTION_DRAG_AND_DROP_BACKGROUND = registerColor('panelSection.dropBackground', { dark: EDITOR_DRAG_AND_DROP_BACKGROUND, light: EDITOR_DRAG_AND_DROP_BACKGROUND, hc: EDITOR_DRAG_AND_DROP_BACKGROUND, -}, nls.localize('panelSectionDragAndDropBackground', "Drag and drop feedback color for the panel sections. The color should have transparency so that the panel sections can still shine through. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.")); +}, localize('panelSectionDragAndDropBackground', "Drag and drop feedback color for the panel sections. The color should have transparency so that the panel sections can still shine through. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.")); export const PANEL_SECTION_HEADER_BACKGROUND = registerColor('panelSectionHeader.background', { dark: Color.fromHex('#808080').transparent(0.2), light: Color.fromHex('#808080').transparent(0.2), hc: null -}, nls.localize('panelSectionHeaderBackground', "Panel section header background color. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.")); +}, localize('panelSectionHeaderBackground', "Panel section header background color. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.")); export const PANEL_SECTION_HEADER_FOREGROUND = registerColor('panelSectionHeader.foreground', { dark: null, light: null, hc: null -}, nls.localize('panelSectionHeaderForeground', "Panel section header foreground color. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.")); +}, localize('panelSectionHeaderForeground', "Panel section header foreground color. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.")); export const PANEL_SECTION_HEADER_BORDER = registerColor('panelSectionHeader.border', { dark: contrastBorder, light: contrastBorder, hc: contrastBorder -}, nls.localize('panelSectionHeaderBorder', "Panel section header border color used when multiple views are stacked vertically in the panel. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.")); +}, localize('panelSectionHeaderBorder', "Panel section header border color used when multiple views are stacked vertically in the panel. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.")); export const PANEL_SECTION_BORDER = registerColor('panelSection.border', { dark: PANEL_BORDER, light: PANEL_BORDER, hc: PANEL_BORDER -}, nls.localize('panelSectionBorder', "Panel section border color used when multiple views are stacked horizontally in the panel. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.")); +}, localize('panelSectionBorder', "Panel section border color used when multiple views are stacked horizontally in the panel. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.")); // < --- Status --- > @@ -339,79 +339,79 @@ export const STATUS_BAR_FOREGROUND = registerColor('statusBar.foreground', { dark: '#FFFFFF', light: '#FFFFFF', hc: '#FFFFFF' -}, nls.localize('statusBarForeground', "Status bar foreground color when a workspace or folder is opened. The status bar is shown in the bottom of the window.")); +}, localize('statusBarForeground', "Status bar foreground color when a workspace or folder is opened. The status bar is shown in the bottom of the window.")); export const STATUS_BAR_NO_FOLDER_FOREGROUND = registerColor('statusBar.noFolderForeground', { dark: STATUS_BAR_FOREGROUND, light: STATUS_BAR_FOREGROUND, hc: STATUS_BAR_FOREGROUND -}, nls.localize('statusBarNoFolderForeground', "Status bar foreground color when no folder is opened. The status bar is shown in the bottom of the window.")); +}, localize('statusBarNoFolderForeground', "Status bar foreground color when no folder is opened. The status bar is shown in the bottom of the window.")); export const STATUS_BAR_BACKGROUND = registerColor('statusBar.background', { dark: '#007ACC', light: '#007ACC', hc: null -}, nls.localize('statusBarBackground', "Status bar background color when a workspace or folder is opened. The status bar is shown in the bottom of the window.")); +}, localize('statusBarBackground', "Status bar background color when a workspace or folder is opened. The status bar is shown in the bottom of the window.")); export const STATUS_BAR_NO_FOLDER_BACKGROUND = registerColor('statusBar.noFolderBackground', { dark: '#68217A', light: '#68217A', hc: null -}, nls.localize('statusBarNoFolderBackground', "Status bar background color when no folder is opened. The status bar is shown in the bottom of the window.")); +}, localize('statusBarNoFolderBackground', "Status bar background color when no folder is opened. The status bar is shown in the bottom of the window.")); export const STATUS_BAR_BORDER = registerColor('statusBar.border', { dark: null, light: null, hc: contrastBorder -}, nls.localize('statusBarBorder', "Status bar border color separating to the sidebar and editor. The status bar is shown in the bottom of the window.")); +}, localize('statusBarBorder', "Status bar border color separating to the sidebar and editor. The status bar is shown in the bottom of the window.")); export const STATUS_BAR_NO_FOLDER_BORDER = registerColor('statusBar.noFolderBorder', { dark: STATUS_BAR_BORDER, light: STATUS_BAR_BORDER, hc: STATUS_BAR_BORDER -}, nls.localize('statusBarNoFolderBorder', "Status bar border color separating to the sidebar and editor when no folder is opened. The status bar is shown in the bottom of the window.")); +}, localize('statusBarNoFolderBorder', "Status bar border color separating to the sidebar and editor when no folder is opened. The status bar is shown in the bottom of the window.")); export const STATUS_BAR_ITEM_ACTIVE_BACKGROUND = registerColor('statusBarItem.activeBackground', { dark: Color.white.transparent(0.18), light: Color.white.transparent(0.18), hc: Color.white.transparent(0.18) -}, nls.localize('statusBarItemActiveBackground', "Status bar item background color when clicking. The status bar is shown in the bottom of the window.")); +}, localize('statusBarItemActiveBackground', "Status bar item background color when clicking. The status bar is shown in the bottom of the window.")); export const STATUS_BAR_ITEM_HOVER_BACKGROUND = registerColor('statusBarItem.hoverBackground', { dark: Color.white.transparent(0.12), light: Color.white.transparent(0.12), hc: Color.white.transparent(0.12) -}, nls.localize('statusBarItemHoverBackground', "Status bar item background color when hovering. The status bar is shown in the bottom of the window.")); +}, localize('statusBarItemHoverBackground', "Status bar item background color when hovering. The status bar is shown in the bottom of the window.")); export const STATUS_BAR_PROMINENT_ITEM_FOREGROUND = registerColor('statusBarItem.prominentForeground', { dark: STATUS_BAR_FOREGROUND, light: STATUS_BAR_FOREGROUND, hc: STATUS_BAR_FOREGROUND -}, nls.localize('statusBarProminentItemForeground', "Status bar prominent items foreground color. Prominent items stand out from other status bar entries to indicate importance. Change mode `Toggle Tab Key Moves Focus` from command palette to see an example. The status bar is shown in the bottom of the window.")); +}, localize('statusBarProminentItemForeground', "Status bar prominent items foreground color. Prominent items stand out from other status bar entries to indicate importance. Change mode `Toggle Tab Key Moves Focus` from command palette to see an example. The status bar is shown in the bottom of the window.")); export const STATUS_BAR_PROMINENT_ITEM_BACKGROUND = registerColor('statusBarItem.prominentBackground', { dark: Color.black.transparent(0.5), light: Color.black.transparent(0.5), hc: Color.black.transparent(0.5), -}, nls.localize('statusBarProminentItemBackground', "Status bar prominent items background color. Prominent items stand out from other status bar entries to indicate importance. Change mode `Toggle Tab Key Moves Focus` from command palette to see an example. The status bar is shown in the bottom of the window.")); +}, localize('statusBarProminentItemBackground', "Status bar prominent items background color. Prominent items stand out from other status bar entries to indicate importance. Change mode `Toggle Tab Key Moves Focus` from command palette to see an example. The status bar is shown in the bottom of the window.")); export const STATUS_BAR_PROMINENT_ITEM_HOVER_BACKGROUND = registerColor('statusBarItem.prominentHoverBackground', { dark: Color.black.transparent(0.3), light: Color.black.transparent(0.3), hc: Color.black.transparent(0.3), -}, nls.localize('statusBarProminentItemHoverBackground', "Status bar prominent items background color when hovering. Prominent items stand out from other status bar entries to indicate importance. Change mode `Toggle Tab Key Moves Focus` from command palette to see an example. The status bar is shown in the bottom of the window.")); +}, localize('statusBarProminentItemHoverBackground', "Status bar prominent items background color when hovering. Prominent items stand out from other status bar entries to indicate importance. Change mode `Toggle Tab Key Moves Focus` from command palette to see an example. The status bar is shown in the bottom of the window.")); export const STATUS_BAR_ERROR_ITEM_BACKGROUND = registerColor('statusBarItem.errorBackground', { dark: darken(errorForeground, .4), light: darken(errorForeground, .4), hc: null, -}, nls.localize('statusBarErrorItemBackground', "Status bar error items background color. Error items stand out from other status bar entries to indicate error conditions. The status bar is shown in the bottom of the window.")); +}, localize('statusBarErrorItemBackground', "Status bar error items background color. Error items stand out from other status bar entries to indicate error conditions. The status bar is shown in the bottom of the window.")); export const STATUS_BAR_ERROR_ITEM_FOREGROUND = registerColor('statusBarItem.errorForeground', { dark: Color.white, light: Color.white, hc: Color.white, -}, nls.localize('statusBarErrorItemForeground', "Status bar error items foreground color. Error items stand out from other status bar entries to indicate error conditions. The status bar is shown in the bottom of the window.")); +}, localize('statusBarErrorItemForeground', "Status bar error items foreground color. Error items stand out from other status bar entries to indicate error conditions. The status bar is shown in the bottom of the window.")); // < --- Activity Bar --- > @@ -419,61 +419,61 @@ export const ACTIVITY_BAR_BACKGROUND = registerColor('activityBar.background', { dark: '#333333', light: '#2C2C2C', hc: '#000000' -}, nls.localize('activityBarBackground', "Activity bar background color. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); +}, localize('activityBarBackground', "Activity bar background color. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); export const ACTIVITY_BAR_FOREGROUND = registerColor('activityBar.foreground', { dark: Color.white, light: Color.white, hc: Color.white -}, nls.localize('activityBarForeground', "Activity bar item foreground color when it is active. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); +}, localize('activityBarForeground', "Activity bar item foreground color when it is active. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); export const ACTIVITY_BAR_INACTIVE_FOREGROUND = registerColor('activityBar.inactiveForeground', { dark: transparent(ACTIVITY_BAR_FOREGROUND, 0.4), light: transparent(ACTIVITY_BAR_FOREGROUND, 0.4), hc: Color.white -}, nls.localize('activityBarInActiveForeground', "Activity bar item foreground color when it is inactive. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); +}, localize('activityBarInActiveForeground', "Activity bar item foreground color when it is inactive. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); export const ACTIVITY_BAR_BORDER = registerColor('activityBar.border', { dark: null, light: null, hc: contrastBorder -}, nls.localize('activityBarBorder', "Activity bar border color separating to the side bar. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); +}, localize('activityBarBorder', "Activity bar border color separating to the side bar. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); export const ACTIVITY_BAR_ACTIVE_BORDER = registerColor('activityBar.activeBorder', { dark: ACTIVITY_BAR_FOREGROUND, light: ACTIVITY_BAR_FOREGROUND, hc: null -}, nls.localize('activityBarActiveBorder', "Activity bar border color for the active item. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); +}, localize('activityBarActiveBorder', "Activity bar border color for the active item. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); export const ACTIVITY_BAR_ACTIVE_FOCUS_BORDER = registerColor('activityBar.activeFocusBorder', { dark: null, light: null, hc: null -}, nls.localize('activityBarActiveFocusBorder', "Activity bar focus border color for the active item. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); +}, localize('activityBarActiveFocusBorder', "Activity bar focus border color for the active item. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); export const ACTIVITY_BAR_ACTIVE_BACKGROUND = registerColor('activityBar.activeBackground', { dark: null, light: null, hc: null -}, nls.localize('activityBarActiveBackground', "Activity bar background color for the active item. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); +}, localize('activityBarActiveBackground', "Activity bar background color for the active item. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); export const ACTIVITY_BAR_DRAG_AND_DROP_BORDER = registerColor('activityBar.dropBorder', { dark: ACTIVITY_BAR_FOREGROUND, light: ACTIVITY_BAR_FOREGROUND, hc: ACTIVITY_BAR_FOREGROUND, -}, nls.localize('activityBarDragAndDropBorder', "Drag and drop feedback color for the activity bar items. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); +}, localize('activityBarDragAndDropBorder', "Drag and drop feedback color for the activity bar items. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); export const ACTIVITY_BAR_BADGE_BACKGROUND = registerColor('activityBarBadge.background', { dark: '#007ACC', light: '#007ACC', hc: '#000000' -}, nls.localize('activityBarBadgeBackground', "Activity notification badge background color. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); +}, localize('activityBarBadgeBackground', "Activity notification badge background color. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); export const ACTIVITY_BAR_BADGE_FOREGROUND = registerColor('activityBarBadge.foreground', { dark: Color.white, light: Color.white, hc: Color.white -}, nls.localize('activityBarBadgeForeground', "Activity notification badge foreground color. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); +}, localize('activityBarBadgeForeground', "Activity notification badge foreground color. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); // < --- Remote --- > @@ -482,25 +482,25 @@ export const STATUS_BAR_HOST_NAME_BACKGROUND = registerColor('statusBarItem.remo dark: ACTIVITY_BAR_BADGE_BACKGROUND, light: ACTIVITY_BAR_BADGE_BACKGROUND, hc: ACTIVITY_BAR_BADGE_BACKGROUND -}, nls.localize('statusBarItemHostBackground', "Background color for the remote indicator on the status bar.")); +}, localize('statusBarItemHostBackground', "Background color for the remote indicator on the status bar.")); export const STATUS_BAR_HOST_NAME_FOREGROUND = registerColor('statusBarItem.remoteForeground', { dark: ACTIVITY_BAR_BADGE_FOREGROUND, light: ACTIVITY_BAR_BADGE_FOREGROUND, hc: ACTIVITY_BAR_BADGE_FOREGROUND -}, nls.localize('statusBarItemHostForeground', "Foreground color for the remote indicator on the status bar.")); +}, localize('statusBarItemHostForeground', "Foreground color for the remote indicator on the status bar.")); export const EXTENSION_BADGE_REMOTE_BACKGROUND = registerColor('extensionBadge.remoteBackground', { dark: ACTIVITY_BAR_BADGE_BACKGROUND, light: ACTIVITY_BAR_BADGE_BACKGROUND, hc: ACTIVITY_BAR_BADGE_BACKGROUND -}, nls.localize('extensionBadge.remoteBackground', "Background color for the remote badge in the extensions view.")); +}, localize('extensionBadge.remoteBackground', "Background color for the remote badge in the extensions view.")); export const EXTENSION_BADGE_REMOTE_FOREGROUND = registerColor('extensionBadge.remoteForeground', { dark: ACTIVITY_BAR_BADGE_FOREGROUND, light: ACTIVITY_BAR_BADGE_FOREGROUND, hc: ACTIVITY_BAR_BADGE_FOREGROUND -}, nls.localize('extensionBadge.remoteForeground', "Foreground color for the remote badge in the extensions view.")); +}, localize('extensionBadge.remoteForeground', "Foreground color for the remote badge in the extensions view.")); // < --- Side Bar --- > @@ -509,49 +509,49 @@ export const SIDE_BAR_BACKGROUND = registerColor('sideBar.background', { dark: '#252526', light: '#F3F3F3', hc: '#000000' -}, nls.localize('sideBarBackground', "Side bar background color. The side bar is the container for views like explorer and search.")); +}, localize('sideBarBackground', "Side bar background color. The side bar is the container for views like explorer and search.")); export const SIDE_BAR_FOREGROUND = registerColor('sideBar.foreground', { dark: null, light: null, hc: null -}, nls.localize('sideBarForeground', "Side bar foreground color. The side bar is the container for views like explorer and search.")); +}, localize('sideBarForeground', "Side bar foreground color. The side bar is the container for views like explorer and search.")); export const SIDE_BAR_BORDER = registerColor('sideBar.border', { dark: null, light: null, hc: contrastBorder -}, nls.localize('sideBarBorder', "Side bar border color on the side separating to the editor. The side bar is the container for views like explorer and search.")); +}, localize('sideBarBorder', "Side bar border color on the side separating to the editor. The side bar is the container for views like explorer and search.")); export const SIDE_BAR_TITLE_FOREGROUND = registerColor('sideBarTitle.foreground', { dark: SIDE_BAR_FOREGROUND, light: SIDE_BAR_FOREGROUND, hc: SIDE_BAR_FOREGROUND -}, nls.localize('sideBarTitleForeground', "Side bar title foreground color. The side bar is the container for views like explorer and search.")); +}, localize('sideBarTitleForeground', "Side bar title foreground color. The side bar is the container for views like explorer and search.")); export const SIDE_BAR_DRAG_AND_DROP_BACKGROUND = registerColor('sideBar.dropBackground', { dark: EDITOR_DRAG_AND_DROP_BACKGROUND, light: EDITOR_DRAG_AND_DROP_BACKGROUND, hc: EDITOR_DRAG_AND_DROP_BACKGROUND, -}, nls.localize('sideBarDragAndDropBackground', "Drag and drop feedback color for the side bar sections. The color should have transparency so that the side bar sections can still shine through. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.")); +}, localize('sideBarDragAndDropBackground', "Drag and drop feedback color for the side bar sections. The color should have transparency so that the side bar sections can still shine through. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.")); export const SIDE_BAR_SECTION_HEADER_BACKGROUND = registerColor('sideBarSectionHeader.background', { dark: Color.fromHex('#808080').transparent(0.2), light: Color.fromHex('#808080').transparent(0.2), hc: null -}, nls.localize('sideBarSectionHeaderBackground', "Side bar section header background color. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.")); +}, localize('sideBarSectionHeaderBackground', "Side bar section header background color. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.")); export const SIDE_BAR_SECTION_HEADER_FOREGROUND = registerColor('sideBarSectionHeader.foreground', { dark: SIDE_BAR_FOREGROUND, light: SIDE_BAR_FOREGROUND, hc: SIDE_BAR_FOREGROUND -}, nls.localize('sideBarSectionHeaderForeground', "Side bar section header foreground color. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.")); +}, localize('sideBarSectionHeaderForeground', "Side bar section header foreground color. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.")); export const SIDE_BAR_SECTION_HEADER_BORDER = registerColor('sideBarSectionHeader.border', { dark: contrastBorder, light: contrastBorder, hc: contrastBorder -}, nls.localize('sideBarSectionHeaderBorder', "Side bar section header border color. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.")); +}, localize('sideBarSectionHeaderBorder', "Side bar section header border color. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.")); // < --- Title Bar --- > @@ -560,31 +560,31 @@ export const TITLE_BAR_ACTIVE_FOREGROUND = registerColor('titleBar.activeForegro dark: '#CCCCCC', light: '#333333', hc: '#FFFFFF' -}, nls.localize('titleBarActiveForeground', "Title bar foreground when the window is active.")); +}, localize('titleBarActiveForeground', "Title bar foreground when the window is active.")); export const TITLE_BAR_INACTIVE_FOREGROUND = registerColor('titleBar.inactiveForeground', { dark: transparent(TITLE_BAR_ACTIVE_FOREGROUND, 0.6), light: transparent(TITLE_BAR_ACTIVE_FOREGROUND, 0.6), hc: null -}, nls.localize('titleBarInactiveForeground', "Title bar foreground when the window is inactive.")); +}, localize('titleBarInactiveForeground', "Title bar foreground when the window is inactive.")); export const TITLE_BAR_ACTIVE_BACKGROUND = registerColor('titleBar.activeBackground', { dark: '#3C3C3C', light: '#DDDDDD', hc: '#000000' -}, nls.localize('titleBarActiveBackground', "Title bar background when the window is active.")); +}, localize('titleBarActiveBackground', "Title bar background when the window is active.")); export const TITLE_BAR_INACTIVE_BACKGROUND = registerColor('titleBar.inactiveBackground', { dark: transparent(TITLE_BAR_ACTIVE_BACKGROUND, 0.6), light: transparent(TITLE_BAR_ACTIVE_BACKGROUND, 0.6), hc: null -}, nls.localize('titleBarInactiveBackground', "Title bar background when the window is inactive.")); +}, localize('titleBarInactiveBackground', "Title bar background when the window is inactive.")); export const TITLE_BAR_BORDER = registerColor('titleBar.border', { dark: null, light: null, hc: contrastBorder -}, nls.localize('titleBarBorder', "Title bar border color.")); +}, localize('titleBarBorder', "Title bar border color.")); // < --- Menubar --- > @@ -592,19 +592,19 @@ export const MENUBAR_SELECTION_FOREGROUND = registerColor('menubar.selectionFore dark: TITLE_BAR_ACTIVE_FOREGROUND, light: TITLE_BAR_ACTIVE_FOREGROUND, hc: TITLE_BAR_ACTIVE_FOREGROUND -}, nls.localize('menubarSelectionForeground', "Foreground color of the selected menu item in the menubar.")); +}, localize('menubarSelectionForeground', "Foreground color of the selected menu item in the menubar.")); export const MENUBAR_SELECTION_BACKGROUND = registerColor('menubar.selectionBackground', { dark: transparent(Color.white, 0.1), light: transparent(Color.black, 0.1), hc: null -}, nls.localize('menubarSelectionBackground', "Background color of the selected menu item in the menubar.")); +}, localize('menubarSelectionBackground', "Background color of the selected menu item in the menubar.")); export const MENUBAR_SELECTION_BORDER = registerColor('menubar.selectionBorder', { dark: null, light: null, hc: activeContrastBorder -}, nls.localize('menubarSelectionBorder', "Border color of the selected menu item in the menubar.")); +}, localize('menubarSelectionBorder', "Border color of the selected menu item in the menubar.")); // < --- Notifications --- > @@ -612,76 +612,76 @@ export const NOTIFICATIONS_CENTER_BORDER = registerColor('notificationCenter.bor dark: null, light: null, hc: contrastBorder -}, nls.localize('notificationCenterBorder', "Notifications center border color. Notifications slide in from the bottom right of the window.")); +}, localize('notificationCenterBorder', "Notifications center border color. Notifications slide in from the bottom right of the window.")); export const NOTIFICATIONS_TOAST_BORDER = registerColor('notificationToast.border', { dark: null, light: null, hc: contrastBorder -}, nls.localize('notificationToastBorder', "Notification toast border color. Notifications slide in from the bottom right of the window.")); +}, localize('notificationToastBorder', "Notification toast border color. Notifications slide in from the bottom right of the window.")); export const NOTIFICATIONS_FOREGROUND = registerColor('notifications.foreground', { dark: editorWidgetForeground, light: editorWidgetForeground, hc: editorWidgetForeground -}, nls.localize('notificationsForeground', "Notifications foreground color. Notifications slide in from the bottom right of the window.")); +}, localize('notificationsForeground', "Notifications foreground color. Notifications slide in from the bottom right of the window.")); export const NOTIFICATIONS_BACKGROUND = registerColor('notifications.background', { dark: editorWidgetBackground, light: editorWidgetBackground, hc: editorWidgetBackground -}, nls.localize('notificationsBackground', "Notifications background color. Notifications slide in from the bottom right of the window.")); +}, localize('notificationsBackground', "Notifications background color. Notifications slide in from the bottom right of the window.")); export const NOTIFICATIONS_LINKS = registerColor('notificationLink.foreground', { dark: textLinkForeground, light: textLinkForeground, hc: textLinkForeground -}, nls.localize('notificationsLink', "Notification links foreground color. Notifications slide in from the bottom right of the window.")); +}, localize('notificationsLink', "Notification links foreground color. Notifications slide in from the bottom right of the window.")); export const NOTIFICATIONS_CENTER_HEADER_FOREGROUND = registerColor('notificationCenterHeader.foreground', { dark: null, light: null, hc: null -}, nls.localize('notificationCenterHeaderForeground', "Notifications center header foreground color. Notifications slide in from the bottom right of the window.")); +}, localize('notificationCenterHeaderForeground', "Notifications center header foreground color. Notifications slide in from the bottom right of the window.")); export const NOTIFICATIONS_CENTER_HEADER_BACKGROUND = registerColor('notificationCenterHeader.background', { dark: lighten(NOTIFICATIONS_BACKGROUND, 0.3), light: darken(NOTIFICATIONS_BACKGROUND, 0.05), hc: NOTIFICATIONS_BACKGROUND -}, nls.localize('notificationCenterHeaderBackground', "Notifications center header background color. Notifications slide in from the bottom right of the window.")); +}, localize('notificationCenterHeaderBackground', "Notifications center header background color. Notifications slide in from the bottom right of the window.")); export const NOTIFICATIONS_BORDER = registerColor('notifications.border', { dark: NOTIFICATIONS_CENTER_HEADER_BACKGROUND, light: NOTIFICATIONS_CENTER_HEADER_BACKGROUND, hc: NOTIFICATIONS_CENTER_HEADER_BACKGROUND -}, nls.localize('notificationsBorder', "Notifications border color separating from other notifications in the notifications center. Notifications slide in from the bottom right of the window.")); +}, localize('notificationsBorder', "Notifications border color separating from other notifications in the notifications center. Notifications slide in from the bottom right of the window.")); export const NOTIFICATIONS_ERROR_ICON_FOREGROUND = registerColor('notificationsErrorIcon.foreground', { dark: editorErrorForeground, light: editorErrorForeground, hc: editorErrorForeground -}, nls.localize('notificationsErrorIconForeground', "The color used for the icon of error notifications. Notifications slide in from the bottom right of the window.")); +}, localize('notificationsErrorIconForeground', "The color used for the icon of error notifications. Notifications slide in from the bottom right of the window.")); export const NOTIFICATIONS_WARNING_ICON_FOREGROUND = registerColor('notificationsWarningIcon.foreground', { dark: editorWarningForeground, light: editorWarningForeground, hc: editorWarningForeground -}, nls.localize('notificationsWarningIconForeground', "The color used for the icon of warning notifications. Notifications slide in from the bottom right of the window.")); +}, localize('notificationsWarningIconForeground', "The color used for the icon of warning notifications. Notifications slide in from the bottom right of the window.")); export const NOTIFICATIONS_INFO_ICON_FOREGROUND = registerColor('notificationsInfoIcon.foreground', { dark: editorInfoForeground, light: editorInfoForeground, hc: editorInfoForeground -}, nls.localize('notificationsInfoIconForeground', "The color used for the icon of info notifications. Notifications slide in from the bottom right of the window.")); +}, localize('notificationsInfoIconForeground', "The color used for the icon of info notifications. Notifications slide in from the bottom right of the window.")); export const WINDOW_ACTIVE_BORDER = registerColor('window.activeBorder', { dark: null, light: null, hc: contrastBorder -}, nls.localize('windowActiveBorder', "The color used for the border of the window when it is active. Only supported in the desktop client when using the custom title bar.")); +}, localize('windowActiveBorder', "The color used for the border of the window when it is active. Only supported in the desktop client when using the custom title bar.")); export const WINDOW_INACTIVE_BORDER = registerColor('window.inactiveBorder', { dark: null, light: null, hc: contrastBorder -}, nls.localize('windowInactiveBorder', "The color used for the border of the window when it is inactive. Only supported in the desktop client when using the custom title bar.")); +}, localize('windowInactiveBorder', "The color used for the border of the window when it is inactive. Only supported in the desktop client when using the custom title bar.")); 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/debug/browser/debugViewlet.ts b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts index 5139f3384dd..d1c14273d55 100644 --- a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts +++ b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts @@ -5,7 +5,7 @@ import 'vs/css!./media/debugViewlet'; import * as nls from 'vs/nls'; -import { IAction, IActionViewItem } from 'vs/base/common/actions'; +import { IAction } from 'vs/base/common/actions'; import { IDebugService, VIEWLET_ID, State, BREAKPOINTS_VIEW_ID, CONTEXT_DEBUG_UX, CONTEXT_DEBUG_UX_KEY, REPL_VIEW_ID, CONTEXT_DEBUG_STATE, ILaunch, getStateLabel, CONTEXT_DEBUGGERS_AVAILABLE } from 'vs/workbench/contrib/debug/common/debug'; import { StartDebugActionViewItem, FocusSessionActionViewItem } from 'vs/workbench/contrib/debug/browser/debugActionViewItems'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; @@ -30,6 +30,7 @@ import { debugConfigure } from 'vs/workbench/contrib/debug/browser/debugIcons'; import { WorkbenchStateContext } from 'vs/workbench/browser/contextkeys'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { FOCUS_SESSION_ID, SELECT_AND_START_ID, DEBUG_CONFIGURE_COMMAND_ID, DEBUG_CONFIGURE_LABEL, DEBUG_START_LABEL, DEBUG_START_COMMAND_ID } from 'vs/workbench/contrib/debug/browser/debugCommands'; +import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; export class DebugViewPaneContainer extends ViewPaneContainer { diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 46cd8f54f9d..b8f12426720 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -5,7 +5,7 @@ import 'vs/css!./media/repl'; import { URI as uri } from 'vs/base/common/uri'; -import { IAction, IActionViewItem } from 'vs/base/common/actions'; +import { IAction } from 'vs/base/common/actions'; import * as dom from 'vs/base/browser/dom'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -62,6 +62,7 @@ import { ReplFilter, ReplFilterState, ReplFilterActionViewItem } from 'vs/workbe import { debugConsoleClearAll, debugConsoleEvaluationPrompt } from 'vs/workbench/contrib/debug/browser/debugIcons'; import { registerAction2, MenuId, Action2, IMenuService, IMenu } from 'vs/platform/actions/common/actions'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; const $ = dom.$; diff --git a/src/vs/workbench/contrib/debug/browser/replFilter.ts b/src/vs/workbench/contrib/debug/browser/replFilter.ts index 668a3f0294b..15f2c7bed61 100644 --- a/src/vs/workbench/contrib/debug/browser/replFilter.ts +++ b/src/vs/workbench/contrib/debug/browser/replFilter.ts @@ -158,7 +158,19 @@ export class ReplFilterActionViewItem extends BaseActionViewItem { } focus(): void { - this.filterInputBox.focus(); + if (this.filterInputBox) { + this.filterInputBox.focus(); + } + } + + blur(): void { + if (this.filterInputBox) { + this.filterInputBox.blur(); + } + } + + setFocusable(): void { + // noop input elements are focusable by default } getHistory(): string[] { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 6868dfacdaf..cecfebf4939 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -859,25 +859,27 @@ export class DropDownMenuActionViewItem extends ExtensionActionViewItem { export function getContextMenuActions(extension: IExtension | undefined | null, inExtensionEditor: boolean, instantiationService: IInstantiationService): IAction[][] { return instantiationService.invokeFunction(accessor => { - const scopedContextKeyService = accessor.get(IContextKeyService).createScoped(); const menuService = accessor.get(IMenuService); const extensionRecommendationsService = accessor.get(IExtensionRecommendationsService); const extensionIgnoredRecommendationsService = accessor.get(IExtensionIgnoredRecommendationsService); + const cksOverlay: [string, any][] = []; + if (extension) { - scopedContextKeyService.createKey('extension', extension.identifier.id); - scopedContextKeyService.createKey('isBuiltinExtension', extension.isBuiltin); - scopedContextKeyService.createKey('extensionHasConfiguration', extension.local && !!extension.local.manifest.contributes && !!extension.local.manifest.contributes.configuration); - scopedContextKeyService.createKey('isExtensionRecommended', !!extensionRecommendationsService.getAllRecommendationsWithReason()[extension.identifier.id.toLowerCase()]); - scopedContextKeyService.createKey('isExtensionWorkspaceRecommended', extensionRecommendationsService.getAllRecommendationsWithReason()[extension.identifier.id.toLowerCase()]?.reasonId === ExtensionRecommendationReason.Workspace); - scopedContextKeyService.createKey('isUserIgnoredRecommendation', extensionIgnoredRecommendationsService.globalIgnoredRecommendations.some(e => e === extension.identifier.id.toLowerCase())); - scopedContextKeyService.createKey('inExtensionEditor', inExtensionEditor); + cksOverlay.push(['extension', extension.identifier.id]); + cksOverlay.push(['isBuiltinExtension', extension.isBuiltin]); + cksOverlay.push(['extensionHasConfiguration', extension.local && !!extension.local.manifest.contributes && !!extension.local.manifest.contributes.configuration]); + cksOverlay.push(['isExtensionRecommended', !!extensionRecommendationsService.getAllRecommendationsWithReason()[extension.identifier.id.toLowerCase()]]); + cksOverlay.push(['isExtensionWorkspaceRecommended', extensionRecommendationsService.getAllRecommendationsWithReason()[extension.identifier.id.toLowerCase()]?.reasonId === ExtensionRecommendationReason.Workspace]); + cksOverlay.push(['isUserIgnoredRecommendation', extensionIgnoredRecommendationsService.globalIgnoredRecommendations.some(e => e === extension.identifier.id.toLowerCase())]); + cksOverlay.push(['inExtensionEditor', inExtensionEditor]); if (extension.state === ExtensionState.Installed) { - scopedContextKeyService.createKey('extensionStatus', 'installed'); + cksOverlay.push(['extensionStatus', 'installed']); } } + const contextKeyService = accessor.get(IContextKeyService).createOverlay(cksOverlay); const groups: IAction[][] = []; - const menu = menuService.createMenu(MenuId.ExtensionContext, scopedContextKeyService); + const menu = menuService.createMenu(MenuId.ExtensionContext, contextKeyService); menu.getActions({ shouldForwardArgs: true }).forEach(([, actions]) => groups.push(actions.map(action => { if (action instanceof SubmenuAction) { return action; @@ -885,7 +887,6 @@ export function getContextMenuActions(extension: IExtension | undefined | null, return instantiationService.createInstance(MenuItemExtensionAction, action); }))); menu.dispose(); - scopedContextKeyService.dispose(); return groups; }); 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/markers/browser/markersView.ts b/src/vs/workbench/contrib/markers/browser/markersView.ts index b2a7ce0f6f5..5f8de976926 100644 --- a/src/vs/workbench/contrib/markers/browser/markersView.ts +++ b/src/vs/workbench/contrib/markers/browser/markersView.ts @@ -7,7 +7,7 @@ import 'vs/css!./media/markers'; import { URI } from 'vs/base/common/uri'; import * as dom from 'vs/base/browser/dom'; -import { IAction, IActionViewItem, Action, Separator } from 'vs/base/common/actions'; +import { IAction, Action, Separator } from 'vs/base/common/actions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import Constants from 'vs/workbench/contrib/markers/browser/constants'; @@ -32,7 +32,7 @@ import { deepClone } from 'vs/base/common/objects'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { FilterData, Filter, VirtualDelegate, ResourceMarkersRenderer, MarkerRenderer, RelatedInformationRenderer, MarkersTreeAccessibilityProvider, MarkersViewModel, ResourceDragAndDrop } from 'vs/workbench/contrib/markers/browser/markersTreeViewer'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionBar, IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; diff --git a/src/vs/workbench/contrib/markers/browser/markersViewActions.ts b/src/vs/workbench/contrib/markers/browser/markersViewActions.ts index 2a6fc629fe3..2653e238d6f 100644 --- a/src/vs/workbench/contrib/markers/browser/markersViewActions.ts +++ b/src/vs/workbench/contrib/markers/browser/markersViewActions.ts @@ -283,6 +283,16 @@ export class MarkersFilterActionViewItem extends BaseActionViewItem { } } + blur(): void { + if (this.filterInputBox) { + this.filterInputBox.blur(); + } + } + + setFocusable(): void { + // noop input elements are focusable by default + } + get trapsArrowNavigation(): boolean { return true; } diff --git a/src/vs/workbench/contrib/notebook/browser/constants.ts b/src/vs/workbench/contrib/notebook/browser/constants.ts index ab61c29ec14..f1121eaea24 100644 --- a/src/vs/workbench/contrib/notebook/browser/constants.ts +++ b/src/vs/workbench/contrib/notebook/browser/constants.ts @@ -14,12 +14,12 @@ export const CODE_CELL_LEFT_MARGIN = 32; export const EDITOR_TOOLBAR_HEIGHT = 0; export const BOTTOM_CELL_TOOLBAR_GAP = 16; -export const BOTTOM_CELL_TOOLBAR_HEIGHT = 24; +export const BOTTOM_CELL_TOOLBAR_HEIGHT = 20; export const CELL_STATUSBAR_HEIGHT = 22; // Margin above editor -export const CELL_TOP_MARGIN = 6; -export const CELL_BOTTOM_MARGIN = 6; +export const CELL_TOP_MARGIN = 10; +export const CELL_BOTTOM_MARGIN = 10; // Top and bottom padding inside the monaco editor in a cell, which are included in `cell.editorHeight` // export const EDITOR_TOP_PADDING = 12; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index 304e86c73c6..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[] = []; @@ -1625,7 +1690,7 @@ export class ChangeCellLanguageAction extends NotebookCellAction { const quickInputService = accessor.get(IQuickInputService); const providerLanguages = [ - ...(context.notebookEditor.activeKernel?.supportedLanguages ?? context.notebookEditor.viewModel.notebookDocument.resolvedLanguages), + ...(context.notebookEditor.activeKernel?.supportedLanguages ?? modeService.getRegisteredModes()), 'markdown' ]; providerLanguages.forEach(languageId => { @@ -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 + ); } } @@ -2108,7 +2177,7 @@ CommandsRegistry.registerCommand('_resolveNotebookKernels', async (accessor, arg const notebookService = accessor.get(INotebookService); const uri = URI.revive(args.uri as UriComponents); const source = new CancellationTokenSource(); - const kernels = await notebookService.getContributedNotebookKernels(args.viewType, uri, source.token); + const kernels = await notebookService.getNotebookKernels(args.viewType, uri, source.token); source.dispose(); return kernels.map(provider => ({ diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts b/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts index 2858f29bf52..5b3ec908aef 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts @@ -14,7 +14,7 @@ import { INotebookEditor, NOTEBOOK_IS_ACTIVE_EDITOR } from 'vs/workbench/contrib import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; -import { INotebookKernelInfo2 } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookKernel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; @@ -213,7 +213,7 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution { } } - showKernelStatus(kernel: INotebookKernelInfo2 | undefined) { + showKernelStatus(kernel: INotebookKernel | undefined) { this.kernelInfoElement.value = this._statusbarService.addEntry({ text: kernel ? kernel.label : 'Choose Kernel', ariaLabel: kernel ? kernel.label : 'Choose Kernel', diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index 81390f3d9ae..8112dbcedc2 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -610,6 +610,10 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD return cellInfo.diffElement.getCellByUri(cellInfo.cellUri); } + getCellById(cellId: string): IGenericCellViewModel | undefined { + throw new Error('Not implemented'); + } + removeInset(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: DiffNestedCellViewModel, displayOutput: ICellOutputViewModel, diffSide: DiffSide) { this._insetModifyQueueByOutputId.queue(displayOutput.model.outputId + (diffSide === DiffSide.Modified ? '-right' : 'left'), async () => { const activeWebview = diffSide === DiffSide.Modified ? this._modifiedWebview : this._originalWebview; diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index 50e545cf130..424086d7b19 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -281,27 +281,51 @@ height: 1px; } +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-left:before, +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-right:before { + content: ""; + position: absolute; + width: 1px; + height: 100%; + z-index: 1; +} + /* top border */ -.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-top:before, -.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused .cell-inner-container:before { +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-top:before { border-top: 1px solid transparent; } +/* left border */ +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-left:before { + border-left: 1px solid transparent; +} + /* bottom border */ -.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-bottom:before, -.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused .cell-inner-container:after { +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-bottom:before { border-bottom: 1px solid transparent; } -.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-top:before, -.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused .cell-inner-container:before { +/* right border */ +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-right:before { + border-right: 1px solid transparent; +} + +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-top:before { top: 0; } -.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-bottom:before, -.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused .cell-inner-container:after { + +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-left:before { + left: 0; +} + +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-bottom:before { bottom: 0px; } +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-right:before { + right: 0; +} + .monaco-workbench.hc-black .notebookOverlay .monaco-list-row .cell-editor-focus .cell-editor-part:before { outline-style: dashed; } @@ -316,7 +340,7 @@ display: inline-flex; position: absolute; height: 26px; - top: -12px; + top: -14px; /* this lines up the bottom toolbar border with the current line when on line 01 */ z-index: 30; } @@ -508,6 +532,7 @@ .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list:focus-within > .monaco-scrollable-element > .monaco-list-rows:not(:hover) > .monaco-list-row.focused .cell-has-toolbar-actions .cell-title-toolbar, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover .cell-has-toolbar-actions .cell-title-toolbar, +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .markdown-cell-hover.cell-has-toolbar-actions .cell-title-toolbar, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-has-toolbar-actions.cell-output-hover .cell-title-toolbar, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-has-toolbar-actions:hover .cell-title-toolbar { visibility: visible; @@ -613,6 +638,7 @@ .monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container:focus-within, .monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container:hover, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover .cell-bottom-toolbar-container, +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .markdown-cell-hover .cell-bottom-toolbar-container, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list:focus-within > .monaco-scrollable-element > .monaco-list-rows:not(:hover) > .monaco-list-row.focused .cell-bottom-toolbar-container, .monaco-workbench .notebookOverlay.notebook-editor-editable > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container:focus-within { opacity: 1; @@ -620,7 +646,7 @@ .monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container .monaco-toolbar, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .monaco-toolbar { - margin: 0px 8px; + margin: -8px 0px 8px; } .monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container .monaco-toolbar .action-item, @@ -806,7 +832,7 @@ width: 20px; position: absolute; - top: 6px; + top: 10px; left: 8px; display: flex; justify-content: center; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 8f209eb0aed..192e9bd0c68 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -22,7 +22,7 @@ import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/outpu import { RunStateRenderer, TimerRenderer } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer'; import { CellViewModel, IModelDecorationsChangeAccessor, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { CellKind, NotebookCellMetadata, NotebookDocumentMetadata, IEditor, INotebookKernelInfo2, ICellRange, IOrderedMimeType, INotebookRendererInfo, ICellOutput, IOutputItemDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, NotebookCellMetadata, NotebookDocumentMetadata, IEditor, INotebookKernel, ICellRange, IOrderedMimeType, INotebookRendererInfo, ICellOutput, IOutputItemDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { IMenu } from 'vs/platform/actions/common/actions'; @@ -64,6 +64,7 @@ export const NOTEBOOK_CELL_INPUT_COLLAPSED = new RawContextKey('noteboo export const NOTEBOOK_CELL_OUTPUT_COLLAPSED = new RawContextKey('notebookCellOutputIsCollapsed', false); // bool // Kernels export const NOTEBOOK_HAS_MULTIPLE_KERNELS = new RawContextKey('notebookHasMultipleKernels', false); +export const NOTEBOOK_KERNEL_COUNT = new RawContextKey('notebookKernelCount', 0); //#endregion @@ -161,6 +162,7 @@ export interface ICommonNotebookEditor { getCellOutputLayoutInfo(cell: IGenericCellViewModel): INotebookCellOutputLayoutInfo; triggerScroll(event: IMouseWheelEvent): void; getCellByInfo(cellInfo: ICommonCellInfo): IGenericCellViewModel; + getCellById(cellId: string): IGenericCellViewModel | undefined; focusNotebookCell(cell: IGenericCellViewModel, focus: 'editor' | 'container' | 'output'): void; focusNextNotebookCell(cell: IGenericCellViewModel, focus: 'editor' | 'container' | 'output'): void; updateOutputHeight(cellInfo: ICommonCellInfo, output: IDisplayOutputViewModel, height: number, isInit: boolean): void; @@ -338,7 +340,7 @@ export interface INotebookEditor extends IEditor, ICommonNotebookEditor { readonly onDidChangeModel: Event; readonly onDidFocusEditorWidget: Event; readonly isNotebookEditor: boolean; - activeKernel: INotebookKernelInfo2 | undefined; + activeKernel: INotebookKernel | undefined; multipleKernelsAvailable: boolean; readonly onDidChangeAvailableKernels: Event; readonly onDidChangeKernel: Event; @@ -384,7 +386,7 @@ export interface INotebookEditor extends IEditor, ICommonNotebookEditor { /** * Fetch the contributed kernels for this notebook */ - beginComputeContributedKernels(): Promise; + beginComputeContributedKernels(): Promise; /** * Insert a new cell around `cell` @@ -603,6 +605,7 @@ export interface INotebookEditor extends IEditor, ICommonNotebookEditor { getContribution(id: string): T; getCellByInfo(cellInfo: ICommonCellInfo): ICellViewModel; + getCellById(cellId: string): ICellViewModel | undefined; updateOutputHeight(cellInfo: ICommonCellInfo, output: IDisplayOutputViewModel, height: number, isInit: boolean): void; } @@ -678,6 +681,7 @@ export interface BaseCellRenderTemplate { deleteToolbar: ToolBar; betweenCellToolbar: ToolBar; focusIndicatorLeft: HTMLElement; + focusIndicatorRight: HTMLElement; disposables: DisposableStore; elementDisposables: DisposableStore; bottomCellContainer: HTMLElement; @@ -772,14 +776,15 @@ export enum CursorAtBoundary { } export interface CellViewModelStateChangeEvent { - metadataChanged?: boolean; - selectionChanged?: boolean; - focusModeChanged?: boolean; - editStateChanged?: boolean; - languageChanged?: boolean; - foldingStateChanged?: boolean; - contentChanged?: boolean; - outputIsHoveredChanged?: boolean; + readonly metadataChanged?: boolean; + readonly selectionChanged?: boolean; + readonly focusModeChanged?: boolean; + readonly editStateChanged?: boolean; + readonly languageChanged?: boolean; + readonly foldingStateChanged?: boolean; + readonly contentChanged?: boolean; + readonly outputIsHoveredChanged?: boolean; + readonly cellIsHoveredChanged?: boolean; } export function cellRangesEqual(a: ICellRange[], b: ICellRange[]) { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts index 8442f6e3781..604cad0e755 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts @@ -12,8 +12,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; import { IReference } from 'vs/base/common/lifecycle'; -import { INotebookEditorModel, INotebookDiffEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { NotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookEditorModel'; +import { INotebookDiffEditorModel, IResolvedNotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; interface NotebookEditorInputOptions { startDirty?: boolean; @@ -21,8 +20,8 @@ interface NotebookEditorInputOptions { class NotebookDiffEditorModel extends EditorModel implements INotebookDiffEditorModel { constructor( - readonly original: NotebookEditorModel, - readonly modified: NotebookEditorModel, + readonly original: IResolvedNotebookEditorModel, + readonly modified: IResolvedNotebookEditorModel, ) { super(); } @@ -54,8 +53,8 @@ export class NotebookDiffEditorInput extends EditorInput { static readonly ID: string = 'workbench.input.diffNotebookInput'; - private _textModel: IReference | null = null; - private _originalTextModel: IReference | null = null; + private _textModel: IReference | null = null; + private _originalTextModel: IReference | null = null; private _defaultDirtyState: boolean = false; constructor( @@ -186,10 +185,12 @@ ${patterns} if (!this._textModel) { this._textModel = await this._notebookModelResolverService.resolve(this.resource, this.viewType!); + } + if (!this._originalTextModel) { this._originalTextModel = await this._notebookModelResolverService.resolve(this.originalResource, this.viewType!); } - return new NotebookDiffEditorModel(this._originalTextModel!.object as NotebookEditorModel, this._textModel.object as NotebookEditorModel); + return new NotebookDiffEditorModel(this._originalTextModel.object, this._textModel.object); } matches(otherInput: unknown): boolean { @@ -204,10 +205,10 @@ ${patterns} } dispose() { - if (this._textModel) { - this._textModel.dispose(); - this._textModel = null; - } + this._textModel?.dispose(); + this._textModel = null; + this._originalTextModel?.dispose(); + this._originalTextModel = null; super.dispose(); } } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index 1025ccaf67b..7128a683466 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -173,7 +173,7 @@ export class NotebookEditor extends EditorPane { label: localize('fail.reOpen', "Reopen file with VS Code standard text editor"), run: async () => { const fileEditorInput = this._editorService.createEditorInput({ resource: input.resource, forceFile: true }); - const textOptions: IEditorOptions | ITextEditorOptions = options ? { ...options, override: false } : { override: false }; + const textOptions: IEditorOptions | ITextEditorOptions = { ...options, override: false }; await this._editorService.openEditor(fileEditorInput, textOptions); } }] diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts index 80bb8e33897..dd6cb29677a 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts @@ -12,7 +12,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; import { IReference } from 'vs/base/common/lifecycle'; -import { INotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { IResolvedNotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; interface NotebookEditorInputOptions { startDirty?: boolean; @@ -25,7 +25,7 @@ export class NotebookEditorInput extends EditorInput { static readonly ID: string = 'workbench.input.notebook'; - private _textModel: IReference | null = null; + private _textModel: IReference | null = null; private _defaultDirtyState: boolean = false; constructor( @@ -153,18 +153,19 @@ ${patterns} return; } - async resolve(): Promise { + async resolve(): Promise { if (!await this._notebookService.canResolve(this.viewType!)) { return null; } 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 895fa91331c..2715b9f4f05 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -14,7 +14,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Color, RGBA } from 'vs/base/common/color'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { combinedDisposable, Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { combinedDisposable, Disposable, DisposableStore, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ScrollEvent } from 'vs/base/common/scrollable'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; @@ -41,7 +41,7 @@ import { IEditorMemento } from 'vs/workbench/common/editor'; import { Memento, MementoObject } from 'vs/workbench/common/memento'; import { PANEL_BORDER } from 'vs/workbench/common/theme'; import { BOTTOM_CELL_TOOLBAR_GAP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CELL_MARGIN, CELL_OUTPUT_PADDING, CELL_RUN_GUTTER, CELL_TOP_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT, SCROLLABLE_ELEMENT_PADDING_TOP } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CellEditState, CellFocusMode, IActiveNotebookEditor, ICellOutputViewModel, ICellViewModel, ICommonCellInfo, IDisplayOutputLayoutUpdateRequest, IGenericCellViewModel, IInsetRenderOutput, INotebookCellList, INotebookCellOutputLayoutInfo, INotebookDeltaDecoration, INotebookEditor, INotebookEditorContribution, INotebookEditorContributionDescription, INotebookEditorCreationOptions, INotebookEditorMouseEvent, NotebookEditorOptions, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_HAS_MULTIPLE_KERNELS, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellEditState, CellFocusMode, IActiveNotebookEditor, ICellOutputViewModel, ICellViewModel, ICommonCellInfo, IDisplayOutputLayoutUpdateRequest, IGenericCellViewModel, IInsetRenderOutput, INotebookCellList, INotebookCellOutputLayoutInfo, INotebookDeltaDecoration, INotebookEditor, INotebookEditorContribution, INotebookEditorContributionDescription, INotebookEditorCreationOptions, INotebookEditorMouseEvent, NotebookEditorOptions, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_HAS_MULTIPLE_KERNELS, NOTEBOOK_KERNEL_COUNT, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEditorExtensionsRegistry } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; import { NotebookKernelProviderAssociation, NotebookKernelProviderAssociations, notebookKernelProviderAssociationsSettingId } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation'; import { NotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList'; @@ -54,7 +54,7 @@ import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewMod import { NotebookEventDispatcher, NotebookLayoutChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { CellViewModel, IModelDecorationsChangeAccessor, INotebookEditorViewState, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CellKind, CellToolbarLocKey, ICellRange, INotebookDecorationRenderOptions, INotebookKernelInfo2, NotebookCellRunState, NotebookRunState, ShowCellStatusBarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, CellToolbarLocKey, ICellRange, INotebookDecorationRenderOptions, INotebookKernel, NotebookCellRunState, NotebookRunState, ShowCellStatusBarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { editorGutterModifiedBackground } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator'; @@ -64,6 +64,7 @@ import { configureKernelIcon, errorStateIcon, successStateIcon } from 'vs/workbe import { debugIconStartForeground } from 'vs/workbench/contrib/debug/browser/debugColors'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { extname } from 'vs/base/common/resources'; +import { IModeService } from 'vs/editor/common/services/modeService'; import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; const $ = DOM.$; @@ -91,14 +92,16 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private _dimension: DOM.Dimension | null = null; private _shadowElementViewInfo: { height: number, width: number, top: number; left: number; } | null = null; - private _editorFocus: IContextKey | null = null; - private _outputFocus: IContextKey | null = null; - private _editorEditable: IContextKey | null = null; - private _editorRunnable: IContextKey | null = null; - private _notebookExecuting: IContextKey | null = null; - private _notebookHasMultipleKernels: IContextKey | null = null; + private readonly _editorFocus: IContextKey; + private readonly _outputFocus: IContextKey; + private readonly _editorEditable: IContextKey; + private readonly _editorRunnable: IContextKey; + private readonly _notebookExecuting: IContextKey; + private readonly _notebookHasMultipleKernels: IContextKey; + private readonly _notebookKernelCount: IContextKey; + private _outputRenderer: OutputRenderer; - protected readonly _contributions: { [key: string]: INotebookEditorContribution; }; + protected readonly _contributions = new Map(); private _scrollBeyondLastLine: boolean; private readonly _memento: Memento; private readonly _activeKernelMemento: Memento; @@ -150,20 +153,20 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } private _activeKernelExecuted: boolean = false; - private _activeKernel: INotebookKernelInfo2 | undefined = undefined; + private _activeKernel: INotebookKernel | undefined = undefined; private readonly _onDidChangeKernel = this._register(new Emitter()); readonly onDidChangeKernel: Event = this._onDidChangeKernel.event; private readonly _onDidChangeAvailableKernels = this._register(new Emitter()); readonly onDidChangeAvailableKernels: Event = this._onDidChangeAvailableKernels.event; - private _contributedKernelsComputePromise: CancelablePromise | null = null; + private _contributedKernelsComputePromise: CancelablePromise | null = null; private _initialKernelComputationDone: boolean = false; get activeKernel() { return this._activeKernel; } - set activeKernel(kernel: INotebookKernelInfo2 | undefined) { + set activeKernel(kernel: INotebookKernel | undefined) { if (this._isDisposed) { return; } @@ -257,7 +260,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor @IMenuService private readonly menuService: IMenuService, @IQuickInputService private readonly quickInputService: IQuickInputService, @IThemeService private readonly themeService: IThemeService, - @ITelemetryService private readonly telemetryService: ITelemetryService + @ITelemetryService private readonly telemetryService: ITelemetryService, + @IModeService private readonly modeService: IModeService, ) { super(); this.isEmbedded = creationOptions.isEmbedded || false; @@ -270,7 +274,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._activeKernelMemento = new Memento(NotebookEditorActiveKernelCache, storageService); this._outputRenderer = new OutputRenderer(this, this.instantiationService); - this._contributions = {}; this._scrollBeyondLastLine = this.configurationService.getValue('editor.scrollBeyondLastLine'); this.configurationService.onDidChangeConfiguration(e => { @@ -287,7 +290,41 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor }); this.notebookService.addNotebookEditor(this); - this._createEditor(); + + const id = generateUuid(); + this._overlayContainer.id = `notebook-${id}`; + this._overlayContainer.className = 'notebookOverlay'; + this._overlayContainer.classList.add('notebook-editor'); + this._overlayContainer.style.visibility = 'hidden'; + + this.layoutService.container.appendChild(this._overlayContainer); + this._createBody(this._overlayContainer); + this._generateFontInfo(); + this._isVisible = true; + this._editorFocus = NOTEBOOK_EDITOR_FOCUSED.bindTo(this.scopedContextKeyService); + this._outputFocus = NOTEBOOK_OUTPUT_FOCUSED.bindTo(this.scopedContextKeyService); + this._editorEditable = NOTEBOOK_EDITOR_EDITABLE.bindTo(this.scopedContextKeyService); + this._editorRunnable = NOTEBOOK_EDITOR_RUNNABLE.bindTo(this.scopedContextKeyService); + this._notebookExecuting = NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK.bindTo(this.scopedContextKeyService); + this._notebookHasMultipleKernels = NOTEBOOK_HAS_MULTIPLE_KERNELS.bindTo(this.scopedContextKeyService); + this._notebookKernelCount = NOTEBOOK_KERNEL_COUNT.bindTo(this.scopedContextKeyService); + + let contributions: INotebookEditorContributionDescription[]; + if (Array.isArray(this.creationOptions.contributions)) { + contributions = this.creationOptions.contributions; + } else { + contributions = NotebookEditorExtensionsRegistry.getEditorContributions(); + } + for (const desc of contributions) { + try { + const contribution = this.instantiationService.createInstance(desc.ctor, this); + this._contributions.set(desc.id, contribution); + } catch (err) { + onUnexpectedError(err); + } + } + + this._updateForNotebookConfiguration(); } /** @@ -357,12 +394,12 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor // Note - focus going to the webview will fire 'blur', but the webview element will be // a descendent of the notebook editor root. const focused = DOM.isAncestor(document.activeElement, this._overlayContainer); - this._editorFocus?.set(focused); + this._editorFocus.set(focused); this.viewModel?.setFocus(focused); } hasFocus() { - return this._editorFocus?.get() || false; + return this._editorFocus.get() || false; } hasWebviewFocus() { @@ -403,46 +440,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return false; } - private _createEditor(): void { - const id = generateUuid(); - this._overlayContainer.id = `notebook-${id}`; - this._overlayContainer.className = 'notebookOverlay'; - this._overlayContainer.classList.add('notebook-editor'); - this._overlayContainer.style.visibility = 'hidden'; - - this.layoutService.container.appendChild(this._overlayContainer); - this._createBody(this._overlayContainer); - this._generateFontInfo(); - this._editorFocus = NOTEBOOK_EDITOR_FOCUSED.bindTo(this.scopedContextKeyService); - this._isVisible = true; - this._outputFocus = NOTEBOOK_OUTPUT_FOCUSED.bindTo(this.scopedContextKeyService); - this._editorEditable = NOTEBOOK_EDITOR_EDITABLE.bindTo(this.scopedContextKeyService); - this._editorEditable.set(true); - this._editorRunnable = NOTEBOOK_EDITOR_RUNNABLE.bindTo(this.scopedContextKeyService); - this._editorRunnable.set(true); - this._notebookExecuting = NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK.bindTo(this.scopedContextKeyService); - this._notebookHasMultipleKernels = NOTEBOOK_HAS_MULTIPLE_KERNELS.bindTo(this.scopedContextKeyService); - this._notebookHasMultipleKernels.set(false); - - let contributions: INotebookEditorContributionDescription[]; - if (Array.isArray(this.creationOptions.contributions)) { - contributions = this.creationOptions.contributions; - } else { - contributions = NotebookEditorExtensionsRegistry.getEditorContributions(); - } - - for (const desc of contributions) { - try { - const contribution = this.instantiationService.createInstance(desc.ctor, this); - this._contributions[desc.id] = contribution; - } catch (err) { - onUnexpectedError(err); - } - } - - this._updateForNotebookConfiguration(); - } - private _generateFontInfo(): void { const editorOptions = this.configurationService.getValue('editor'); this._fontInfo = BareFontInfo.createFromRawSettings(editorOptions, getZoomLevel(), getPixelRatio()); @@ -463,7 +460,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._body.classList.add('cell-list-container'); this._dndController = this._register(new CellDragAndDropController(this, this._body)); - const getScopedContextKeyService = (container?: HTMLElement) => this._list.contextKeyService.createScoped(container); + const getScopedContextKeyService = (container: HTMLElement) => this._list.contextKeyService.createScoped(container); const renderers = [ this.instantiationService.createInstance(CodeCellRenderer, this, this._renderedEditors, this._dndController, getScopedContextKeyService), this.instantiationService.createInstance(MarkdownCellRenderer, this, this._dndController, this._renderedEditors, getScopedContextKeyService), @@ -628,7 +625,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor onWillHide() { this._isVisible = false; - this._editorFocus?.set(false); + this._editorFocus.set(false); this._overlayContainer.style.visibility = 'hidden'; this._overlayContainer.style.left = '-50000px'; } @@ -639,7 +636,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor focus() { this._isVisible = true; - this._editorFocus?.set(true); + this._editorFocus.set(true); if (this._webiewFocused) { this._webview?.focusWebview(); @@ -775,7 +772,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } this._contributedKernelsComputePromise = createCancelablePromise(token => { - return this.notebookService.getContributedNotebookKernels(this.viewModel!.viewType, this.viewModel!.uri, token); + return this.notebookService.getNotebookKernels(this.viewModel!.viewType, this.viewModel!.uri, token); }); const result = await this._contributedKernelsComputePromise; @@ -802,15 +799,12 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return; } - if (tokenSource.token.isCancellationRequested) { - return; - } - - if ((availableKernels.length) > 1) { - this._notebookHasMultipleKernels!.set(true); + this._notebookKernelCount.set(availableKernels.length); + if (availableKernels.length > 1) { + this._notebookHasMultipleKernels.set(true); this.multipleKernelsAvailable = true; } else { - this._notebookHasMultipleKernels!.set(false); + this._notebookHasMultipleKernels.set(false); this.multipleKernelsAvailable = false; } @@ -830,7 +824,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor tokenSource.dispose(); } - private async _setKernelsFromProviders(provider: NotebookProviderInfo, kernels: INotebookKernelInfo2[], tokenSource: CancellationTokenSource) { + private async _setKernelsFromProviders(provider: NotebookProviderInfo, kernels: INotebookKernel[], tokenSource: CancellationTokenSource) { const rawAssociations = this.configurationService.getValue(notebookKernelProviderAssociationsSettingId) || []; const userSetKernelProvider = rawAssociations.filter(e => e.viewType === this.viewModel?.viewType)[0]?.kernelProvider; const memento = this._activeKernelMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); @@ -916,7 +910,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor tokenSource.dispose(); } - private async _loadKernelPreloads(extensionLocation: URI, kernel: INotebookKernelInfo2) { + private async _loadKernelPreloads(extensionLocation: URI, kernel: INotebookKernel) { if (kernel.preloads && kernel.preloads.length) { await this._resolveWebview(); this._webview?.updateKernelPreloads([extensionLocation], kernel.preloads.map(preload => URI.revive(preload))); @@ -929,12 +923,12 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } const notebookMetadata = this.viewModel.metadata; - this._editorEditable?.set(!!notebookMetadata?.editable); - this._editorRunnable?.set(this.viewModel.runnable); + this._editorEditable.set(!!notebookMetadata?.editable); + this._editorRunnable.set(this.viewModel.runnable); this._overflowContainer.classList.toggle('notebook-editor-editable', !!notebookMetadata?.editable); this.getDomNode().classList.toggle('notebook-editor-editable', !!notebookMetadata?.editable); - this._notebookExecuting?.set(notebookMetadata.runState === NotebookRunState.Running); + this._notebookExecuting.set(notebookMetadata.runState === NotebookRunState.Running); } private async _resolveWebview(): Promise | null> { @@ -966,7 +960,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } this._webview.webview.onDidBlur(() => { - this._outputFocus?.set(false); + this._outputFocus.set(false); this.updateEditorFocus(); if (this._overlayContainer.contains(document.activeElement)) { @@ -974,7 +968,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } }); this._webview.webview.onDidFocus(() => { - this._outputFocus?.set(true); + this._outputFocus.set(true); this.updateEditorFocus(); this._onDidFocusEmitter.fire(); @@ -1039,10 +1033,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor // contribution state restore const contributionsState = viewState?.contributionsState || {}; - const keys = Object.keys(this._contributions); - for (let i = 0, len = keys.length; i < len; i++) { - const id = keys[i]; - const contribution = this._contributions[id]; + for (const [id, contribution] of this._contributions) { if (typeof contribution.restoreViewState === 'function') { contribution.restoreViewState(contributionsState[id]); } @@ -1217,10 +1208,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor // Save contribution view states const contributionsState: { [key: string]: unknown } = {}; - - const keys = Object.keys(this._contributions); - for (const id of keys) { - const contribution = this._contributions[id]; + for (const [id, contribution] of this._contributions) { if (typeof contribution.saveViewState === 'function') { contributionsState[id] = contribution.saveViewState(); } @@ -1496,7 +1484,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor const nextIndex = ui ? this.viewModel.getNextVisibleCellIndex(index) : index + 1; let language; if (type === CellKind.Code) { - const supportedLanguages = this._activeKernel?.supportedLanguages ?? this.viewModel.notebookDocument.resolvedLanguages; + const supportedLanguages = this._activeKernel?.supportedLanguages ?? this.modeService.getRegisteredModes(); const defaultLanguage = supportedLanguages[0] || 'plaintext'; if (cell?.cellKind === CellKind.Code) { language = cell.language; @@ -2092,6 +2080,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return this.viewModel?.viewCells.find(vc => vc.handle === cellHandle) as CodeCellViewModel; } + getCellById(cellId: string): ICellViewModel | undefined { + return this.viewModel?.viewCells.find(vc => vc.id === cellId); + } + updateOutputHeight(cellInfo: ICommonCellInfo, output: ICellOutputViewModel, outputHeight: number, isInit: boolean): void { const cell = this.viewModel?.viewCells.find(vc => vc.handle === cellInfo.cellHandle); if (cell && cell instanceof CodeCellViewModel) { @@ -2102,24 +2094,21 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } updateMarkdownCellHeight(cellId: string, height: number, isInit: boolean) { - const cell = this.viewModel?.viewCells.find(vc => vc.id === cellId); - + const cell = this.getCellById(cellId); if (cell && cell instanceof MarkdownCellViewModel) { cell.renderedMarkdownHeight = height; } } setMarkdownCellEditState(cellId: string, editState: CellEditState): void { - const cell = this.viewModel?.viewCells.find(vc => vc.id === cellId); - + const cell = this.getCellById(cellId); if (cell && cell instanceof MarkdownCellViewModel) { cell.editState = editState; } } markdownCellDragStart(cellId: string, ctx: { clientY: number }): void { - const cell = this.viewModel?.viewCells.find(vc => vc.id === cellId); - + const cell = this.getCellById(cellId); if (cell && cell instanceof MarkdownCellViewModel) { this._dndController?.startExplicitDrag(cell, ctx); } @@ -2144,8 +2133,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor //#endregion //#region Editor Contributions - public getContribution(id: string): T { - return (this._contributions[id] || null); + getContribution(id: string): T { + return (this._contributions.get(id) || null); } //#endregion @@ -2157,11 +2146,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._webview?.dispose(); this.notebookService.removeNotebookEditor(this); - const keys = Object.keys(this._contributions); - for (let i = 0, len = keys.length; i < len; i++) { - const contributionId = keys[i]; - this._contributions[contributionId].dispose(); - } + dispose(this._contributions.values()); + this._contributions.clear(); this._localStore.clear(); this._list.dispose(); @@ -2395,16 +2381,15 @@ registerThemingParticipant((theme, collector) => { collector.addRule(` .monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-focus-indicator-top:before, .monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-focus-indicator-bottom:before, - .monaco-workbench .notebookOverlay .monaco-list:focus-within .markdown-cell-row.focused .cell-inner-container:not(.cell-editor-focus):before { + .monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-inner-container:not(.cell-editor-focus) .cell-focus-indicator-left:before, + .monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-inner-container:not(.cell-editor-focus) .cell-focus-indicator-right:before { border-color: ${focusedCellBorderColor} !important; }`); const inactiveFocusedBorderColor = theme.getColor(inactiveFocusedCellBorder); collector.addRule(` .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-top:before, - .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-bottom:before, - .monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused .cell-inner-container:not(.cell-editor-focus):before, - .monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused .cell-inner-container:not(.cell-editor-focus):after { + .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-bottom:before { border-color: ${inactiveFocusedBorderColor} !important; }`); @@ -2463,22 +2448,19 @@ registerThemingParticipant((theme, collector) => { const scrollbarSliderBackgroundColor = theme.getColor(listScrollbarSliderBackground); if (scrollbarSliderBackgroundColor) { - collector.addRule(` .notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .scrollbar > .slider { background: ${editorBackgroundColor}; } `); - collector.addRule(` .notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .scrollbar > .slider:before { content: ""; width: 100%; height: 100%; position: absolute; background: ${scrollbarSliderBackgroundColor}; } `); /* hack to not have cells see through scroller */ + collector.addRule(` .notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .scrollbar > .slider { background: ${scrollbarSliderBackgroundColor}; } `); // collector.addRule(` .monaco-workbench .notebookOverlay .output-plaintext::-webkit-scrollbar-track { background: ${scrollbarSliderBackgroundColor}; } `); } const scrollbarSliderHoverBackgroundColor = theme.getColor(listScrollbarSliderHoverBackground); if (scrollbarSliderHoverBackgroundColor) { - collector.addRule(` .notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .scrollbar > .slider:hover { background: ${editorBackgroundColor}; } `); - collector.addRule(` .notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .scrollbar > .slider:hover:before { content: ""; width: 100%; height: 100%; position: absolute; background: ${scrollbarSliderHoverBackgroundColor}; } `); /* hack to not have cells see through scroller */ + collector.addRule(` .notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .scrollbar > .slider:hover { background: ${scrollbarSliderHoverBackgroundColor}; } `); collector.addRule(` .monaco-workbench .notebookOverlay .output-plaintext::-webkit-scrollbar-thumb { background: ${scrollbarSliderHoverBackgroundColor}; } `); } const scrollbarSliderActiveBackgroundColor = theme.getColor(listScrollbarSliderActiveBackground); if (scrollbarSliderActiveBackgroundColor) { - collector.addRule(` .notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .scrollbar > .slider.active { background: ${editorBackgroundColor}; } `); - collector.addRule(` .notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .scrollbar > .slider.active:before { content: ""; width: 100%; height: 100%; position: absolute; background: ${scrollbarSliderActiveBackgroundColor}; } `); /* hack to not have cells see through scroller */ + collector.addRule(` .notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .scrollbar > .slider.active { background: ${scrollbarSliderActiveBackgroundColor}; } `); } // case ChangeType.Modify: return theme.getColor(editorGutterModifiedBackground); @@ -2547,6 +2529,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)}`); }); @@ -2625,15 +2608,12 @@ class DecorationCSSRules { const borderColor = this._resolveValue(this._providerArgs.options.borderColor); this._styleSheet.insertRule(`.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.${this.className} .cell-focus-indicator-top:before, - .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.${this.className} .cell-focus-indicator-bottom:before, - .monaco-workbench .notebookOverlay .monaco-list .${this.className}.markdown-cell-row.focused:before, - .monaco-workbench .notebookOverlay .monaco-list .${this.className}.markdown-cell-row.focused:after { + .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.${this.className} .cell-focus-indicator-bottom:before { border-color: ${borderColor} !important; }`); this._styleSheet.insertRule(` - .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.${this.className} .cell-focus-indicator-bottom:before, - .monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.${this.className}:after { + .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.${this.className} .cell-focus-indicator-bottom:before { content: ""; position: absolute; width: 100%; @@ -2643,8 +2623,7 @@ class DecorationCSSRules { `); this._styleSheet.insertRule(` - .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.${this.className} .cell-focus-indicator-top:before, - .monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.${this.className}:before { + .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.${this.className} .cell-focus-indicator-top:before { content: ""; position: absolute; width: 100%; @@ -2654,9 +2633,7 @@ class DecorationCSSRules { // more specific rule for `.focused` can override existing rules this._styleSheet.insertRule(`.monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused.${this.className} .cell-focus-indicator-top:before, - .monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused.${this.className} .cell-focus-indicator-bottom:before, - .monaco-workbench .notebookOverlay .monaco-list:focus-within .markdown-cell-row.focused.${this.className}:before, - .monaco-workbench .notebookOverlay .monaco-list:focus-within .markdown-cell-row.focused.${this.className}:after { + .monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused.${this.className} .cell-focus-indicator-bottom:before { border-color: ${borderColor} !important; }`); } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 8587d657663..f0ae2a42917 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -10,7 +10,6 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Iterable } from 'vs/base/common/iterator'; import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ResourceMap } from 'vs/base/common/map'; -import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import * as UUID from 'vs/base/common/uuid'; import { RedoCommand, UndoCommand } from 'vs/editor/browser/editorExtensions'; @@ -32,7 +31,7 @@ import { NotebookKernelProviderAssociationRegistry, NotebookViewTypesExtensionRe import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellEditType, CellKind, DisplayOrderKey, ICellEditOperation, INotebookDecorationRenderOptions, INotebookKernelInfo2, INotebookKernelProvider, INotebookMarkdownRendererInfo, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, IOutputDto, mimeTypeIsAlwaysSecure, mimeTypeSupportedByCore, notebookDocumentFilterMatch, NotebookEditorPriority, NOTEBOOK_DISPLAY_ORDER, RENDERER_NOT_AVAILABLE, sortMimeTypes } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellEditType, CellKind, DisplayOrderKey, ICellEditOperation, INotebookDecorationRenderOptions, INotebookKernel, INotebookKernelProvider, INotebookMarkdownRendererInfo, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, IOutputDto, mimeTypeIsAlwaysSecure, mimeTypeSupportedByCore, NotebookDataDto, notebookDocumentFilterMatch, NotebookEditorPriority, NOTEBOOK_DISPLAY_ORDER, RENDERER_NOT_AVAILABLE, sortMimeTypes, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookMarkdownRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookMarkdownRenderer'; import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer'; import { NotebookEditorDescriptor, NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; @@ -680,6 +679,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)) { @@ -721,42 +723,18 @@ export class NotebookService extends Disposable implements INotebookService, ICu return toDisposable(() => { kernelChangeEventListener.dispose(); d.dispose(); + this._onDidChangeKernels.fire(undefined); }); } - async getContributedNotebookKernels(viewType: string, resource: URI, token: CancellationToken): Promise { + async getNotebookKernels(viewType: string, resource: URI, token: CancellationToken): Promise { const filteredProvider = this.notebookKernelProviderInfoStore.get(viewType, resource); - const result = new Array(filteredProvider.length); - + const result = new Array(filteredProvider.length); const promises = filteredProvider.map(async (provider, index) => { const data = await provider.provideKernels(resource, token); - result[index] = data.map(dto => { - return { - id: dto.id, - extension: dto.extension, - extensionLocation: URI.revive(dto.extensionLocation), - friendlyId: dto.friendlyId, - label: dto.label, - description: dto.description, - detail: dto.detail, - isPreferred: dto.isPreferred, - preloads: dto.preloads, - providerHandle: dto.providerHandle, - resolve: async (uri: URI, editorId: string, token: CancellationToken) => { - return provider.resolveKernel(editorId, uri, dto.friendlyId, token); - }, - executeNotebookCell: async (uri: URI, handle: number | undefined) => { - return provider.executeNotebook(uri, dto.friendlyId, handle); - }, - cancelNotebookCell: (uri: URI, handle: number | undefined): Promise => { - return provider.cancelNotebook(uri, dto.friendlyId, handle); - } - }; - }); + result[index] = data; }); - await Promise.all(promises); - return flatten(result); } @@ -773,45 +751,22 @@ export class NotebookService extends Disposable implements INotebookService, ICu return Array.from(this.markdownRenderersInfos); } - async resolveNotebook(viewType: string, uri: URI, forceReload: boolean, backupId?: string): Promise { - + async fetchNotebookRawData(viewType: string, uri: URI, backupId?: string): Promise<{ data: NotebookDataDto, transientOptions: TransientOptions }> { if (!await this.canResolve(viewType)) { - throw new Error(`CANNOT load notebook, no provider for '${viewType}'`); + throw new Error(`CANNOT fetch notebook data, there is NO provider for '${viewType}'`); } - const provider = this._notebookProviders.get(viewType)!; - let notebookModel: NotebookTextModel; + return await provider.controller.openNotebook(viewType, uri, backupId); + } + + createNotebookTextModel(viewType: string, uri: URI, data: NotebookDataDto, transientOptions: TransientOptions): NotebookTextModel { if (this._models.has(uri)) { - // the model already exists - notebookModel = this._models.get(uri)!.model; - if (forceReload) { - await provider.controller.reloadNotebook(notebookModel); - } - return notebookModel; - - } else { - const dataDto = await provider.controller.resolveNotebookDocument(viewType, uri, backupId); - let cells = dataDto.data.cells.length ? dataDto.data.cells : (uri.scheme === Schemas.untitled ? [{ - cellKind: CellKind.Code, - language: dataDto.data.languages.length ? dataDto.data.languages[0] : '', - outputs: [], - metadata: undefined, - source: '' - }] : []); - - notebookModel = this._instantiationService.createInstance(NotebookTextModel, viewType, provider.controller.supportBackup, uri, cells, dataDto.data.languages, dataDto.data.metadata, dataDto.transientOptions); + throw new Error(`notebook for ${uri} already exists`); } - - // new notebook model created - const modelData = new ModelData( - notebookModel, - (model) => this._onWillDisposeDocument(model), - ); - - this._models.set(uri, modelData); + const notebookModel = this._instantiationService.createInstance(NotebookTextModel, viewType, true, uri, data.cells, data.metadata, transientOptions); + this._models.set(uri, new ModelData(notebookModel, this._onWillDisposeDocument.bind(this))); this._onDidAddNotebookDocument.fire(notebookModel); - - return modelData.model; + return notebookModel; } getNotebookTextModel(uri: URI): NotebookTextModel | undefined { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index bc6c5860802..7d6765fc046 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as DOM from 'vs/base/browser/dom'; import { VSBuffer } from 'vs/base/common/buffer'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -20,6 +19,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { CellEditState, ICellOutputViewModel, ICommonCellInfo, ICommonNotebookEditor, IDisplayOutputLayoutUpdateRequest, IDisplayOutputViewModel, IGenericCellViewModel, IInsetRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { preloadsScriptStr } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads'; import { transformWebviewThemeVars } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewThemeMapping'; +import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; import { INotebookRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IWebviewService, WebviewContentPurpose, WebviewElement } from 'vs/workbench/contrib/webview/browser/webview'; @@ -35,9 +35,9 @@ export interface IDimensionMessage { __vscode_notebook_message: boolean; type: 'dimension'; id: string; - init: boolean; - data: DOM.Dimension; - isOutput: boolean; + init?: boolean; + data: { height: number }; + isOutput?: boolean; } export interface IMouseEnterMessage { @@ -76,10 +76,28 @@ export interface IBlurOutputMessage { export interface IClickedDataUrlMessage { __vscode_notebook_message: boolean; type: 'clicked-data-url'; - data: string; + data: string | ArrayBuffer | null; downloadName?: string; } +export interface IFocusMarkdownPreviewMessage { + __vscode_notebook_message: boolean; + type: 'focusMarkdownPreview'; + cellId: string; +} + +export interface IMouseEnterMarkdownPreviewMessage { + __vscode_notebook_message: boolean; + type: 'mouseEnterMarkdownPreview'; + cellId: string; +} + +export interface IMouseLeaveMarkdownPreviewMessage { + __vscode_notebook_message: boolean; + type: 'mouseLeaveMarkdownPreview'; + cellId: string; +} + export interface IToggleMarkdownPreviewMessage { __vscode_notebook_message: boolean; type: 'toggleMarkdownPreview'; @@ -111,6 +129,10 @@ export interface ICellDragEndMessage { readonly clientY: number; }; } +export interface IInitializedMarkdownPreviewMessage { + readonly __vscode_notebook_message: boolean; + readonly type: 'initializedMarkdownPreview'; +} export interface IClearMessage { type: 'clear'; @@ -264,10 +286,14 @@ export type FromWebviewMessage = | IBlurOutputMessage | ICustomRendererMessage | IClickedDataUrlMessage + | IFocusMarkdownPreviewMessage + | IMouseEnterMarkdownPreviewMessage + | IMouseLeaveMarkdownPreviewMessage | IToggleMarkdownPreviewMessage | ICellDragStartMessage | ICellDragMessage | ICellDragEndMessage + | IInitializedMarkdownPreviewMessage ; export type ToWebviewMessage = | IClearMessage @@ -783,8 +809,23 @@ var requirejs = (function() { this._onDidClickDataLink(data); } else if (data.type === 'customRendererMessage') { this._onMessage.fire({ message: data.message, forRenderer: data.rendererId }); + } else if (data.type === 'focusMarkdownPreview') { + const cell = this.notebookEditor.getCellById(data.cellId); + if (cell) { + this.notebookEditor.focusNotebookCell(cell, 'container'); + } } else if (data.type === 'toggleMarkdownPreview') { this.notebookEditor.setMarkdownCellEditState(data.cellId, CellEditState.Editing); + } else if (data.type === 'mouseEnterMarkdownPreview') { + const cell = this.notebookEditor.getCellById(data.cellId); + if (cell instanceof MarkdownCellViewModel) { + cell.cellIsHovered = true; + } + } else if (data.type === 'mouseLeaveMarkdownPreview') { + const cell = this.notebookEditor.getCellById(data.cellId); + if (cell instanceof MarkdownCellViewModel) { + cell.cellIsHovered = false; + } } else if (data.type === 'cell-drag-start') { this.notebookEditor.markdownCellDragStart(data.cellId, data.position); } else if (data.type === 'cell-drag') { @@ -804,6 +845,10 @@ var requirejs = (function() { } private async _onDidClickDataLink(event: IClickedDataUrlMessage): Promise { + if (typeof event.data !== 'string') { + return; + } + const [splitStart, splitData] = event.data.split(';base64,'); if (!splitData || !splitStart) { return; 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 2bdc13a43b9..672cf0180cd 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -178,7 +178,7 @@ abstract class AbstractCellRenderer { configurationService: IConfigurationService, private readonly keybindingService: IKeybindingService, private readonly notificationService: INotificationService, - protected readonly contextKeyServiceProvider: (container?: HTMLElement) => IContextKeyService, + protected readonly contextKeyServiceProvider: (container: HTMLElement) => IContextKeyService, language: string, protected readonly dndController: CellDragAndDropController ) { @@ -375,7 +375,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR notebookEditor: INotebookEditor, dndController: CellDragAndDropController, private renderedEditors: Map, - contextKeyServiceProvider: (container?: HTMLElement) => IContextKeyService, + contextKeyServiceProvider: (container: HTMLElement) => IContextKeyService, @IInstantiationService instantiationService: IInstantiationService, @IConfigurationService configurationService: IConfigurationService, @IContextMenuService contextMenuService: IContextMenuService, @@ -400,7 +400,9 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR const deleteToolbar = disposables.add(this.createToolbar(titleToolbarContainer, 'cell-delete-toolbar')); deleteToolbar.setActions([this.instantiationService.createInstance(DeleteCellAction)]); + DOM.append(container, $('.cell-focus-indicator.cell-focus-indicator-top')); const focusIndicatorLeft = DOM.append(container, DOM.$('.cell-focus-indicator.cell-focus-indicator-side.cell-focus-indicator-left')); + const focusIndicatorRight = DOM.append(container, DOM.$('.cell-focus-indicator.cell-focus-indicator-side.cell-focus-indicator-right')); const codeInnerContent = DOM.append(container, $('.cell.code')); const editorPart = DOM.append(codeInnerContent, $('.cell-editor-part')); @@ -434,6 +436,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR editorContainer, focusIndicatorLeft, focusIndicatorBottom, + focusIndicatorRight, foldingIndicator, disposables, elementDisposables: new DisposableStore(), @@ -531,6 +534,13 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR this.updateForLayout(element, templateData); })); + this.updateForHover(element, templateData); + elementDisposables.add(element.onDidChangeState(e => { + if (e.cellIsHoveredChanged) { + this.updateForHover(element, templateData); + } + })); + // render toolbar first this.setupCellToolbarActions(templateData, elementDisposables); @@ -555,6 +565,14 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR private updateForLayout(element: MarkdownCellViewModel, templateData: MarkdownCellRenderTemplate): void { // templateData.focusIndicatorLeft.style.height = `${element.layoutInfo.indicatorHeight}px`; templateData.focusIndicatorBottom.style.top = `${element.layoutInfo.totalHeight - BOTTOM_CELL_TOOLBAR_GAP - CELL_BOTTOM_MARGIN}px`; + + const focusSideHeight = element.layoutInfo.totalHeight - BOTTOM_CELL_TOOLBAR_GAP; + templateData.focusIndicatorLeft.style.height = `${focusSideHeight}px`; + templateData.focusIndicatorRight.style.height = `${focusSideHeight}px`; + } + + private updateForHover(element: MarkdownCellViewModel, templateData: MarkdownCellRenderTemplate): void { + templateData.container.classList.toggle('markdown-cell-hover', element.cellIsHovered); } disposeTemplate(templateData: MarkdownCellRenderTemplate): void { @@ -681,7 +699,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende protected notebookEditor: INotebookEditor, private renderedEditors: Map, dndController: CellDragAndDropController, - contextKeyServiceProvider: (container?: HTMLElement) => IContextKeyService, + contextKeyServiceProvider: (container: HTMLElement) => IContextKeyService, @IContextMenuService contextMenuService: IContextMenuService, @IConfigurationService configurationService: IConfigurationService, @IInstantiationService instantiationService: IInstantiationService, 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 4d5b516c59b..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,9 @@ export class StatefulMarkdownCell extends Disposable { this.templateData.editorContainer.innerText = ''; // create a special context key service that set the inCompositeEditor-contextkey - const editorContextKeyService = this.contextKeyService.createScoped(); - const editorInstaService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, editorContextKeyService])); + 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, { ...this.editorOptions, diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index 9874394d3e5..cba45be0a7a 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -5,8 +5,8 @@ import type { Event } from 'vs/base/common/event'; import type { IDisposable } from 'vs/base/common/lifecycle'; -import { ICellDragEndMessage, ICellDragMessage, ICellDragStartMessage, ToWebviewMessage } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView'; import { RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { FromWebviewMessage, IBlurOutputMessage, ICellDragEndMessage, ICellDragMessage, ICellDragStartMessage, IClickedDataUrlMessage, ICustomRendererMessage, IDimensionMessage, IFocusMarkdownPreviewMessage, IMouseEnterMarkdownPreviewMessage, IMouseEnterMessage, IMouseLeaveMarkdownPreviewMessage, IMouseLeaveMessage, IToggleMarkdownPreviewMessage, IWheelMessage, ToWebviewMessage } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView'; // !! IMPORTANT !! everything must be in-line within the webviewPreloads // function. Imports are not allowed. This is stringifies and injected into @@ -60,9 +60,7 @@ function webviewPreloads() { }; const handleDataUrl = async (data: string | ArrayBuffer | null, downloadName: string) => { - vscode.postMessage({ - __vscode_notebook_message: true, - type: 'clicked-data-url', + postNotebookMessage('clicked-data-url', { data, downloadName }); @@ -146,9 +144,7 @@ function webviewPreloads() { if (entry.target.id === id && entry.contentRect) { if (entry.contentRect.height !== 0) { entry.target.style.padding = `${__outputNodePadding__}px ${__outputNodePadding__}px ${__outputNodePadding__}px ${__outputNodeLeftPadding__}px`; - vscode.postMessage({ - __vscode_notebook_message: true, - type: 'dimension', + postNotebookMessage('dimension', { id: id, data: { height: entry.contentRect.height + __outputNodePadding__ * 2 @@ -157,9 +153,7 @@ function webviewPreloads() { }); } else { entry.target.style.padding = `0px`; - vscode.postMessage({ - __vscode_notebook_message: true, - type: 'dimension', + postNotebookMessage('dimension', { id: id, data: { height: entry.contentRect.height @@ -201,10 +195,7 @@ function webviewPreloads() { if (event.defaultPrevented || scrollWillGoToParent(event)) { return; } - - vscode.postMessage({ - __vscode_notebook_message: true, - type: 'did-scroll-wheel', + postNotebookMessage('did-scroll-wheel', { payload: { deltaMode: event.deltaMode, deltaX: event.deltaX, @@ -228,9 +219,7 @@ function webviewPreloads() { const element = document.createElement('div'); element.tabIndex = 0; element.addEventListener('focus', () => { - vscode.postMessage({ - __vscode_notebook_message: true, - type: 'focus-editor', + postNotebookMessage('focus-editor', { id: outputId, focusNext }); @@ -246,19 +235,13 @@ function webviewPreloads() { function addMouseoverListeners(element: HTMLElement, outputId: string): void { element.addEventListener('mouseenter', () => { - vscode.postMessage({ - __vscode_notebook_message: true, - type: 'mouseenter', + postNotebookMessage('mouseenter', { id: outputId, - data: {} }); }); element.addEventListener('mouseleave', () => { - vscode.postMessage({ - __vscode_notebook_message: true, - type: 'mouseleave', + postNotebookMessage('mouseleave', { id: outputId, - data: {} }); }); } @@ -345,9 +328,7 @@ function webviewPreloads() { return { postMessage(message: unknown) { - vscode.postMessage({ - __vscode_notebook_message: true, - type: 'customRendererMessage', + postNotebookMessage('customRendererMessage', { rendererId: namespace, message, }); @@ -418,10 +399,7 @@ function webviewPreloads() { createMarkdownPreview(cell.cellId, cell.content, -10000); } - vscode.postMessage({ - __vscode_notebook_message: true, - type: 'initializedMarkdownPreview', - }); + postNotebookMessage('initializedMarkdownPreview', {}); break; case 'createMarkdownPreview': createMarkdownPreview(event.data.id, event.data.content, event.data.top); @@ -524,9 +502,7 @@ function webviewPreloads() { resizeObserve(outputNode, outputId, true); - vscode.postMessage({ - __vscode_notebook_message: true, - type: 'dimension', + postNotebookMessage('dimension', { id: outputId, init: true, data: { @@ -602,9 +578,7 @@ function webviewPreloads() { output.parentElement!.style.display = 'block'; output.style.top = top + 'px'; - vscode.postMessage({ - __vscode_notebook_message: true, - type: 'dimension', + postNotebookMessage('dimension', { id: outputId, data: { height: output.clientHeight @@ -674,15 +648,12 @@ function webviewPreloads() { e.preventDefault(); const { cellId } = JSON.parse(data); - const msg: ICellDragEndMessage = { - __vscode_notebook_message: true, - type: 'cell-drag-end', + postNotebookMessage('cell-drag-end', { cellId: cellId, ctrlKey: e.ctrlKey, altKey: e.altKey, position: { clientX: e.clientX, clientY: e.clientY }, - }; - vscode.postMessage(msg); + }); }); function createMarkdownPreview(cellId: string, content: string, top: number) { @@ -700,12 +671,21 @@ function webviewPreloads() { previewContainerNode.style.top = top + 'px'; previewContainerNode.id = `${cellId}_preview`; previewContainerNode.classList.add('preview'); + previewContainerNode.addEventListener('dblclick', () => { - vscode.postMessage({ - __vscode_notebook_message: true, - type: 'toggleMarkdownPreview', - cellId, - }); + postNotebookMessage('toggleMarkdownPreview', { cellId }); + }); + + previewContainerNode.addEventListener('click', () => { + postNotebookMessage('focusMarkdownPreview', { cellId }); + }); + + previewContainerNode.addEventListener('mouseenter', () => { + postNotebookMessage('mouseEnterMarkdownPreview', { cellId }); + }); + + previewContainerNode.addEventListener('mouseleave', () => { + postNotebookMessage('mouseLeaveMarkdownPreview', { cellId }); }); previewContainerNode.setAttribute('draggable', 'true'); @@ -718,23 +698,17 @@ function webviewPreloads() { (e.target as HTMLElement).classList.add('dragging'); - const msg: ICellDragStartMessage = { - __vscode_notebook_message: true, - type: 'cell-drag-start', + postNotebookMessage('cell-drag-start', { cellId: cellId, position: { clientX: e.clientX, clientY: e.clientY }, - }; - vscode.postMessage(msg); + }); }); previewContainerNode.addEventListener('drag', e => { - const msg: ICellDragMessage = { - __vscode_notebook_message: true, - type: 'cell-drag', + postNotebookMessage('cell-drag', { cellId: cellId, position: { clientX: e.clientX, clientY: e.clientY }, - }; - vscode.postMessage(msg); + }); }); previewContainerNode.addEventListener('dragend', e => { @@ -754,13 +728,11 @@ function webviewPreloads() { resizeObserve(previewContainerNode, `${cellId}_preview`, false); - vscode.postMessage({ - __vscode_notebook_message: true, - type: 'dimension', + postNotebookMessage('dimension', { id: `${cellId}_preview`, init: true, data: { - height: previewContainerNode.clientHeight + height: previewContainerNode.clientHeight, }, isOutput: false }); @@ -769,6 +741,17 @@ function webviewPreloads() { } } + function postNotebookMessage( + type: T['type'], + properties: Omit + ) { + vscode.postMessage({ + __vscode_notebook_message: true, + type, + ...properties + }); + } + function updateMarkdownPreview(cellId: string, content: string) { const previewNode = document.getElementById(`${cellId}_preview`); if (previewNode) { diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts index eae9ae0070c..dd382054f84 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts @@ -69,6 +69,16 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie this._hoveringOutput = v; } + private _hoveringCell = false; + public get cellIsHovered(): boolean { + return this._hoveringCell; + } + + public set cellIsHovered(v: boolean) { + this._hoveringCell = v; + this._onDidChangeState.fire({ cellIsHoveredChanged: true }); + } + constructor( readonly viewType: string, readonly model: NotebookCellTextModel, 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 38a90af56c6..ad138d44321 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -12,7 +12,6 @@ import { ITextSnapshot } from 'vs/editor/common/model'; import { IUndoRedoService, UndoRedoElementType, IUndoRedoElement, IResourceUndoRedoElement, UndoRedoGroup, IWorkspaceUndoRedoElement } from 'vs/platform/undoRedo/common/undoRedo'; import { MoveCellEdit, SpliceCellsEdit, CellMetadataEdit } from 'vs/workbench/contrib/notebook/common/model/cellEdit'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { IModeService } from 'vs/editor/common/services/modeService'; import { ISequence, LcsDiff } from 'vs/base/common/diff/diff'; import { hash } from 'vs/base/common/hash'; import { NotebookCellOutputTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellOutputTextModel'; @@ -27,7 +26,7 @@ export class NotebookTextModelSnapshot implements ITextSnapshot { if (this._index === -1) { this._index++; - return `{ "metadata": ${JSON.stringify(this._model.metadata)}, "languages": ${JSON.stringify(this._model.languages)}, "cells": [`; + return `{ "metadata": ${JSON.stringify(this._model.metadata)}, "cells": [`; } if (this._index < this._model.cells.length) { @@ -213,20 +212,6 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel private _mapping: Map = new Map(); private _cellListeners: Map = new Map(); private _cells: NotebookCellTextModel[] = []; - private _languages: string[] = []; - private _allLanguages: boolean = false; - - get languages() { - return this._languages; - } - - get resolvedLanguages() { - if (this._allLanguages) { - return this._modeService.getRegisteredModes(); - } - - return this._languages; - } metadata: NotebookDocumentMetadata = notebookDocumentMetadataDefaults; transientOptions: TransientOptions = { transientMetadata: {}, transientOutputs: false }; @@ -244,20 +229,17 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel constructor( readonly viewType: string, - readonly supportBackup: boolean, + readonly supportBackup: boolean, //TODO@jrieken,@rebornix all support backup, right? readonly uri: URI, cells: ICellDto2[], - languages: string[], metadata: NotebookDocumentMetadata, options: TransientOptions, @IUndoRedoService private _undoService: IUndoRedoService, @ITextModelService private _modelService: ITextModelService, - @IModeService private readonly _modeService: IModeService, ) { super(); this.transientOptions = options; this.metadata = metadata; - this.updateLanguages(metadata.languages && metadata.languages.length ? metadata.languages : languages); this._initialize(cells); this._eventEmitter = new DelayedEmitter( @@ -451,17 +433,6 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._versionId = this._versionId + 1; } - updateLanguages(languages: string[]) { - const allLanguages = languages.find(lan => lan === '*'); - this._allLanguages = allLanguages !== undefined; - this._languages = languages; - - const resolvedLanguages = this.resolvedLanguages; - if (resolvedLanguages.length && this._cells.length) { - this._cells[0].language = resolvedLanguages[0]; - } - } - private _isDocumentMetadataChangeTransient(a: NotebookDocumentMetadata, b: NotebookDocumentMetadata) { const keys = new Set([...Object.keys(a || {}), ...Object.keys(b || {})]); for (let key of keys) { @@ -477,10 +448,6 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel const oldMetadata = this.metadata; this.metadata = metadata; - if (this.metadata.languages && this.metadata.languages.length) { - this.updateLanguages(this.metadata.languages); - } - if (computeUndoRedo) { const that = this; this._operationManager.pushEditOperation(new class implements IResourceUndoRedoElement { @@ -694,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[]) { @@ -710,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 b3800141c57..d9592bd84b8 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -67,7 +67,6 @@ export const notebookDocumentMetadataDefaults: Required; } export interface ICellOutput { @@ -197,8 +190,6 @@ export interface INotebookTextModel { readonly uri: URI; readonly versionId: number; - /** @deprecated */ - languages: string[]; readonly cells: readonly ICell[]; onWillDispose(listener: () => void): IDisposable; } @@ -241,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 { @@ -274,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; @@ -295,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; @@ -382,7 +382,6 @@ export type ICellEditOperation = ICellReplaceEdit | ICellOutputEdit | ICellMetad export interface NotebookDataDto { readonly cells: ICellDto2[]; - readonly languages: string[]; readonly metadata: NotebookDocumentMetadata; } @@ -597,22 +596,35 @@ export const NOTEBOOK_EDITOR_CURSOR_BOUNDARY = new RawContextKey<'none' | 'top' export const NOTEBOOK_EDITOR_CURSOR_BEGIN_END = new RawContextKey('notebookEditorCursorAtEditorBeginEnd', false); +export interface INotebookLoadOptions { + /** + * Go to disk bypassing any cache of the model if any. + */ + forceReadFromDisk?: boolean; +} + +export interface IResolvedNotebookEditorModel extends INotebookEditorModel { + notebook: NotebookTextModel; +} + export interface INotebookEditorModel extends IEditorModel { readonly onDidChangeDirty: Event; readonly resource: URI; readonly viewType: string; - readonly notebook: NotebookTextModel; + readonly notebook: NotebookTextModel | undefined; readonly lastResolvedFileStat: IFileStatWithMetadata | undefined; + isResolved(): this is IResolvedNotebookEditorModel; isDirty(): boolean; isUntitled(): boolean; + load(options?: INotebookLoadOptions): Promise; save(): Promise; saveAs(target: URI): Promise; revert(options?: IRevertOptions | undefined): Promise; } export interface INotebookDiffEditorModel extends IEditorModel { - original: INotebookEditorModel; - modified: INotebookEditorModel; + original: IResolvedNotebookEditorModel; + modified: IResolvedNotebookEditorModel; resolveOriginalFromDisk(): Promise; resolveModifiedFromDisk(): Promise; } @@ -720,7 +732,7 @@ export function notebookDocumentFilterMatch(filter: INotebookDocumentFilter, vie return false; } -export interface INotebookKernelInfoDto2 { +export interface INotebookKernel { id?: string; friendlyId: string; label: string; @@ -730,11 +742,9 @@ export interface INotebookKernelInfoDto2 { description?: string; detail?: string; isPreferred?: boolean; - preloads?: UriComponents[]; + preloads?: URI[]; supportedLanguages?: string[] -} -export interface INotebookKernelInfo2 extends INotebookKernelInfoDto2 { resolve(uri: URI, editorId: string, token: CancellationToken): Promise; executeNotebookCell(uri: URI, handle: number | undefined): Promise; cancelNotebookCell(uri: URI, handle: number | undefined): Promise; @@ -745,10 +755,7 @@ export interface INotebookKernelProvider { providerDescription?: string; selector: INotebookDocumentFilter; onDidChangeKernels: Event; - provideKernels(uri: URI, token: CancellationToken): Promise; - resolveKernel(editorId: string, uri: UriComponents, kernelId: string, token: CancellationToken): Promise; - executeNotebook(uri: URI, kernelId: string, handle: number | undefined): Promise; - cancelNotebook(uri: URI, kernelId: string, handle: number | undefined): Promise; + provideKernels(uri: URI, token: CancellationToken): Promise; } export class CellSequence implements ISequence { diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts index 1ad9b60a8b6..ab43d00fc11 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { EditorModel, IRevertOptions } from 'vs/workbench/common/editor'; import { Emitter, Event } from 'vs/base/common/event'; -import { INotebookEditorModel, NotebookCellsChangeType, NotebookDocumentBackupData } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellEditType, CellKind, ICellEditOperation, INotebookEditorModel, INotebookLoadOptions, IResolvedNotebookEditorModel, NotebookCellsChangeType, NotebookDocumentBackupData } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { URI } from 'vs/base/common/uri'; @@ -14,20 +14,12 @@ import { IWorkingCopyService, IWorkingCopy, IWorkingCopyBackup, WorkingCopyCapab import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { Schemas } from 'vs/base/common/network'; -import { IFileStatWithMetadata, IFileService } from 'vs/platform/files/common/files'; +import { IFileStatWithMetadata, IFileService, FileChangeType } from 'vs/platform/files/common/files'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { ILabelService } from 'vs/platform/label/common/label'; import { ILogService } from 'vs/platform/log/common/log'; import { TaskSequentializer } from 'vs/base/common/async'; - - -export interface INotebookLoadOptions { - /** - * Go to disk bypassing any cache of the model if any. - */ - forceReadFromDisk?: boolean; -} - +import { assertType } from 'vs/base/common/types'; export class NotebookEditorModel extends EditorModel implements INotebookEditorModel { @@ -37,14 +29,13 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM readonly onDidChangeDirty = this._onDidChangeDirty.event; readonly onDidChangeContent = this._onDidChangeContent.event; - private _notebook!: NotebookTextModel; private _lastResolvedFileStat?: IFileStatWithMetadata; private readonly _name: string; private readonly _workingCopyResource: URI; - private readonly saveSequentializer = new TaskSequentializer(); + private readonly _saveSequentializer = new TaskSequentializer(); - private _dirty = false; + private _dirty: boolean = false; constructor( readonly resource: URI, @@ -76,14 +67,41 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM }; this._register(this._workingCopyService.registerWorkingCopy(workingCopyAdapter)); + this._register(this._fileService.onDidFilesChange(async e => { + if (this.isDirty() || !this.isResolved()) { + // skip when dirty or unresolved... + return; + } + if (!e.affects(this.resource, FileChangeType.UPDATED)) { + // no my file + return; + } + const stats = await this._resolveStats(this.resource); + if (stats && this._lastResolvedFileStat && stats.etag !== this._lastResolvedFileStat.etag) { + this.load({ forceReadFromDisk: true }); + } + })); } - get lastResolvedFileStat() { + isResolved(): this is IResolvedNotebookEditorModel { + return this.notebook !== undefined; + } + + isDirty(): boolean { + return this._dirty; + } + + isUntitled(): boolean { + return this.resource.scheme === Schemas.untitled; + } + + get lastResolvedFileStat(): IFileStatWithMetadata | undefined { return this._lastResolvedFileStat; } - get notebook() { - return this._notebook; + get notebook(): NotebookTextModel | undefined { + const candidate = this._notebookService.getNotebookTextModel(this.resource); + return candidate && candidate.viewType === this.viewType ? candidate : undefined; } setDirty(newState: boolean) { @@ -94,7 +112,12 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM } async backup(token: CancellationToken): Promise> { - if (this._notebook.supportBackup) { + + if (!this.isResolved()) { + throw new Error('CANNOT call backup before notebook is resolved'); + } + + if (this.notebook.supportBackup) { const tokenSource = new CancellationTokenSource(token); const backupId = await this._notebookService.backup(this.viewType, this.resource, tokenSource.token); if (token.isCancellationRequested) { @@ -106,7 +129,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM meta: { mtime: stats?.mtime || new Date().getTime(), name: this._name, - viewType: this._notebook.viewType, + viewType: this.notebook.viewType, backupId: backupId } }; @@ -115,9 +138,9 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM meta: { mtime: new Date().getTime(), name: this._name, - viewType: this._notebook.viewType + viewType: this.notebook.viewType }, - content: this._notebook.createSnapshot(true) + content: this.notebook.createSnapshot(true) }; } } @@ -125,6 +148,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; } @@ -136,9 +160,11 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM this._onDidChangeDirty.fire(); } - async load(options?: INotebookLoadOptions): Promise { + async load(options?: INotebookLoadOptions): Promise { if (options?.forceReadFromDisk) { - return this._loadFromProvider(true, undefined); + this._loadFromProvider(undefined); + assertType(this.isResolved()); + return this; } if (this.isResolved()) { @@ -151,63 +177,82 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM return this; // Make sure meanwhile someone else did not succeed in loading } - return this._loadFromProvider(false, backup?.meta?.backupId); - } - - private async _loadFromProvider(forceReloadFromDisk: boolean, backupId: string | undefined) { - this._notebook = await this._notebookService.resolveNotebook(this.viewType!, this.resource, forceReloadFromDisk, backupId); - - const newStats = await this._resolveStats(this.resource); - this._lastResolvedFileStat = newStats; - - this._register(this._notebook); - - this._register(this._notebook.onDidChangeContent(e => { - let triggerDirty = false; - for (let i = 0; i < e.rawEvents.length; i++) { - if (e.rawEvents[i].kind !== NotebookCellsChangeType.Initialize) { - this._onDidChangeContent.fire(); - triggerDirty = triggerDirty || !e.rawEvents[i].transient; - } - } - - if (triggerDirty) { - this.setDirty(true); - } - })); - - if (forceReloadFromDisk) { - this.setDirty(false); - } - - if (backupId) { - await this._backupFileService.discardBackup(this._workingCopyResource); - this.setDirty(true); - } - + await this._loadFromProvider(backup?.meta?.backupId); + assertType(this.isResolved()); return this; } - isResolved(): boolean { - return !!this._notebook; + private async _loadFromProvider(backupId: string | undefined): Promise { + + const data = await this._notebookService.fetchNotebookRawData(this.viewType, this.resource, backupId); + this._lastResolvedFileStat = await this._resolveStats(this.resource); + + if (this.isDisposed()) { + return; + } + + if (!this.notebook) { + // FRESH there is no notebook yet and we are now creating it + + // UGLY + // There might be another notebook for the URI which was created from a different + // source (different viewType). In that case we simply dispose the + // existing/conflicting model and proceed with a new notebook + const conflictingNotebook = this._notebookService.getNotebookTextModel(this.resource); + if (conflictingNotebook) { + this._logService.warn('DISPOSING conflicting notebook with same URI but different view type', this.resource.toString(), this.viewType); + conflictingNotebook.dispose(); + } + + // todo@jrieken@rebornix what about reload? + if (this.resource.scheme === Schemas.untitled && data.data.cells.length === 0) { + data.data.cells.push({ + cellKind: CellKind.Code, + language: 'plaintext', //TODO@jrieken unsure what this is + outputs: [], + metadata: undefined, + source: '' + }); + } + + // this creates and caches a new notebook model so that notebookService.getNotebookTextModel(...) + // will return this one model + const notebook = this._notebookService.createNotebookTextModel(this.viewType, this.resource, data.data, data.transientOptions); + this._register(notebook); + this._register(notebook.onDidChangeContent(e => { + let triggerDirty = false; + for (let i = 0; i < e.rawEvents.length; i++) { + if (e.rawEvents[i].kind !== NotebookCellsChangeType.Initialize) { + this._onDidChangeContent.fire(); + triggerDirty = triggerDirty || !e.rawEvents[i].transient; + } + } + if (triggerDirty) { + this.setDirty(true); + } + })); + + } else { + // UPDATE exitsing notebook with data that we have just fetched + this.notebook.metadata = data.data.metadata; + this.notebook.transientOptions = data.transientOptions; + const edits: ICellEditOperation[] = [{ editType: CellEditType.Replace, index: 0, count: this.notebook.cells.length, cells: data.data.cells }]; + this.notebook.applyEdits(this.notebook.versionId, edits, true, undefined, () => undefined, undefined); + } + + if (backupId) { + this._backupFileService.discardBackup(this._workingCopyResource); + this.setDirty(true); + } else { + this.setDirty(false); + } } - isDirty() { - return this._dirty; - } - - isUntitled() { - return this.resource.scheme === Schemas.untitled; - } - - private async _assertStat() { + private async _assertStat(): Promise<'overwrite' | 'revert' | 'none'> { this._logService.debug('[notebook editor model] start assert stat'); const stats = await this._resolveStats(this.resource); if (this._lastResolvedFileStat && stats && stats.mtime > this._lastResolvedFileStat.mtime) { - this._logService.debug(`[notebook editor model] noteboook file on disk is newer: -LastResolvedStat: ${this._lastResolvedFileStat ? JSON.stringify(this._lastResolvedFileStat) : undefined}. -Current stat: ${JSON.stringify(stats)} -`); + this._logService.debug(`[notebook editor model] noteboook file on disk is newer:\nLastResolvedStat: ${this._lastResolvedFileStat ? JSON.stringify(this._lastResolvedFileStat) : undefined}.\nCurrent stat: ${JSON.stringify(stats)}`); this._lastResolvedFileStat = stats; return new Promise<'overwrite' | 'revert' | 'none'>(resolve => { const handle = this._notificationService.prompt( @@ -240,40 +285,44 @@ Current stat: ${JSON.stringify(stats)} } async save(): Promise { - let versionId = this._notebook.versionId; + + if (!this.isResolved()) { + return false; + } + + const versionId = this.notebook.versionId; this._logService.debug(`[notebook editor model] save(${versionId}) - enter with versionId ${versionId}`, this.resource.toString(true)); - if (this.saveSequentializer.hasPending(versionId)) { + if (this._saveSequentializer.hasPending(versionId)) { this._logService.debug(`[notebook editor model] save(${versionId}) - exit - found a pending save for versionId ${versionId}`, this.resource.toString(true)); - return this.saveSequentializer.pending.then(() => { + return this._saveSequentializer.pending.then(() => { return true; }); } - if (this.saveSequentializer.hasPending()) { - return this.saveSequentializer.setNext(async () => { + if (this._saveSequentializer.hasPending()) { + return this._saveSequentializer.setNext(async () => { await this.save(); }).then(() => { return true; }); } - return this.saveSequentializer.setPending(versionId, (async () => { + return this._saveSequentializer.setPending(versionId, (async () => { const result = await this._assertStat(); if (result === 'none') { return; } - if (result === 'revert') { await this.revert(); return; } - - const tokenSource = new CancellationTokenSource(); - await this._notebookService.save(this.notebook.viewType, this.notebook.uri, tokenSource.token); + if (!this.isResolved()) { + return; + } + await this._notebookService.save(this.notebook.viewType, this.notebook.uri, CancellationToken.None); this._logService.debug(`[notebook editor model] save(${versionId}) - document saved saved, start updating file stats`, this.resource.toString(true)); - const newStats = await this._resolveStats(this.resource); - this._lastResolvedFileStat = newStats; + this._lastResolvedFileStat = await this._resolveStats(this.resource); this.setDirty(false); })()).then(() => { return true; @@ -281,6 +330,11 @@ Current stat: ${JSON.stringify(stats)} } async saveAs(targetResource: URI): Promise { + + if (!this.isResolved()) { + return false; + } + this._logService.debug(`[notebook editor model] saveAs - enter`, this.resource.toString(true)); const result = await this._assertStat(); @@ -293,11 +347,9 @@ Current stat: ${JSON.stringify(stats)} return true; } - const tokenSource = new CancellationTokenSource(); - await this._notebookService.saveAs(this.notebook.viewType, this.notebook.uri, targetResource, tokenSource.token); + await this._notebookService.saveAs(this.notebook.viewType, this.notebook.uri, targetResource, CancellationToken.None); this._logService.debug(`[notebook editor model] saveAs - document saved, start updating file stats`, this.resource.toString(true)); - const newStats = await this._resolveStats(this.resource); - this._lastResolvedFileStat = newStats; + this._lastResolvedFileStat = await this._resolveStats(this.resource); this.setDirty(false); return true; } diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts index ace31e6a1b4..85a19e97356 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts @@ -5,7 +5,7 @@ import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { URI } from 'vs/base/common/uri'; -import { CellUri, INotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellUri, IResolvedNotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookEditorModel'; import { combinedDisposable, DisposableStore, IDisposable, IReference, ReferenceCollection } from 'vs/base/common/lifecycle'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; @@ -16,11 +16,11 @@ export const INotebookEditorModelResolverService = createDecorator>; + resolve(resource: URI, viewType?: string): Promise>; } -export class NotebookModelReferenceCollection extends ReferenceCollection> { +export class NotebookModelReferenceCollection extends ReferenceCollection> { constructor( @IInstantiationService readonly _instantiationService: IInstantiationService, @@ -30,14 +30,14 @@ export class NotebookModelReferenceCollection extends ReferenceCollection { + protected createReferencedObject(key: string, viewType: string): Promise { const uri = URI.parse(key); const model = this._instantiationService.createInstance(NotebookEditorModel, uri, viewType); const promise = model.load(); return promise; } - protected destroyReferencedObject(_key: string, object: Promise): void { + protected destroyReferencedObject(_key: string, object: Promise): void { object.then(model => { this._notebookService.destoryNotebookDocument(model.viewType, model.notebook); model.dispose(); @@ -60,7 +60,7 @@ export class NotebookModelResolverService implements INotebookEditorModelResolve this._data = instantiationService.createInstance(NotebookModelReferenceCollection); } - async resolve(resource: URI, viewType?: string): Promise> { + async resolve(resource: URI, viewType?: string): Promise> { if (resource.scheme === CellUri.scheme) { throw new Error(`CANNOT open a cell-uri as notebook. Tried with ${resource.toString()}`); } @@ -96,7 +96,7 @@ export class NotebookModelResolverService implements INotebookEditorModelResolve }; } - private static _autoReferenceDirtyModel(model: INotebookEditorModel, ref: () => IDisposable): IDisposable { + private static _autoReferenceDirtyModel(model: IResolvedNotebookEditorModel, ref: () => IDisposable): IDisposable { const references = new DisposableStore(); const listener = model.onDidChangeDirty(() => { diff --git a/src/vs/workbench/contrib/notebook/common/notebookService.ts b/src/vs/workbench/contrib/notebook/common/notebookService.ts index a347ac720b9..f25bea8de6f 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookService.ts @@ -8,10 +8,7 @@ import { URI } from 'vs/base/common/uri'; import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.protocol'; import { Event } from 'vs/base/common/event'; -import { - INotebookTextModel, INotebookRendererInfo, - IEditor, INotebookKernelProvider, INotebookKernelInfo2, TransientMetadata, NotebookDataDto, TransientOptions, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter, IOrderedMimeType, IOutputDto, INotebookMarkdownRendererInfo -} from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookTextModel, INotebookRendererInfo, IEditor, INotebookKernelProvider, INotebookKernel, TransientMetadata, NotebookDataDto, TransientOptions, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter, IOrderedMimeType, IOutputDto, INotebookMarkdownRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { CancellationToken } from 'vs/base/common/cancellation'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; @@ -26,8 +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; }>; - reloadNotebook(mainthreadTextModel: NotebookTextModel): Promise; + openNotebook(viewType: string, uri: URI, backupId?: string): Promise<{ data: NotebookDataDto, transientOptions: TransientOptions; }>; resolveNotebookEditor(viewType: string, uri: URI, editorId: string): Promise; onDidReceiveMessage(editorId: string, rendererType: string | undefined, message: any): void; save(uri: URI, token: CancellationToken): Promise; @@ -35,6 +31,12 @@ export interface IMainNotebookController { backup(uri: URI, token: CancellationToken): Promise; } + +export interface INotebookRawData { + data: NotebookDataDto; + transientOptions: TransientOptions; +} + export interface INotebookService { readonly _serviceBrand: undefined; canResolve(viewType: string): Promise; @@ -52,13 +54,13 @@ export interface INotebookService { getMimeTypeInfo(textModel: NotebookTextModel, output: IOutputDto): readonly IOrderedMimeType[]; registerNotebookKernelProvider(provider: INotebookKernelProvider): IDisposable; - getContributedNotebookKernels(viewType: string, resource: URI, token: CancellationToken): Promise; + getNotebookKernels(viewType: string, resource: URI, token: CancellationToken): Promise; getContributedNotebookKernelProviders(): Promise; getContributedNotebookOutputRenderers(id: string): NotebookOutputRendererInfo | undefined; getRendererInfo(id: string): INotebookRendererInfo | undefined; getMarkdownRendererInfo(): INotebookMarkdownRendererInfo[]; - resolveNotebook(viewType: string, uri: URI, forceReload: boolean, backupId?: string): Promise; + createNotebookTextModel(viewType: string, uri: URI, data: NotebookDataDto, transientOptions: TransientOptions): NotebookTextModel; getNotebookTextModel(uri: URI): NotebookTextModel | undefined; getNotebookTextModels(): Iterable; getContributedNotebookProviders(resource?: URI): readonly NotebookProviderInfo[]; @@ -67,9 +69,12 @@ export interface INotebookService { destoryNotebookDocument(viewType: string, notebook: INotebookTextModel): void; updateActiveNotebookEditor(editor: IEditor | null): void; updateVisibleNotebookEditor(editors: string[]): void; + + fetchNotebookRawData(viewType: string, uri: URI, backupId?: string): Promise; save(viewType: string, resource: URI, token: CancellationToken): Promise; saveAs(viewType: string, resource: URI, target: URI, token: CancellationToken): Promise; backup(viewType: string, uri: URI, token: CancellationToken): Promise; + onDidReceiveMessage(viewType: string, editorId: string, rendererType: string | undefined, message: unknown): void; setToCopy(items: NotebookCellTextModel[], isCopy: boolean): void; getToCopy(): { items: NotebookCellTextModel[], isCopy: boolean; } | undefined; diff --git a/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts b/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts index 1e7ca334718..f81832d035a 100644 --- a/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts +++ b/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts @@ -88,7 +88,6 @@ class MirrorNotebookDocument { constructor( readonly uri: URI, public cells: MirrorCell[], - public languages: string[], public metadata: NotebookDocumentMetadata, ) { } @@ -175,7 +174,7 @@ export class NotebookEditorSimpleWorker implements IRequestHandler, IDisposable dto.cellKind, dto.outputs, dto.metadata - )), data.languages, data.metadata); + )), data.metadata); } public acceptModelChanged(strURL: string, event: NotebookCellsChangedEventDto) { @@ -266,4 +265,3 @@ export class NotebookEditorSimpleWorker implements IRequestHandler, IDisposable export function create(host: EditorWorkerHost): IRequestHandler { return new NotebookEditorSimpleWorker(); } - diff --git a/src/vs/workbench/contrib/notebook/common/services/notebookWorkerServiceImpl.ts b/src/vs/workbench/contrib/notebook/common/services/notebookWorkerServiceImpl.ts index 5d1f47606ec..395bf87c2ec 100644 --- a/src/vs/workbench/contrib/notebook/common/services/notebookWorkerServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/common/services/notebookWorkerServiceImpl.ts @@ -107,7 +107,6 @@ export class NotebookEditorModelManager extends Disposable { outputs: cell.outputs.map(op => ({ outputId: op.outputId, outputs: op.outputs })), metadata: cell.metadata })), - languages: model.languages, metadata: model.metadata } ); diff --git a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts index 16b97952617..65f836cef5d 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts @@ -15,17 +15,15 @@ import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/v import { TrackedRangeStickiness } from 'vs/editor/common/model'; import { reduceCellRanges } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { IModeService } from 'vs/editor/common/services/modeService'; suite('NotebookViewModel', () => { const instantiationService = setupInstantiationService(); const textModelService = instantiationService.get(ITextModelService); const blukEditService = instantiationService.get(IBulkEditService); const undoRedoService = instantiationService.get(IUndoRedoService); - const modeService = instantiationService.get(IModeService); test('ctor', function () { - const notebook = new NotebookTextModel('notebook', false, URI.parse('test'), [], [], notebookDocumentMetadataDefaults, { transientMetadata: {}, transientOutputs: false }, undoRedoService, textModelService, modeService); + const notebook = new NotebookTextModel('notebook', false, URI.parse('test'), [], notebookDocumentMetadataDefaults, { transientMetadata: {}, transientOutputs: false }, undoRedoService, textModelService); const model = new NotebookEditorTestModel(notebook); const eventDispatcher = new NotebookEventDispatcher(); const viewModel = new NotebookViewModel('notebook', model.notebook, eventDispatcher, null, instantiationService, blukEditService, undoRedoService); @@ -156,7 +154,7 @@ suite('NotebookViewModel', () => { ['var e = 5;', 'javascript', CellKind.Code, [], { editable: false, runnable: false }], ], (editor, viewModel) => { - viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: true, cellEditable: true, cellHasExecutionOrder: true, trusted: true, languages: [] }; + viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: true, cellEditable: true, cellHasExecutionOrder: true, trusted: true }; const defaults = { hasExecutionOrder: true }; @@ -190,7 +188,7 @@ suite('NotebookViewModel', () => { ...defaults }); - viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: false, cellEditable: true, cellHasExecutionOrder: true, trusted: true, languages: [] }; + viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: false, cellEditable: true, cellHasExecutionOrder: true, trusted: true }; assert.deepEqual(viewModel.viewCells[0].getEvaluatedMetadata(viewModel.metadata), { editable: true, @@ -222,7 +220,7 @@ suite('NotebookViewModel', () => { ...defaults }); - viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: false, cellEditable: false, cellHasExecutionOrder: true, trusted: true, languages: [] }; + viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: false, cellEditable: false, cellHasExecutionOrder: true, trusted: true }; assert.deepEqual(viewModel.viewCells[0].getEvaluatedMetadata(viewModel.metadata), { editable: false, diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index 6f098280a87..5614be8731e 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -18,7 +18,7 @@ import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/v import { CellViewModel, IModelDecorationsChangeAccessor, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CellKind, CellUri, INotebookEditorModel, NotebookCellMetadata, ICellRange, INotebookKernelInfo2, notebookDocumentMetadataDefaults, IOutputDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, CellUri, INotebookEditorModel, NotebookCellMetadata, ICellRange, INotebookKernel, notebookDocumentMetadataDefaults, IOutputDto, IResolvedNotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { ICompositeCodeEditor, IEditor } from 'vs/editor/common/editorCommon'; import { NotImplementedError } from 'vs/base/common/errors'; @@ -34,7 +34,6 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { ScrollEvent } from 'vs/base/common/scrollable'; -import { IModeService } from 'vs/editor/common/services/modeService'; import { IFileStatWithMetadata } from 'vs/platform/files/common/files'; export class TestCell extends NotebookCellTextModel { @@ -84,9 +83,13 @@ export class TestNotebookEditor implements INotebookEditor { getCellByInfo(cellInfo: ICommonCellInfo): ICellViewModel { throw new Error('Method not implemented.'); } + getCellById(cellId: string): ICellViewModel { + throw new Error('Method not implemented.'); + } updateOutputHeight(cellInfo: ICommonCellInfo, output: ICellOutputViewModel, height: number, isInit: boolean): void { throw new Error('Method not implemented.'); } + setMarkdownCellEditState(cellId: string, editState: CellEditState): void { throw new Error('Method not implemented.'); } @@ -99,7 +102,7 @@ export class TestNotebookEditor implements INotebookEditor { markdownCellDragEnd(cellId: string, position: { clientY: number }): void { throw new Error('Method not implemented.'); } - async beginComputeContributedKernels(): Promise { + async beginComputeContributedKernels(): Promise { return []; } setEditorDecorations(key: string, range: ICellRange): void { @@ -154,7 +157,7 @@ export class TestNotebookEditor implements INotebookEditor { } cursorNavigationMode = false; - activeKernel: INotebookKernelInfo2 | undefined; + activeKernel: INotebookKernel | undefined; onDidChangeKernel: Event = new Emitter().event; onDidChangeActiveEditor: Event = new Emitter().event; activeCodeEditor: IEditor | undefined; @@ -413,6 +416,10 @@ export class NotebookEditorTestModel extends EditorModel implements INotebookEdi return this._notebook; } + async load(): Promise { + return this; + } + async save(): Promise { if (this._notebook) { this._dirty = false; @@ -447,7 +454,6 @@ export function setupInstantiationService() { export function withTestNotebook(instantiationService: TestInstantiationService, blukEditService: IBulkEditService, undoRedoService: IUndoRedoService, cells: [string, string, CellKind, IOutputDto[], NotebookCellMetadata][], callback: (editor: TestNotebookEditor, viewModel: NotebookViewModel, textModel: NotebookTextModel) => void) { const textModelService = instantiationService.get(ITextModelService); - const modeService = instantiationService.get(IModeService); const viewType = 'notebook'; const editor = new TestNotebookEditor(); @@ -459,7 +465,7 @@ export function withTestNotebook(instantiationService: TestInstantiationService, outputs: cell[3], metadata: cell[4] }; - }), [], notebookDocumentMetadataDefaults, { transientMetadata: {}, transientOutputs: false }, undoRedoService, textModelService, modeService); + }), notebookDocumentMetadataDefaults, { transientMetadata: {}, transientOutputs: false }, undoRedoService, textModelService); const model = new NotebookEditorTestModel(notebook); const eventDispatcher = new NotebookEventDispatcher(); const viewModel = new NotebookViewModel(viewType, model.notebook, eventDispatcher, null, instantiationService, blukEditService, undoRedoService); 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/output/browser/outputView.ts b/src/vs/workbench/contrib/output/browser/outputView.ts index ddd71be77d1..d7c7c6ddb69 100644 --- a/src/vs/workbench/contrib/output/browser/outputView.ts +++ b/src/vs/workbench/contrib/output/browser/outputView.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IAction, IActionViewItem } from 'vs/base/common/actions'; +import { IAction } from 'vs/base/common/actions'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -36,6 +36,7 @@ import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { editorBackground, selectBorder } from 'vs/platform/theme/common/colorRegistry'; import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { Dimension } from 'vs/base/browser/dom'; +import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; export class OutputViewPane extends ViewPane { 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/settingsLayout.ts b/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts index b6792cc7092..1e0aa909416 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts @@ -137,6 +137,11 @@ export const tocData: ITOCEntry = { label: localize('debug', "Debug"), settings: ['debug.*', 'launch'] }, + { + id: 'features/testing', + label: localize('testing', "Testing"), + settings: ['testing.*'] + }, { id: 'features/scm', label: localize('scm', "SCM"), 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/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index bb18656a314..bbb6695f3c2 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -36,7 +36,7 @@ import { ReloadWindowAction } from 'vs/workbench/browser/actions/windowActions'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { SwitchRemoteViewItem, SwitchRemoteAction } from 'vs/workbench/contrib/remote/browser/explorerViewItems'; -import { Action, IActionViewItem } from 'vs/base/common/actions'; +import { Action } from 'vs/base/common/actions'; import { isStringArray } from 'vs/base/common/types'; import { IRemoteExplorerService } from 'vs/workbench/services/remote/common/remoteExplorerService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -53,6 +53,7 @@ import * as icons from 'vs/workbench/contrib/remote/browser/remoteIcons'; import { ILogService } from 'vs/platform/log/common/log'; import { ITimerService } from 'vs/workbench/services/timer/browser/timerService'; import { getRemoteName } from 'vs/platform/remote/common/remoteHosts'; +import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; export interface HelpInformation { 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 ed625f3c294..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 { @@ -271,10 +270,11 @@ class TunnelTreeRenderer extends Disposable implements ITreeRenderer { this.titleActions = []; this.titleActionsDisposable.value = createAndFillInActionBarActions(titleMenu, undefined, this.titleActions); @@ -831,7 +828,7 @@ namespace LabelTunnelAction { remoteExplorerService.setEditable(context, { onFinish: async (value, success) => { if (success) { - remoteExplorerService.tunnelModel.name(context.remoteHost, context.remotePort, value); + await remoteExplorerService.tunnelModel.name(context.remoteHost, context.remotePort, value); } remoteExplorerService.setEditable(context, null); resolve(success ? { port: context.remotePort, label: value } : undefined); 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/scm/browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts index ffd41e575a8..a728a5af0bc 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts @@ -169,7 +169,6 @@ class DirtyDiffWidget extends PeekViewWidget { private index: number = 0; private change: IChange | undefined; private height: number | undefined = undefined; - private contextKeyService: IContextKeyService; constructor( editor: ICodeEditor, @@ -177,16 +176,17 @@ class DirtyDiffWidget extends PeekViewWidget { @IThemeService private readonly themeService: IThemeService, @IInstantiationService instantiationService: IInstantiationService, @IMenuService menuService: IMenuService, - @IContextKeyService contextKeyService: IContextKeyService + @IContextKeyService _contextKeyService: IContextKeyService ) { super(editor, { isResizeable: true, frameWidth: 1, keepEditorSelection: true }, instantiationService); this._disposables.add(themeService.onDidColorThemeChange(this._applyTheme, this)); this._applyTheme(themeService.getColorTheme()); - this.contextKeyService = contextKeyService.createScoped(); - this.contextKeyService.createKey('originalResourceScheme', this.model.original!.uri.scheme); - this.menu = menuService.createMenu(MenuId.SCMChangeContext, this.contextKeyService); + const contextKeyService = _contextKeyService.createOverlay([ + ['originalResourceScheme', this.model.original!.uri.scheme] + ]); + this.menu = menuService.createMenu(MenuId.SCMChangeContext, contextKeyService); this.create(); if (editor.hasModel()) { diff --git a/src/vs/workbench/contrib/scm/browser/menus.ts b/src/vs/workbench/contrib/scm/browser/menus.ts index 4fd31d3a776..8580f1134fc 100644 --- a/src/vs/workbench/contrib/scm/browser/menus.ts +++ b/src/vs/workbench/contrib/scm/browser/menus.ts @@ -119,15 +119,12 @@ class SCMMenusItem implements IDisposable { let item = this.contextualResourceMenus.get(resource.contextValue); if (!item) { - const contextKeyService = this.contextKeyService.createScoped(); - contextKeyService.createKey('scmResourceState', resource.contextValue); - + const contextKeyService = this.contextKeyService.createOverlay([['scmResourceState', resource.contextValue]]); const menu = this.menuService.createMenu(MenuId.SCMResourceContext, contextKeyService); item = { menu, dispose() { menu.dispose(); - contextKeyService.dispose(); } }; @@ -148,7 +145,6 @@ class SCMMenusItem implements IDisposable { } this.resourceFolderMenu?.dispose(); - this.contextKeyService.dispose(); } } @@ -178,10 +174,11 @@ export class SCMRepositoryMenus implements ISCMRepositoryMenus, IDisposable { @IInstantiationService instantiationService: IInstantiationService, @IMenuService private readonly menuService: IMenuService ) { - this.contextKeyService = contextKeyService.createScoped(); - this.contextKeyService.createKey('scmProvider', provider.contextValue); - this.contextKeyService.createKey('scmProviderRootUri', provider.rootUri?.toString()); - this.contextKeyService.createKey('scmProviderHasRootUri', !!provider.rootUri); + this.contextKeyService = contextKeyService.createOverlay([ + ['scmProvider', provider.contextValue], + ['scmProviderRootUri', provider.rootUri?.toString()], + ['scmProviderHasRootUri', !!provider.rootUri], + ]); const serviceCollection = new ServiceCollection([IContextKeyService, this.contextKeyService]); instantiationService = instantiationService.createChild(serviceCollection); @@ -207,9 +204,9 @@ export class SCMRepositoryMenus implements ISCMRepositoryMenus, IDisposable { let result = this.resourceGroupMenusItems.get(group); if (!result) { - const contextKeyService = this.contextKeyService.createScoped(); - contextKeyService.createKey('scmProvider', group.provider.contextValue); - contextKeyService.createKey('scmResourceGroup', group.id); + const contextKeyService = this.contextKeyService.createOverlay([ + ['scmResourceGroup', group.id], + ]); result = new SCMMenusItem(contextKeyService, this.menuService); this.resourceGroupMenusItems.set(group, result); diff --git a/src/vs/workbench/contrib/scm/browser/scmRepositoryRenderer.ts b/src/vs/workbench/contrib/scm/browser/scmRepositoryRenderer.ts index ffcb36be97f..739853ebdb7 100644 --- a/src/vs/workbench/contrib/scm/browser/scmRepositoryRenderer.ts +++ b/src/vs/workbench/contrib/scm/browser/scmRepositoryRenderer.ts @@ -10,7 +10,7 @@ import { ISCMRepository, ISCMViewService } from 'vs/workbench/contrib/scm/common import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IAction, IActionViewItemProvider } from 'vs/base/common/actions'; +import { IAction } from 'vs/base/common/actions'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { connectPrimaryMenu, isSCMRepository, StatusBarAction } from './util'; import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; @@ -21,6 +21,7 @@ import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IListRenderer } from 'vs/base/browser/ui/list/list'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { basename } from 'vs/base/common/resources'; +import { IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; interface RepositoryTemplate { readonly label: HTMLElement; diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 9e49aa3061c..f0bb38ed9f7 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -20,8 +20,8 @@ import { IContextKeyService, IContextKey, ContextKeyExpr, RawContextKey } from ' import { ICommandService } from 'vs/platform/commands/common/commands'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { MenuItemAction, IMenuService, registerAction2, MenuId, IAction2Options, MenuRegistry, Action2 } from 'vs/platform/actions/common/actions'; -import { IAction, ActionRunner, IActionViewItemProvider } from 'vs/base/common/actions'; -import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IAction, ActionRunner } from 'vs/base/common/actions'; +import { ActionBar, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; import { IThemeService, registerThemingParticipant, IFileIconTheme } from 'vs/platform/theme/common/themeService'; import { isSCMResource, isSCMResourceGroup, connectPrimaryMenuToInlineActionBar, isSCMRepository, isSCMInput, collectContextMenuActions, getActionViewItemProvider } from './util'; import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; diff --git a/src/vs/workbench/contrib/scm/browser/util.ts b/src/vs/workbench/contrib/scm/browser/util.ts index 7d916c437d5..007d674bac3 100644 --- a/src/vs/workbench/contrib/scm/browser/util.ts +++ b/src/vs/workbench/contrib/scm/browser/util.ts @@ -5,9 +5,9 @@ import { ISCMResource, ISCMRepository, ISCMResourceGroup, ISCMInput } from 'vs/workbench/contrib/scm/common/scm'; import { IMenu } from 'vs/platform/actions/common/actions'; -import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionBar, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; import { IDisposable, Disposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { Action, IAction, IActionViewItemProvider } from 'vs/base/common/actions'; +import { Action, IAction } from 'vs/base/common/actions'; import { createActionViewItem, createAndFillInActionBarActions, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { equals } from 'vs/base/common/arrays'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; 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/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 23f483efa8f..6bb210baa2d 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -31,6 +31,11 @@ export interface ITerminalInstanceService { /** Fired when the ptyHost process goes down, losing all connections to the service's ptys. */ onPtyHostExit: Event; + /** + * Fired when the ptyHost process becomes non-responsive, this should disable stdin for all + * terminals using this pty host connection and mark them as disconnected. + */ + onPtyHostUnresponsive: Event; // These events are optional as the requests they make are only needed on the browser side onRequestDefaultShellAndArgs?: Event; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index c2a4f1453b8..1ff07439199 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -950,6 +950,11 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._processManager.onProcessOverrideDimensions(e => this.setDimensions(e, true)); this._processManager.onProcessResolvedShellLaunchConfig(e => this._setResolvedShellLaunchConfig(e)); this._processManager.onEnvironmentVariableInfoChanged(e => this._onEnvironmentVariableInfoChanged(e)); + this._processManager.onPtyDisconnect(() => { + this._safeSetOption('disableStdin', true); + // Use api source so it cannot be overridden + this.setTitle(nls.localize('ptyDisconnected', "{0} (disconnected)", this._title), TitleEventSource.Api); + }); if (this._shellLaunchConfig.name) { this.setTitle(this._shellLaunchConfig.name, TitleEventSource.Api); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts index 416df81e7ce..65d5c56ebfd 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts @@ -10,7 +10,7 @@ import type { SearchAddon as XTermSearchAddon } from 'xterm-addon-search'; import type { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11'; import type { WebglAddon as XTermWebglAddon } from 'xterm-addon-webgl'; import { IProcessEnvironment } from 'vs/base/common/platform'; -import { Emitter, Event } from 'vs/base/common/event'; +import { Emitter } from 'vs/base/common/event'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Disposable } from 'vs/base/common/lifecycle'; import { ITerminalsLayoutInfoById, ITerminalsLayoutInfo, ITerminalChildProcess } from 'vs/platform/terminal/common/terminal'; @@ -27,8 +27,10 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst private readonly _onPtyHostExit = this._register(new Emitter()); readonly onPtyHostExit = this._onPtyHostExit.event; + private readonly _onPtyHostUnresponsive = this._register(new Emitter()); + readonly onPtyHostUnresponsive = this._onPtyHostUnresponsive.event; private readonly _onRequestDefaultShellAndArgs = this._register(new Emitter()); - public get onRequestDefaultShellAndArgs(): Event { return this._onRequestDefaultShellAndArgs.event; } + readonly onRequestDefaultShellAndArgs = this._onRequestDefaultShellAndArgs.event; public async getXtermConstructor(): Promise { if (!Terminal) { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index ae3af7cd398..b8f92b0cf32 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -69,6 +69,8 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce private _ackDataBufferer: AckDataBufferer; private _hasWrittenData: boolean = false; + private readonly _onPtyDisconnect = this._register(new Emitter()); + public get onPtyDisconnect(): Event { return this._onPtyDisconnect.event; } private readonly _onProcessReady = this._register(new Emitter()); public get onProcessReady(): Event { return this._onProcessReady.event; } private readonly _onBeforeProcessData = this._register(new Emitter()); @@ -317,6 +319,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce const env = await this._setupEnvVariableInfo(activeWorkspaceRootUri, shellLaunchConfig); const useConpty = this._configHelper.config.windowsEnableConpty && !isScreenReaderModeEnabled; + this._terminalInstanceService.onPtyHostUnresponsive(() => this._onPtyDisconnect.fire()); return await this._terminalInstanceService.createTerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, useConpty); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 7674a991804..c24ce885c6b 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -6,7 +6,7 @@ import * as dom from 'vs/base/browser/dom'; import * as nls from 'vs/nls'; import * as platform from 'vs/base/common/platform'; -import { Action, IAction, IActionViewItem } from 'vs/base/common/actions'; +import { Action, IAction } from 'vs/base/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -35,6 +35,8 @@ 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'; +import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; const FIND_FOCUS_CLASS = 'find-focused'; @@ -244,7 +246,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 7209f6a30ff..91a506cfb48 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -16,40 +16,40 @@ import { IProcessDataEvent, IShellDefinition, IShellLaunchConfig, ITerminalDimen 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, nls.localize('terminalFocusContextKey', "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: nls.localize('terminalShellTypeContextKey', "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, nls.localize('terminalTextSelectedContextKey', "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, nls.localize('terminalProcessSupportedContextKey', "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'; @@ -250,6 +250,7 @@ export interface ITerminalProcessManager extends IDisposable { /** Whether the process has had data written to it yet. */ readonly hasWrittenData: boolean; + readonly onPtyDisconnect: Event; readonly onProcessReady: Event; readonly onBeforeProcessData: Event; readonly onProcessData: Event; diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts index cd53620088b..67de18ccf00 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts @@ -26,9 +26,10 @@ import { IDefaultShellAndArgsRequest, IShellLaunchConfig, ITerminalChildProcess, import { LocalPty } from 'vs/workbench/contrib/terminal/electron-sandbox/localPty'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { INotificationService } from 'vs/platform/notification/common/notification'; import { IGetTerminalLayoutInfoArgs, ISetTerminalLayoutInfoArgs } from 'vs/platform/terminal/common/terminalProcess'; import { ILabelService } from 'vs/platform/label/common/label'; +import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification'; +import { localize } from 'vs/nls'; let Terminal: typeof XTermTerminal; let SearchAddon: typeof XTermSearchAddon; @@ -40,6 +41,8 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst private readonly _onPtyHostExit = this._register(new Emitter()); readonly onPtyHostExit = this._onPtyHostExit.event; + private readonly _onPtyHostUnresponsive = this._register(new Emitter()); + readonly onPtyHostUnresponsive = this._onPtyHostUnresponsive.event; constructor( @IInstantiationService private readonly _instantiationService: IInstantiationService, @@ -65,6 +68,16 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst this._logService.info(`ptyHost restarted`); }); } + if (this._localPtyService.onPtyHostUnresponsive) { + this._localPtyService.onPtyHostUnresponsive(() => { + const choices: IPromptChoice[] = [{ + label: localize('restartPtyHost', "Restart pty host"), + run: () => this._localPtyService.restartPtyHost!() + }]; + notificationService.prompt(Severity.Error, localize('nonResponsivePtyHost', "The connection to the terminal's pty host process is unresponsive, the terminals may stop working."), choices); + this._onPtyHostUnresponsive.fire(); + }); + } } onRequestDefaultShellAndArgs?: Event | undefined; diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/localPty.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/localPty.ts index 48248d07f7a..07a2beb6501 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/localPty.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/localPty.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Emitter, Event } from 'vs/base/common/event'; +import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { ILocalPtyService } from 'vs/platform/terminal/electron-sandbox/terminal'; import { IProcessDataEvent, IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal'; @@ -15,20 +15,21 @@ import { IPtyHostProcessReplayEvent } from 'vs/platform/terminal/common/terminal */ export class LocalPty extends Disposable implements ITerminalChildProcess { private readonly _onProcessData = this._register(new Emitter()); - public readonly onProcessData: Event = this._onProcessData.event; + public readonly onProcessData = this._onProcessData.event; private readonly _onProcessReplay = this._register(new Emitter()); - public readonly onProcessReplay: Event = this._onProcessReplay.event; + public readonly onProcessReplay = this._onProcessReplay.event; private readonly _onProcessExit = this._register(new Emitter()); - public readonly onProcessExit: Event = this._onProcessExit.event; + public readonly onProcessExit = this._onProcessExit.event; private readonly _onProcessReady = this._register(new Emitter<{ pid: number, cwd: string }>()); - public get onProcessReady(): Event<{ pid: number, cwd: string }> { return this._onProcessReady.event; } + public readonly onProcessReady = this._onProcessReady.event; private readonly _onProcessTitleChanged = this._register(new Emitter()); - public readonly onProcessTitleChanged: Event = this._onProcessTitleChanged.event; + public readonly onProcessTitleChanged = this._onProcessTitleChanged.event; private readonly _onProcessOverrideDimensions = this._register(new Emitter()); - public get onProcessOverrideDimensions(): Event { return this._onProcessOverrideDimensions.event; } + public readonly onProcessOverrideDimensions = this._onProcessOverrideDimensions.event; private readonly _onProcessResolvedShellLaunchConfig = this._register(new Emitter()); - public get onProcessResolvedShellLaunchConfig(): Event { return this._onProcessResolvedShellLaunchConfig.event; } + public readonly onProcessResolvedShellLaunchConfig = this._onProcessResolvedShellLaunchConfig.event; private _inReplay = false; + constructor( private readonly _localPtyId: number, @ILocalPtyService private readonly _localPtyService: ILocalPtyService 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/icons.ts b/src/vs/workbench/contrib/testing/browser/icons.ts index 6ce787cabf7..b89c7382b08 100644 --- a/src/vs/workbench/contrib/testing/browser/icons.ts +++ b/src/vs/workbench/contrib/testing/browser/icons.ts @@ -16,6 +16,7 @@ export const testingRunAllIcon = registerIcon('testing-run-all-icon', Codicon.ru export const testingDebugIcon = registerIcon('testing-debug-icon', Codicon.debugAlt, localize('testingDebugIcon', 'Icon of the "debug test" action.')); export const testingCancelIcon = registerIcon('testing-cancel-icon', Codicon.close, localize('testingCancelIcon', 'Icon to cancel ongoing test runs.')); export const testingFilterIcon = registerIcon('testing-filter', Codicon.filter, localize('filterIcon', 'Icon for the \'Filter\' action in the testing view.')); +export const testingAutorunIcon = registerIcon('testing-autorun', Codicon.symbolEvent, localize('autoRunIcon', 'Icon for the \'Autorun\' toggle in the testing view.')); export const testingShowAsList = registerIcon('testing-show-as-list-icon', Codicon.listTree, localize('testingShowAsList', 'Icon shown when the test explorer is disabled as a tree.')); export const testingShowAsTree = registerIcon('testing-show-as-list-icon', Codicon.listFlat, localize('testingShowAsTree', 'Icon shown when the test explorer is disabled as a list.')); diff --git a/src/vs/workbench/contrib/testing/browser/media/testing.css b/src/vs/workbench/contrib/testing/browser/media/testing.css index 3943b0bd98d..52988718b5a 100644 --- a/src/vs/workbench/contrib/testing/browser/media/testing.css +++ b/src/vs/workbench/contrib/testing/browser/media/testing.css @@ -12,6 +12,7 @@ .test-explorer > .test-explorer-tree { flex-grow: 1; height: 0px; + position: relative; } .test-explorer .test-item { @@ -60,11 +61,41 @@ margin-right: 8px; } +.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 { /* Use steps to throttle FPS to reduce CPU usage */ animation: codicon-spin 1.25s steps(30) infinite; } +.testing-no-test-placeholder { + display: none; + padding: 0 20px; + position: absolute; + left: 0; + right: 0; + top: 0; + z-index: 1; +} + +.testing-no-test-placeholder.visible { + display: block; +} + /** -- peek */ .monaco-editor .zone-widget.test-output-peek .zone-widget-container.peekview-widget { border-top-width: 2px; diff --git a/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts b/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts index 6125b257fa7..6697a6c0d94 100644 --- a/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts +++ b/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts @@ -19,22 +19,33 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { ExtHostTestingResource } from 'vs/workbench/api/common/extHost.protocol'; import { ViewAction } from 'vs/workbench/browser/parts/views/viewPane'; import { FocusedViewContext } from 'vs/workbench/common/views'; +import { IExtensionsViewPaneContainer, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import * as icons from 'vs/workbench/contrib/testing/browser/icons'; import { TestingExplorerView, TestingExplorerViewModel } from 'vs/workbench/contrib/testing/browser/testingExplorerView'; -import { TestExplorerViewSorting, TestExplorerViewMode, Testing } from 'vs/workbench/contrib/testing/common/constants'; +import { TestExplorerViewMode, TestExplorerViewSorting, Testing } from 'vs/workbench/contrib/testing/common/constants'; 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'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; const category = localize('testing.category', 'Test'); const enum ActionOrder { + // Navigation: Run = 10, Debug, - Refresh, + AutoRun, Collapse, + + // Submenu: + DisplayMode, + Sort, + Refresh, } export class DebugAction extends Action { @@ -87,7 +98,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, @@ -96,6 +107,7 @@ abstract class RunOrDebugAction extends ViewAction { viewId: Testing.ExplorerViewId, f1: true, category, + precondition: FocusedViewContext.isEqualTo(Testing.ExplorerViewId), }); } @@ -136,7 +148,7 @@ abstract class RunOrDebugAction extends ViewAction { protected abstract filter(item: InternalTestItem): boolean; } -export class RunSelectedAction extends RunOrDebugAction { +export class RunSelectedAction extends RunOrDebugSelectedAction { constructor( ) { super( @@ -155,7 +167,7 @@ export class RunSelectedAction extends RunOrDebugAction { } } -export class DebugSelectedAction extends RunOrDebugAction { +export class DebugSelectedAction extends RunOrDebugSelectedAction { constructor() { super( 'testing.debugSelected', @@ -187,7 +199,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), ]) } }); @@ -287,7 +302,7 @@ export class TestingViewAsListAction extends ViewAction { toggled: TestingContextKeys.viewMode.isEqualTo(TestExplorerViewMode.List), menu: { id: MenuId.ViewTitle, - order: 10, + order: ActionOrder.DisplayMode, group: 'viewAs', when: ContextKeyEqualsExpr.create('view', Testing.ExplorerViewId) } @@ -312,7 +327,7 @@ export class TestingViewAsTreeAction extends ViewAction { toggled: TestingContextKeys.viewMode.isEqualTo(TestExplorerViewMode.Tree), menu: { id: MenuId.ViewTitle, - order: 10, + order: ActionOrder.DisplayMode, group: 'viewAs', when: ContextKeyEqualsExpr.create('view', Testing.ExplorerViewId) } @@ -338,7 +353,7 @@ export class TestingSortByNameAction extends ViewAction { toggled: TestingContextKeys.viewSorting.isEqualTo(TestExplorerViewSorting.ByName), menu: { id: MenuId.ViewTitle, - order: 10, + order: ActionOrder.Sort, group: 'sortBy', when: ContextKeyEqualsExpr.create('view', Testing.ExplorerViewId) } @@ -363,7 +378,7 @@ export class TestingSortByLocationAction extends ViewAction toggled: TestingContextKeys.viewSorting.isEqualTo(TestExplorerViewSorting.ByLocation), menu: { id: MenuId.ViewTitle, - order: 10, + order: ActionOrder.Sort, group: 'sortBy', when: ContextKeyEqualsExpr.create('view', Testing.ExplorerViewId) } @@ -410,11 +425,10 @@ export class RefreshTestsAction extends Action2 { title: localize('testing.refresh', "Refresh Tests"), category, f1: true, - icon: Codicon.refresh, menu: { id: MenuId.ViewTitle, order: ActionOrder.Refresh, - group: 'navigation', + group: 'refresh', when: ContextKeyEqualsExpr.create('view', Testing.ExplorerViewId) } }); @@ -471,3 +485,365 @@ export class EditFocusedTest extends ViewAction { } } } + +export class ToggleAutoRun extends Action2 { + constructor() { + super({ + id: 'testing.toggleautoRun', + title: localize('testing.toggleautoRun', "Toggle Auto Run"), + f1: true, + toggled: TestingContextKeys.autoRun.isEqualTo(true), + icon: icons.testingAutorunIcon, + menu: { + id: MenuId.ViewTitle, + order: ActionOrder.AutoRun, + group: 'navigation', + when: ContextKeyEqualsExpr.create('view', Testing.ExplorerViewId) + } + }); + } + + /** + * @override + */ + public run(accessor: ServicesAccessor) { + 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 })) }); + } +} + +export class SearchForTestExtension extends Action2 { + constructor() { + super({ + id: 'testing.searchForTestExtension', + title: localize('testing.searchForTestExtension', "Search for Test Extension"), + f1: false, + }); + } + + public async run(accessor: ServicesAccessor) { + const viewletService = accessor.get(IViewletService); + const viewlet = (await viewletService.openViewlet(EXTENSIONS_VIEWLET_ID, true))?.getViewPaneContainer() as IExtensionsViewPaneContainer; + viewlet.search('tag:testing @sort:installs'); + viewlet.focus(); + } +} diff --git a/src/vs/workbench/contrib/testing/browser/testing.contribution.ts b/src/vs/workbench/contrib/testing/browser/testing.contribution.ts index c98cf48cb5c..3919c1f1500 100644 --- a/src/vs/workbench/contrib/testing/browser/testing.contribution.ts +++ b/src/vs/workbench/contrib/testing/browser/testing.contribution.ts @@ -7,6 +7,7 @@ import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { localize } from 'vs/nls'; import { registerAction2 } from 'vs/platform/actions/common/actions'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { Extensions as ConfigurationExtensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -18,10 +19,12 @@ 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'; import { TestIdWithProvider } from 'vs/workbench/contrib/testing/common/testCollection'; +import { ITestingAutoRun, TestingAutoRun } from 'vs/workbench/contrib/testing/common/testingAutoRun'; import { TestingContentProvider } from 'vs/workbench/contrib/testing/common/testingContentProvider'; import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys'; import { ITestResultService, TestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; @@ -34,6 +37,8 @@ import * as Action from './testExplorerActions'; 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({ @@ -46,7 +51,6 @@ const viewContainer = Registry.as(ViewContainerExtensio hideIfEmpty: true, }, ViewContainerLocation.Sidebar); - const viewsRegistry = Registry.as(ViewContainerExtensions.ViewsRegistry); viewsRegistry.registerViewWelcomeContent(Testing.ExplorerViewId, { @@ -75,7 +79,7 @@ viewsRegistry.registerViews([{ order: -999, containerIcon: testingViewIcon, // temporary until release, at which point we can show the welcome view: - when: ContextKeyExpr.greater(TestingContextKeys.providerCount.serialize(), 0), + when: ContextKeyExpr.greater(TestingContextKeys.providerCount.key, 0), }], viewContainer); registerAction2(Action.TestingViewAsListAction); @@ -91,6 +95,16 @@ registerAction2(Action.RunAllAction); 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(Action.SearchForTestExtension); registerAction2(CloseTestPeek); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TestingContentProvider, LifecyclePhase.Eventually); @@ -121,3 +135,15 @@ CommandsRegistry.registerCommand({ accessor.get(IViewsService).openView(Testing.ExplorerViewId); } }); + +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..54f94ebe446 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, TestResultItem } 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 { 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 ed9186220ae..3c102a91fd5 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts @@ -9,9 +9,9 @@ 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 { Event, Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; import { localize } from 'vs/nls'; import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; @@ -20,12 +20,11 @@ import { ContextKeyEqualsExpr, ContextKeyExpr } from 'vs/platform/contextkey/com import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { inputActiveOptionBorder, inputActiveOptionForeground, inputActiveOptionBackground } from 'vs/platform/theme/common/colorRegistry'; import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; -import { IColorTheme, ICssStyleCollector, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; +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'; @@ -35,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; @@ -47,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, @@ -84,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)); } /** @@ -167,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; } } @@ -199,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, @@ -232,18 +254,3 @@ registerAction2(class extends Action2 { } async run(): Promise { } }); - -registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { - const inputActiveOptionBorderColor = theme.getColor(inputActiveOptionBorder); - if (inputActiveOptionBorderColor) { - collector.addRule(`.testing-filter-button.checked { border-color: ${inputActiveOptionBorderColor}; }`); - } - const inputActiveOptionForegroundColor = theme.getColor(inputActiveOptionForeground); - if (inputActiveOptionForegroundColor) { - collector.addRule(`.testing-filter-button.checked { color: ${inputActiveOptionForegroundColor}; }`); - } - const inputActiveOptionBackgroundColor = theme.getColor(inputActiveOptionBackground); - if (inputActiveOptionBackgroundColor) { - collector.addRule(`.testing-filter-button.checked { background-color: ${inputActiveOptionBackgroundColor}; }`); - } -}); diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts index b1a3c35be43..51c0af3a7d5 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts @@ -4,22 +4,26 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; -import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { ActionBar, IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import * as aria from 'vs/base/browser/ui/aria/aria'; +import { Button } from 'vs/base/browser/ui/button/button'; import { IIdentityProvider, IKeyboardNavigationLabelProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { DefaultKeyboardNavigationDelegate, IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; import { ObjectTree } from 'vs/base/browser/ui/tree/objectTree'; import { ITreeEvent, ITreeFilter, ITreeNode, ITreeRenderer, ITreeSorter, TreeFilterResult, TreeVisibility } from 'vs/base/browser/ui/tree/tree'; -import { Action, IAction, IActionViewItem } from 'vs/base/common/actions'; -import { DeferredPromise } from 'vs/base/common/async'; +import { Action, IAction } from 'vs/base/common/actions'; +import { DeferredPromise, RunOnceScheduler } from 'vs/base/common/async'; import { Color, RGBA } from 'vs/base/common/color'; import { throttle } from 'vs/base/common/decorators'; 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'; @@ -27,6 +31,7 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService import { localize } from 'vs/nls'; import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { MenuItemAction } from 'vs/platform/actions/common/actions'; +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 } from 'vs/platform/contextview/browser/contextView'; @@ -39,6 +44,7 @@ import { IProgress, IProgressService, IProgressStep } from 'vs/platform/progress import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { foreground } from 'vs/platform/theme/common/colorRegistry'; +import { attachButtonStyler } from 'vs/platform/theme/common/styler'; import { IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { TestRunState } from 'vs/workbench/api/common/extHostTypes'; import { IResourceLabel, IResourceLabelOptions, IResourceLabelProps, ResourceLabels } from 'vs/workbench/browser/labels'; @@ -50,11 +56,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'; @@ -146,7 +151,10 @@ export class TestingExplorerView extends ViewPane { } private createFilterActionBar() { - const bar = new ActionBar(this.container, { actionViewItemProvider: action => this.getActionViewItem(action) }); + const bar = new ActionBar(this.container, { + actionViewItemProvider: action => this.getActionViewItem(action), + triggerKeys: { keyDown: false, keys: [] }, + }); bar.push(new Action(Testing.FilterActionId)); bar.getContainer().classList.add('testing-filter-action-bar'); return bar; @@ -178,11 +186,35 @@ export class TestingExplorerView extends ViewPane { } } +class EmptyTestsWidget extends Disposable { + private readonly el: HTMLElement; + constructor( + container: HTMLElement, + @ICommandService commandService: ICommandService, + @IThemeService themeService: IThemeService, + ) { + super(); + const el = this.el = dom.append(container, dom.$('.testing-no-test-placeholder')); + const emptyParagraph = dom.append(el, dom.$('p')); + emptyParagraph.innerText = localize('testingNoTest', 'No tests have been found in this workspace yet.'); + const buttonLabel = localize('testingFindExtension', 'Find Test Extensions'); + const button = this._register(new Button(el, { title: buttonLabel })); + button.label = buttonLabel; + this._register(attachButtonStyler(button, themeService)); + this._register(button.onDidClick(() => commandService.executeCommand('testing.searchForTestExtension'))); + } + + public setVisible(isVisible: boolean) { + this.el.classList.toggle('visible', isVisible); + } +} + export class TestingExplorerViewModel extends Disposable { public tree: ObjectTree; private filter: TestsFilter; public projection!: ITestTreeProjection; + private readonly emptyTestsWidget: EmptyTestsWidget; private readonly _viewMode = TestingContextKeys.viewMode.bindTo(this.contextKeyService); private readonly _viewSorting = TestingContextKeys.viewSorting.bindTo(this.contextKeyService); @@ -224,15 +256,20 @@ export class TestingExplorerViewModel extends Disposable { listContainer: HTMLElement, onDidChangeVisibility: Event, private listener: TestSubscriptionListener | undefined, + @ICommandService commandService: ICommandService, + @IThemeService themeService: IThemeService, + @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(); + this.emptyTestsWidget = this._register(instantiationService.createInstance(EmptyTestsWidget, listContainer)); this._viewMode.set(this.storageService.get('testing.viewMode', StorageScope.WORKSPACE, TestExplorerViewMode.Tree) as TestExplorerViewMode); this._viewSorting.set(this.storageService.get('testing.viewSorting', StorageScope.WORKSPACE, TestExplorerViewSorting.ByLocation) as TestExplorerViewSorting); @@ -257,7 +294,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 +311,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 +435,36 @@ 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 shouldShowEmptyPlaceholder() { + return !!this.listener + && this.listener.subscription.busyProviders === 0 + && this.listener.subscription.isEmpty; } private updatePreferredProjection() { @@ -430,12 +480,18 @@ export class TestingExplorerViewModel extends Disposable { this.projection = this.instantiationService.createInstance(HierarchicalByLocationProjection, this.listener); } - this.projection.onUpdate(this.deferUpdate, this); - this.projection.applyTo(this.tree); + const scheduler = new RunOnceScheduler(() => this.applyProjectionChanges(), 200); + this.projection.onUpdate(() => { + if (!scheduler.isScheduled()) { + scheduler.schedule(); + } + }); + + this.applyProjectionChanges(); } - @throttle(200) - private deferUpdate() { + private applyProjectionChanges() { + this.emptyTestsWidget.setVisible(this.shouldShowEmptyPlaceholder()); this.projection.applyTo(this.tree); } @@ -549,7 +605,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 +615,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; @@ -832,7 +899,7 @@ class TestRunProgress { } private updateProgress() { - const running = this.resultService.results.filter(r => !r.isComplete); + const running = this.resultService.results.filter(r => r.completedAt === undefined); if (!running.length) { this.setIdleText(this.resultService.results[0]?.counts); this.current?.deferred.complete(); @@ -881,7 +948,7 @@ class TestRunProgress { return; } - if (!result.isComplete) { + if (result.completedAt === undefined) { const badge = new ProgressBadge(() => localize('testBadgeRunning', 'Test run in progress')); this.badge.value = this.activityService.showViewActivity(Testing.ExplorerViewId, { badge, clazz: 'progress-badge' }); return; diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index 081c8893ad9..b3715669718 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 { ITestItem, ITestMessage, ITestState, TestResultItem } 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, 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 dfd5607d854..635baca1d34 100644 --- a/src/vs/workbench/contrib/testing/browser/theme.ts +++ b/src/vs/workbench/contrib/testing/browser/theme.ts @@ -5,9 +5,10 @@ import { Color, RGBA } from 'vs/base/common/color'; import { localize } from 'vs/nls'; -import { editorErrorForeground, editorForeground, editorHintForeground, editorInfoForeground, editorWarningForeground, registerColor } from 'vs/platform/theme/common/colorRegistry'; +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', @@ -124,9 +125,28 @@ export const testStatesToIconColors: { [K in TestRunState]?: string } = { registerThemingParticipant((theme, collector) => { + //#region test states for (const [state, { marginBackground }] of Object.entries(testMessageSeverityColors)) { collector.addRule(`.monaco-editor .testing-inline-message-severity-${state} { background: ${theme.getColor(marginBackground)}; }`); } + //#endregion test states + + //#region active buttons + const inputActiveOptionBorderColor = theme.getColor(inputActiveOptionBorder); + if (inputActiveOptionBorderColor) { + collector.addRule(`.testing-filter-button.checked { border-color: ${inputActiveOptionBorderColor}; }`); + } + const inputActiveOptionForegroundColor = theme.getColor(inputActiveOptionForeground); + if (inputActiveOptionForegroundColor) { + collector.addRule(`.testing-filter-button.checked { color: ${inputActiveOptionForegroundColor}; }`); + } + const inputActiveOptionBackgroundColor = theme.getColor(inputActiveOptionBackground); + if (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 new file mode 100644 index 00000000000..546ec5ff9da --- /dev/null +++ b/src/vs/workbench/contrib/testing/common/configuration.ts @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; + +export const enum TestingConfigKeys { + AutoRunDelay = 'testing.autoRun.delay', + AutoOpenPeekView = 'testing.automaticallyOpenPeekView', + AutoOpenPeekViewDuringAutoRun = 'testing.automaticallyOpenPeekViewDuringAutoRun', +} + +export const enum AutoOpenPeekViewWhen { + FailureVisible = 'failureInVisibleDocument', + FailureAnywhere = 'failureAnywhere', +} + +export const testingConfiguation: IConfigurationNode = { + id: 'testing', + order: 21, + title: localize('testConfigurationTitle', "Testing"), + type: 'object', + properties: { + [TestingConfigKeys.AutoRunDelay]: { + type: 'integer', + minimum: 0, + 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..0a08e9d4f37 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; @@ -70,12 +79,32 @@ export interface InternalTestItem { item: ITestItem; } -export interface InternalTestItemWithChildren extends InternalTestItem { - children: this[]; +/** + * Test result item used in the main thread. + */ +export interface TestResultItem extends IncrementalTestCollectionItem { + /** Current state of this test */ + state: ITestState; + /** Computed state based on children */ + computedState: TestRunState; + /** True if the test is outdated */ + retired: boolean; + /** True if the test was directly requested by the run (is not a child or parent) */ + direct?: true; } -export interface InternalTestResults { - tests: InternalTestItemWithChildren[]; +export type SerializedTestResultItem = Omit & { children: string[], retired: undefined }; + +/** + * Test results serialized for transport and storage. + */ +export interface ISerializedTestResults { + /** ID of these test results */ + id: string; + /** Time the results were compelted */ + completedAt: number; + /** Subset of test result items */ + items: SerializedTestResultItem[]; } export const enum TestDiffOpType { diff --git a/src/vs/workbench/contrib/testing/common/testResultService.ts b/src/vs/workbench/contrib/testing/common/testResultService.ts index be1377587a1..ed6e98446a2 100644 --- a/src/vs/workbench/contrib/testing/common/testResultService.ts +++ b/src/vs/workbench/contrib/testing/common/testResultService.ts @@ -4,15 +4,18 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter, Event } from 'vs/base/common/event'; +import { Lazy } from 'vs/base/common/lazy'; +import { isDefined } from 'vs/base/common/types'; 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, ISerializedTestResults, ITestState, RunTestsRequest, TestResultItem } 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 +25,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. @@ -34,9 +49,20 @@ export interface ITestResult { readonly id: string; /** - * Gets whether the test run has finished. + * If the test is completed, the unix milliseconds time at which it was + * completed. If undefined, the test is still running. */ - readonly isComplete: boolean; + readonly completedAt: number | undefined; + + /** + * 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. @@ -47,10 +73,10 @@ export interface ITestResult { * Serializes the test result. Used to save and restore results * in the workspace. */ - toJSON(): ISerializedResults; + toJSON(): ISerializedTestResults | undefined; } -const makeEmptyCounts = () => { +export const makeEmptyCounts = () => { const o: Partial = {}; for (const state of statesInOrder) { o[state] = 0; @@ -129,6 +155,7 @@ const makeNodeAndChildren = ( test: IncrementalTestCollectionItem, byExtId: Map, byInternalId: Map, + isExecutedDirectly = true, ): TestResultItem => { const existing = byInternalId.get(test.id); if (existing) { @@ -136,28 +163,20 @@ 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); } } return mapped; }; -interface ISerializedResults { - id: string; - counts: TestStateCount; - items: Iterable<[extId: string, item: TestResultItem]>; -} - -interface TestResultItem extends IncrementalTestCollectionItem { - state: ITestState; - computedState: TestRunState; - retired: boolean; -} - /** * Results of a test. These are created when the test initially started running * and marked as "complete" when the run finishes. @@ -169,11 +188,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,12 +204,12 @@ 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 changeEmitter = new Emitter(); - private _complete = false; + private readonly changeEmitter = new Emitter(); + private _completedAt?: number; public readonly onChange = this.changeEmitter.event; public readonly onComplete = this.completeEmitter.event; @@ -203,8 +222,8 @@ export class LiveTestResult implements ITestResult { /** * @inheritdoc */ - public get isComplete() { - return this._complete; + public get completedAt() { + return this._completedAt; } /** @@ -213,7 +232,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(); @@ -254,6 +273,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; } @@ -271,10 +291,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); } } } @@ -291,15 +308,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 }); } /** @@ -318,7 +342,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 + }); } } } @@ -333,7 +363,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; } } @@ -344,22 +378,32 @@ export class LiveTestResult implements ITestResult { * Notifies the service that all tests are complete. */ public markComplete() { - if (this._complete) { + if (this._completedAt !== undefined) { throw new Error('cannot complete a test result multiple times'); } // un-queue any tests that weren't explicitly updated this.setAllToState(unsetState, t => t.state.state === TestRunState.Queued); - this._complete = true; + this._completedAt = Date.now(); this.completeEmitter.fire(); } /** * @inheritdoc */ - public toJSON(): ISerializedResults { - return { id: this.id, counts: this.counts, items: [...this.testByExtId.entries()] }; + public toJSON(): ISerializedTestResults | undefined { + return this.completedAt ? this.doSerialize.getValue() : undefined; } + + private readonly doSerialize = new Lazy((): ISerializedTestResults => ({ + id: this.id, + completedAt: this.completedAt!, + items: [...this.testByExtId.values()].map(entry => ({ + ...entry, + retired: undefined, + children: [...entry.children], + })), + })); } /** @@ -369,30 +413,47 @@ 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; + public readonly completedAt: number; - private readonly map = new Map(); + /** + * @inheritdoc + */ + public get tests() { + return this.byExtId.values(); + } - constructor(private readonly serialized: ISerializedResults) { - for (const [key, value] of serialized.items) { - this.map.set(key, value); + private readonly byExtId = new Map(); - value.retired = true; - for (const message of value.state.messages) { + constructor(private readonly serialized: ISerializedTestResults) { + this.id = serialized.id; + this.completedAt = serialized.completedAt; + + 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); } } @@ -400,13 +461,13 @@ class HydratedTestResult implements ITestResult { * @inheritdoc */ public getStateByExtId(extTestId: string) { - return this.map.get(extTestId); + return this.byExtId.get(extTestId); } /** * @inheritdoc */ - public toJSON(): ISerializedResults { + public toJSON(): ISerializedTestResults { return this.serialized; } } @@ -426,7 +487,7 @@ export interface ITestResultService { /** * Fired when a test changed it state, or its computed state is updated. */ - readonly onTestChanged: Event<[results: ITestResult, item: TestResultItem]>; + readonly onTestChanged: Event; /** * List of known test results. @@ -461,7 +522,7 @@ const RETAIN_LAST_RESULTS = 64; export class TestResultService implements ITestResultService { declare _serviceBrand: undefined; private changeResultEmitter = new Emitter(); - private testChangeEmitter = new Emitter<[results: ITestResult, item: TestResultItem]>(); + private testChangeEmitter = new Emitter(); /** * @inheritdoc @@ -479,7 +540,7 @@ export class TestResultService implements ITestResultService { public readonly onTestChanged = this.testChangeEmitter.event; private readonly isRunning: IContextKey; - private readonly serializedResults: StoredValue; + private readonly serializedResults: StoredValue; constructor(@IContextKeyService contextKeyService: IContextKeyService, @IStorageService storage: IStorageService) { this.isRunning = TestingContextKeys.isRunning.bindTo(contextKeyService); @@ -489,8 +550,15 @@ 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([])) { + // todo@connor4312: temp to migrate old insiders + if (value.completedAt) { + this.results.push(new HydratedTestResult(value)); + } + } + } catch (e) { + // outdated structure } } @@ -518,7 +586,7 @@ export class TestResultService implements ITestResultService { } result.onComplete(() => this.onComplete(result)); - result.onChange(t => this.testChangeEmitter.fire([result, t]), this.testChangeEmitter); + result.onChange(this.testChangeEmitter.fire, this.testChangeEmitter); this.isRunning.set(true); this.changeResultEmitter.fire({ started: result }); result.setAllToState(queuedState, () => true); @@ -539,7 +607,7 @@ export class TestResultService implements ITestResultService { const keep: ITestResult[] = []; const removed: ITestResult[] = []; for (const result of this.results) { - if (result.isComplete) { + if (result.completedAt !== undefined) { removed.push(result); } else { keep.push(result); @@ -547,20 +615,19 @@ export class TestResultService implements ITestResultService { } this.results = keep; - this.serializedResults.store(this.results.map(r => r.toJSON())); + this.serializedResults.store(this.results.map(r => r.toJSON()).filter(isDefined)); this.changeResultEmitter.fire({ removed }); } 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++) { - if (this.results[i].isComplete && !this.results[i + 1].isComplete) { - [this.results[i], this.results[i + 1]] = [this.results[i + 1], this.results[i]]; - } - } - - this.isRunning.set(!this.results[0]?.isComplete); - this.serializedResults.store(this.results.map(r => r.toJSON())); + this.resort(); + this.isRunning.set(this.results.length > 0 && this.results[0].completedAt === undefined); + this.serializedResults.store(this.results.map(r => r.toJSON()).filter(isDefined)); this.changeResultEmitter.fire({ completed: result }); } + + private resort() { + this.results.sort((a, b) => (b.completedAt ?? Number.MAX_SAFE_INTEGER) - (a.completedAt ?? Number.MAX_SAFE_INTEGER)); + } } 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 new file mode 100644 index 00000000000..a1eae07dd53 --- /dev/null +++ b/src/vs/workbench/contrib/testing/common/testingAutoRun.ts @@ -0,0 +1,112 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { mapFind } from 'vs/base/common/arrays'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { Iterable } from 'vs/base/common/iterator'; +import { Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +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, 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'; + +export interface ITestingAutoRun { + /** + * Toggles autorun on or off. + */ + toggle(): void; +} + +export const ITestingAutoRun = createDecorator('testingAutoRun'); + +export class TestingAutoRun extends Disposable implements ITestingAutoRun { + private enabled: IContextKey; + private runner = this._register(new MutableDisposable()); + + constructor( + @IContextKeyService contextKeyService: IContextKeyService, + @IWorkspaceTestCollectionService private readonly workspaceTests: IWorkspaceTestCollectionService, + @ITestService private readonly testService: ITestService, + @ITestResultService private readonly results: ITestResultService, + @IConfigurationService private readonly configuration: IConfigurationService, + ) { + super(); + this.enabled = TestingContextKeys.autoRun.bindTo(contextKeyService); + } + + /** + * @inheritdoc + */ + public toggle(): void { + const enabled = this.enabled.get(); + if (enabled) { + this.runner.value = undefined; + } else { + this.runner.value = this.makeRunner(); + } + + this.enabled.set(!enabled); + } + + /** + * Creates the runner. Is triggered when tests are marked as retired. + * Runs them on a debounce. + * + * We keep a workspace subscription open and try to find always find + * tests in the workspace -- as opposed to document tests. This is needed + * because a user could trigger a test run from a document, but close that + * document and edit another file that would cause the test to be retired. + */ + private makeRunner() { + let isRunning = false; + const rerunIds = new Map(); + const store = new DisposableStore(); + + let delay = getTestingConfiguration(this.configuration, TestingConfigKeys.AutoRunDelay); + + store.add(this.configuration.onDidChangeConfiguration(evt => { + delay = getTestingConfiguration(this.configuration, TestingConfigKeys.AutoRunDelay); + })); + + const workspaceTests = store.add(this.workspaceTests.subscribeToWorkspaceTests()); + + const scheduler = store.add(new RunOnceScheduler(async () => { + const tests = [...rerunIds.values()]; + + isRunning = true; + rerunIds.clear(); + await this.testService.runTests({ debug: false, tests, isAutoRun: true }); + isRunning = false; + + if (rerunIds.size > 0) { + scheduler.schedule(delay); + } + }, delay)); + + 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(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 })); + + if (!isRunning) { + scheduler.schedule(delay); + } + })); + + return store; + } +} 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 a7cbbb57a0e..6f951ef63ac 100644 --- a/src/vs/workbench/contrib/testing/common/testingContextKeys.ts +++ b/src/vs/workbench/contrib/testing/common/testingContextKeys.ts @@ -9,10 +9,13 @@ 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); export const isInPeek = new RawContextKey('testing.isInPeek', true); export const isPeekVisible = new RawContextKey('testing.isPeekVisible', false); export const explorerLocation = new RawContextKey('testing.explorerLocation', ViewContainerLocation.Sidebar); + export const autoRun = new RawContextKey('testing.autoRun', 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/common/workspaceTestCollectionService.ts b/src/vs/workbench/contrib/testing/common/workspaceTestCollectionService.ts index e38fb6d1ae6..dcd1f333174 100644 --- a/src/vs/workbench/contrib/testing/common/workspaceTestCollectionService.ts +++ b/src/vs/workbench/contrib/testing/common/workspaceTestCollectionService.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter } from 'vs/base/common/event'; +import { Iterable } from 'vs/base/common/iterator'; import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IWorkspaceContextService, IWorkspaceFolder, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace'; @@ -148,6 +149,19 @@ class TestSubscription extends Disposable { return [...this.collectionsForWorkspaces.values()].map(v => [v.folder, v.collection] as const); } + /** + * Returns whether there are any subscriptions with non-empty providers. + */ + public get isEmpty() { + for (const { collection } of this.collectionsForWorkspaces.values()) { + if (Iterable.some(collection.all, t => !!t.parent)) { + return false; + } + } + + return true; + } + constructor( @IWorkspaceContextService workspaceContext: IWorkspaceContextService, @ITestService private readonly testService: ITestService, 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..bf5deca24c4 --- /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(typeof rehydrated.completedAt, 'number'); + }); + + 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/timeline/browser/timelinePane.ts b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts index 70fa384c259..a0b5aa73388 100644 --- a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts +++ b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/timelinePane'; import { localize } from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; -import { IAction, ActionRunner, IActionViewItemProvider } from 'vs/base/common/actions'; +import { IAction, ActionRunner } from 'vs/base/common/actions'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { fromNow } from 'vs/base/common/date'; import { debounce } from 'vs/base/common/decorators'; @@ -36,7 +36,7 @@ import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService' import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionBar, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; import { createAndFillInContextMenuActions, createActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenuService, MenuId, registerAction2, Action2, MenuRegistry } from 'vs/platform/actions/common/actions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -1240,18 +1240,18 @@ class TimelinePaneCommands extends Disposable { } private getActions(menuId: MenuId, context: { key: string, value?: string }): { primary: IAction[]; secondary: IAction[]; } { - const scoped = this.contextKeyService.createScoped(); - scoped.createKey('view', this.pane.id); - scoped.createKey(context.key, context.value); + const contextKeyService = this.contextKeyService.createOverlay([ + ['view', this.pane.id], + [context.key, context.value], + ]); - const menu = this.menuService.createMenu(menuId, scoped); + const menu = this.menuService.createMenu(menuId, contextKeyService); const primary: IAction[] = []; const secondary: IAction[] = []; const result = { primary, secondary }; createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result, g => /^inline/.test(g)); menu.dispose(); - scoped.dispose(); return result; } diff --git a/src/vs/workbench/contrib/update/browser/update.ts b/src/vs/workbench/contrib/update/browser/update.ts index 63cfe385dca..2315cdb25b0 100644 --- a/src/vs/workbench/contrib/update/browser/update.ts +++ b/src/vs/workbench/contrib/update/browser/update.ts @@ -9,7 +9,7 @@ import { Action } from 'vs/base/common/actions'; import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IActivityService, NumberBadge, IBadge, ProgressBadge } from 'vs/workbench/services/activity/common/activity'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; @@ -21,12 +21,17 @@ import { ReleaseNotesManager } from './releaseNotesEditor'; import { isWindows } from 'vs/base/common/platform'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { RawContextKey, IContextKey, IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; +import { MenuRegistry, MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ShowCurrentReleaseNotesActionId, CheckForVSCodeUpdateActionId } from 'vs/workbench/contrib/update/common/update'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IProductService } from 'vs/platform/product/common/productService'; import product from 'vs/platform/product/common/product'; +import { IUserDataAutoSyncEnablementService, IUserDataSyncService, IUserDataSyncStoreManagementService, SyncStatus, UserDataSyncStoreType } from 'vs/platform/userDataSync/common/userDataSync'; +import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys'; +import { Promises } from 'vs/base/common/async'; +import { IUserDataSyncWorkbenchService } from 'vs/workbench/services/userDataSync/common/userDataSync'; +import { Event } from 'vs/base/common/event'; export const CONTEXT_UPDATE_STATE = new RawContextKey('updateState', StateType.Idle); @@ -524,27 +529,93 @@ export class SwitchProductQualityContribution extends Disposable implements IWor if (productQualityChangeHandler && (quality === 'stable' || quality === 'insider')) { const newQuality = quality === 'stable' ? 'insider' : 'stable'; const commandId = `update.switchQuality.${newQuality}`; - CommandsRegistry.registerCommand(commandId, async accessor => { - const dialogService = accessor.get(IDialogService); - - const res = await dialogService.confirm({ - type: 'info', - message: nls.localize('relaunchMessage', "Changing the version requires a reload to take effect"), - detail: newQuality === 'insider' ? - nls.localize('relaunchDetailInsiders', "Press the reload button to switch to the nightly pre-production version of VSCode.") : - nls.localize('relaunchDetailStable', "Press the reload button to switch to the monthly released stable version of VSCode."), - primaryButton: nls.localize('reload', "&&Reload") - }); - - if (res.confirmed) { - productQualityChangeHandler(newQuality); + const isSwitchingToInsiders = newQuality === 'insider'; + registerAction2(class SwitchQuality extends Action2 { + constructor() { + super({ + id: commandId, + title: isSwitchingToInsiders ? nls.localize('switchToInsiders', "Switch to Insiders Version...") : nls.localize('switchToStable', "Switch to Stable Version..."), + precondition: IsWebContext, + menu: { + id: MenuId.GlobalActivity, + when: IsWebContext, + group: '7_update', + } + }); } - }); - MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { - group: '7_update', - command: { - id: commandId, - title: newQuality === 'insider' ? nls.localize('switchToInsiders', "Switch to Insiders Version...") : nls.localize('switchToStable', "Switch to Stable Version...") + + async run(accessor: ServicesAccessor): Promise { + const dialogService = accessor.get(IDialogService); + const userDataAutoSyncEnablementService = accessor.get(IUserDataAutoSyncEnablementService); + const userDataSyncStoreManagementService = accessor.get(IUserDataSyncStoreManagementService); + const storageService = accessor.get(IStorageService); + const userDataSyncWorkbenchService = accessor.get(IUserDataSyncWorkbenchService); + const userDataSyncService = accessor.get(IUserDataSyncService); + + const selectSettingsSyncServiceDialogShownKey = 'switchQuality.selectSettingsSyncServiceDialogShown'; + const userDataSyncStore = userDataSyncStoreManagementService.userDataSyncStore; + let userDataSyncStoreType: UserDataSyncStoreType | undefined; + if (userDataSyncStore && isSwitchingToInsiders && userDataAutoSyncEnablementService.isEnabled() + && !storageService.getBoolean(selectSettingsSyncServiceDialogShownKey, StorageScope.GLOBAL, false)) { + userDataSyncStoreType = await this.selectSettingsSyncService(dialogService); + if (!userDataSyncStoreType) { + return; + } + storageService.store(selectSettingsSyncServiceDialogShownKey, true, StorageScope.GLOBAL, StorageTarget.USER); + if (userDataSyncStoreType === 'stable') { + // Update the stable service type in the current window, so that it uses stable service after switched to insiders version (after reload). + await userDataSyncStoreManagementService.switch(userDataSyncStoreType); + } + } + + const res = await dialogService.confirm({ + type: 'info', + message: nls.localize('relaunchMessage', "Changing the version requires a reload to take effect"), + detail: newQuality === 'insider' ? + nls.localize('relaunchDetailInsiders', "Press the reload button to switch to the nightly pre-production version of VSCode.") : + nls.localize('relaunchDetailStable', "Press the reload button to switch to the monthly released stable version of VSCode."), + primaryButton: nls.localize('reload', "&&Reload") + }); + + if (res.confirmed) { + const promises: Promise[] = []; + + // If sync is happening wait until it is finished before reload + if (userDataSyncService.status === SyncStatus.Syncing) { + promises.push(Event.toPromise(Event.filter(userDataSyncService.onDidChangeStatus, status => status !== SyncStatus.Syncing))); + } + + // Synchronise the store type option in insiders service, so that other clients using insiders service are also updated. + if (isSwitchingToInsiders) { + promises.push(userDataSyncWorkbenchService.synchroniseUserDataSyncStoreType()); + } + + await Promises.settled(promises); + + productQualityChangeHandler(newQuality); + } else { + // Reset + if (userDataSyncStoreType) { + storageService.remove(selectSettingsSyncServiceDialogShownKey, StorageScope.GLOBAL); + } + } + } + + private async selectSettingsSyncService(dialogService: IDialogService): Promise { + const res = await dialogService.show( + Severity.Info, + nls.localize('selectSyncService.message', "Choose the settings sync service to use after changing the version"), + [ + nls.localize('use insiders', "Insiders"), + nls.localize('use stable', "Stable (current)"), + nls.localize('cancel', "Cancel"), + ], + { + detail: nls.localize('selectSyncService.detail', "Insiders version of VSCode will synchronize your settings, keybindings, extensions, snippets and UI State using separate insiders settings sync service by default."), + cancelId: 2 + } + ); + return res.choice === 0 ? 'insiders' : res.choice === 1 ? 'stable' : undefined; } }); } diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 33b6202653d..2524044c559 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -111,7 +111,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo @IOutputService private readonly outputService: IOutputService, @IUserDataSyncAccountService readonly authTokenService: IUserDataSyncAccountService, @IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService, - @IUserDataAutoSyncService private readonly userDataAutoSyncService: IUserDataAutoSyncService, + @IUserDataAutoSyncService userDataAutoSyncService: IUserDataAutoSyncService, @ITextModelService textModelResolverService: ITextModelService, @IPreferencesService private readonly preferencesService: IPreferencesService, @ITelemetryService private readonly telemetryService: ITelemetryService, @@ -288,7 +288,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo case UserDataSyncErrorCode.TurnedOff: this.notificationService.notify({ severity: Severity.Info, - message: localize('turned off', "Settings sync was turned off from another device, please sign in again to turn on sync."), + message: localize('turned off', "Settings sync was turned off from another device, please turn on sync again."), actions: { primary: [new Action('turn on sync', localize('turn on sync', "Turn on Settings Sync..."), undefined, true, () => this.turnOn())] } @@ -323,11 +323,34 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } }); return; + + case UserDataSyncErrorCode.ServiceChanged: + this.notificationService.notify({ + severity: Severity.Info, + message: this.userDataSyncStoreManagementService.userDataSyncStore?.type === 'insiders' ? + localize('service switched to insiders', "Settings Sync has been switched to insiders service") : + localize('service switched to stable', "Settings Sync has been switched to stable service"), + }); + + return; + case UserDataSyncErrorCode.DefaultServiceChanged: - if (isEqual(this.userDataSyncStoreManagementService.userDataSyncStore?.url, this.userDataSyncStoreManagementService.userDataSyncStore?.insidersUrl)) { + // Settings sync is using separate service + if (this.userDataAutoSyncEnablementService.isEnabled()) { this.notificationService.notify({ severity: Severity.Info, - message: localize('switched to insiders', "Settings sync now uses a separate service, more information is available in the [release notes](https://code.visualstudio.com/updates/v1_48#_settings-sync)."), + message: localize('using separate service', "Settings sync now uses a separate service, more information is available in the [Settings Sync Documentation](https://aka.ms/vscode-settings-sync-help#_syncing-stable-versus-insiders)."), + }); + } + + // If settings sync got turned off then ask user to turn on sync again. + else { + this.notificationService.notify({ + severity: Severity.Info, + message: localize('service changed and turned off', "Settings sync was turned off because {0} now uses a separate service. Please turn on sync again.", this.productService.nameLong), + actions: { + primary: [new Action('turn on sync', localize('turn on sync', "Turn on Settings Sync..."), undefined, true, () => this.turnOn())] + } }); } return; @@ -1030,7 +1053,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo }); } run(accessor: ServicesAccessor): Promise { - return that.userDataAutoSyncService.triggerSync([syncNowCommand.id], false, true); + return that.userDataSyncWorkbenchService.syncNow(); } })); } 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..1288ef1abb5 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; } @@ -506,12 +508,15 @@ newFrame.setAttribute('id', 'pending-frame'); newFrame.setAttribute('frameborder', '0'); newFrame.setAttribute('sandbox', options.allowScripts ? 'allow-scripts allow-forms allow-same-origin allow-pointer-lock allow-downloads' : 'allow-same-origin allow-pointer-lock'); + newFrame.setAttribute('allow', options.allowScripts ? 'clipboard-read; clipboard-write;' : ''); if (host.fakeLoad) { // We should just be able to use srcdoc, but I wasn't // seeing the service worker applying properly. // Fake load an empty on the correct origin and then write real html // into it to get around this. newFrame.src = `./fake.html?id=${ID}`; + } else { + newFrame.src = `about:blank?webviewFrame`; } newFrame.style.cssText = 'display: block; margin: 0; overflow: hidden; position: absolute; width: 100%; height: 100%; visibility: hidden'; document.body.appendChild(newFrame); @@ -589,6 +594,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/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index d006ebb3243..f6ff3b6e7be 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -92,6 +92,7 @@ export class IFrameWebview extends BaseWebview implements Web const element = document.createElement('iframe'); element.className = `webview ${options.customClasses || ''}`; element.sandbox.add('allow-scripts', 'allow-same-origin', 'allow-forms', 'allow-pointer-lock', 'allow-downloads'); + element.setAttribute('allow', 'clipboard-read; clipboard-write;'); element.style.border = 'none'; element.style.width = '100%'; element.style.height = '100%'; 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.css b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css index 8b777cf64a7..5b4d1fcb93d 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css +++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css @@ -330,3 +330,7 @@ text-decoration: underline; background: transparent; } + +.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide .showOnStartup { + text-align: center; +} diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts index bed7ba568fb..482b3ca428a 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts +++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts @@ -26,8 +26,10 @@ import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Schemas } from 'vs/base/common/network'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; const SLIDE_TRANSITION_TIME_MS = 250; +const configurationKey = 'workbench.startupEditor'; export const gettingStartedInputTypeId = 'workbench.editors.gettingStartedInput'; @@ -89,6 +91,7 @@ export class GettingStartedPage extends EditorPane { @IProductService private readonly productService: IProductService, @IKeybindingService private readonly keybindingService: IKeybindingService, @IGettingStartedService private readonly gettingStartedService: IGettingStartedService, + @IConfigurationService private readonly configurationService: IConfigurationService, @ITelemetryService telemetryService: ITelemetryService, @IOpenerService private readonly openerService: IOpenerService, @IThemeService themeService: IThemeService, @@ -101,7 +104,11 @@ export class GettingStartedPage extends EditorPane { this.gettingStartedCategories = this.gettingStartedService.getCategories(); this._register(this.dispatchListeners); - this._register(this.gettingStartedService.onDidAddTask(task => console.log('added new task', task, 'that isnt being rendered yet'))); + this._register(this.gettingStartedService.onDidAddTask(task => { + this.gettingStartedCategories = this.gettingStartedService.getCategories(); + this.buildCategoriesSlide(); + })); + this._register(this.gettingStartedService.onDidAddCategory(category => console.log('added new category', category, 'that isnt being rendered yet'))); this._register(this.gettingStartedService.onDidProgressTask(task => { const category = this.gettingStartedCategories.find(category => category.id === task.category); @@ -239,13 +246,18 @@ 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(); } private updateMediaSourceForColorMode(element: HTMLImageElement, sources: { hc: URI, dark: URI, light: URI }) { const themeType = this.themeService.getColorTheme().type; - element.src = sources[themeType].toString(); + element.src = sources[themeType].toString(true); } createEditor(parent: HTMLElement) { @@ -270,7 +282,11 @@ export class GettingStartedPage extends EditorPane { ); const gettingStartedPage = - $('.gettingStarted', { role: 'document' }, + $('.gettingStarted.welcomePageFocusElement', { + role: 'document', + tabIndex: '0', + 'aria-label': localize('gettingStartedLabel', "Getting Started. Overview of how to get up to speed with your editor.") + }, $('.gettingStartedSlideCategory.gettingStartedSlide.categories'), tasksSlide ); @@ -295,7 +311,7 @@ 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', {}, @@ -303,18 +319,33 @@ 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); }); categoryScrollContainer.appendChild(categoriesContainer); - categoryScrollContainer.appendChild($('.footer', {}, $('button.skip.button-link', { 'x-dispatch': 'skip' }, localize('gettingStarted.skip', "Skip")))); + const showOnStartupCheckbox = $('input.checkbox', { id: 'showOnStartup', type: 'checkbox' }) as HTMLInputElement; + categoryScrollContainer.appendChild( + $('.footer', {}, + // $('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"))) + )); + + showOnStartupCheckbox.checked = this.configurationService.getValue(configurationKey) === 'gettingStarted'; + this._register(addDisposableListener(showOnStartupCheckbox, 'click', () => { + this.configurationService.updateValue(configurationKey, showOnStartupCheckbox.checked ? 'gettingStarted' : 'welcomePage'); + })); if (this.categoriesScrollbar) { this.categoriesScrollbar.dispose(); } this.categoriesScrollbar = this._register(new DomScrollableElement(categoryScrollContainer, {})); @@ -344,7 +375,6 @@ export class GettingStartedPage extends EditorPane { this.setSlide('details'); this.buildCategorySlide(this.editorInput.selectedCategory, this.editorInput.selectedTask); } else { - this.focusFirstUncompletedCategory(); this.setSlide('categories'); } } @@ -366,6 +396,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) { @@ -411,6 +445,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', {}, @@ -433,7 +468,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)); @@ -521,6 +556,7 @@ export class GettingStartedPage extends EditorPane { slideManager.classList.add('showCategories'); this.container.querySelector('.gettingStartedSlideDetails')!.querySelectorAll('button').forEach(button => button.disabled = true); this.container.querySelector('.gettingStartedSlideCategory')!.querySelectorAll('button').forEach(button => button.disabled = false); + (this.container.querySelector('.welcomePageFocusElement') as HTMLElement)?.focus(); } else { slideManager.classList.add('showDetails'); slideManager.classList.remove('showCategories'); diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.contribution.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.contribution.ts index d49c5f3c91d..e65b6f5b462 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.contribution.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.contribution.ts @@ -30,7 +30,7 @@ Registry.as(ConfigurationExtensions.Configuration) localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.welcomePageInEmptyWorkbench' }, "Open the Welcome page when opening an empty workbench."), localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.gettingStarted' }, "Open the Getting Started page.")] ], - 'default': 'welcomePage', + 'default': 'gettingStarted', 'description': localize('workbench.startupEditor', "Controls which editor is shown at startup, if none are restored from the previous session.") }, } diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts index 4952cefc6e8..fa02ee7ae2e 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts @@ -336,7 +336,7 @@ class WelcomePage extends Disposable { } private onReady(container: HTMLElement, recentlyOpened: Promise, installedExtensions: Promise): void { - const enabled = isWelcomePageEnabled(this.configurationService, this.contextService); + const enabled = this.configurationService.getValue(configurationKey) === 'welcomePage'; const showOnStartup = container.querySelector('#showOnStartup'); if (enabled) { showOnStartup.setAttribute('checked', 'checked'); diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts index 5422d519840..5f06b8c2b02 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts @@ -92,6 +92,7 @@ export class WalkThroughPart extends EditorPane { createEditor(container: HTMLElement): void { this.content = document.createElement('div'); + this.content.classList.add('welcomePageFocusElement'); this.content.tabIndex = 0; this.content.style.outlineStyle = 'none'; diff --git a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts index 8d6cb051ee9..6240c337b3a 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 WorkspaceTrustEditorInputFactory 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, WorkspaceTrustEditorInputFactory); + +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..8ffa5670f52 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'; @@ -51,8 +52,8 @@ import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/ur 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 { LoggerChannelClient } from 'vs/platform/log/common/logIpc'; class DesktopMain extends Disposable { @@ -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(); @@ -166,7 +170,7 @@ class DesktopMain extends Disposable { serviceCollection.set(IProductService, this.productService); // Logger - const loggerService = new LoggerService(mainProcessService); + const loggerService = new LoggerChannelClient(mainProcessService.getChannel('logger')); serviceCollection.set(ILoggerService, loggerService); // Log @@ -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/actions/windowActions.ts b/src/vs/workbench/electron-sandbox/actions/windowActions.ts index f153e787029..c6567c33e06 100644 --- a/src/vs/workbench/electron-sandbox/actions/windowActions.ts +++ b/src/vs/workbench/electron-sandbox/actions/windowActions.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/actions'; import { URI } from 'vs/base/common/uri'; import { Action } from 'vs/base/common/actions'; -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { applyZoom } from 'vs/platform/windows/electron-sandbox/window'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { getZoomLevel } from 'vs/base/browser/browser'; @@ -25,7 +25,7 @@ import { isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/pla export class CloseCurrentWindowAction extends Action { static readonly ID = 'workbench.action.closeWindow'; - static readonly LABEL = nls.localize('closeWindow', "Close Window"); + static readonly LABEL = localize('closeWindow', "Close Window"); constructor( id: string, @@ -71,7 +71,7 @@ export abstract class BaseZoomAction extends Action { export class ZoomInAction extends BaseZoomAction { static readonly ID = 'workbench.action.zoomIn'; - static readonly LABEL = nls.localize('zoomIn', "Zoom In"); + static readonly LABEL = localize('zoomIn', "Zoom In"); constructor( id: string, @@ -89,7 +89,7 @@ export class ZoomInAction extends BaseZoomAction { export class ZoomOutAction extends BaseZoomAction { static readonly ID = 'workbench.action.zoomOut'; - static readonly LABEL = nls.localize('zoomOut', "Zoom Out"); + static readonly LABEL = localize('zoomOut', "Zoom Out"); constructor( id: string, @@ -107,7 +107,7 @@ export class ZoomOutAction extends BaseZoomAction { export class ZoomResetAction extends BaseZoomAction { static readonly ID = 'workbench.action.zoomReset'; - static readonly LABEL = nls.localize('zoomReset', "Reset Zoom"); + static readonly LABEL = localize('zoomReset', "Reset Zoom"); constructor( id: string, @@ -126,12 +126,12 @@ export abstract class BaseSwitchWindow extends Action { private readonly closeWindowAction: IQuickInputButton = { iconClass: Codicon.removeClose.classNames, - tooltip: nls.localize('close', "Close Window") + tooltip: localize('close', "Close Window") }; private readonly closeDirtyWindowAction: IQuickInputButton = { iconClass: 'dirty-window ' + Codicon.closeDirty, - tooltip: nls.localize('close', "Close Window"), + tooltip: localize('close', "Close Window"), alwaysVisible: true }; @@ -153,16 +153,16 @@ export abstract class BaseSwitchWindow extends Action { const currentWindowId = this.nativeHostService.windowId; const windows = await this.nativeHostService.getWindows(); - const placeHolder = nls.localize('switchWindowPlaceHolder', "Select a window to switch to"); + const placeHolder = localize('switchWindowPlaceHolder', "Select a window to switch to"); const picks = windows.map(window => { const resource = window.filename ? URI.file(window.filename) : isSingleFolderWorkspaceIdentifier(window.workspace) ? window.workspace.uri : isWorkspaceIdentifier(window.workspace) ? window.workspace.configPath : undefined; const fileKind = window.filename ? FileKind.FILE : isSingleFolderWorkspaceIdentifier(window.workspace) ? FileKind.FOLDER : isWorkspaceIdentifier(window.workspace) ? FileKind.ROOT_FOLDER : FileKind.FILE; return { payload: window.id, label: window.title, - ariaLabel: window.dirty ? nls.localize('windowDirtyAriaLabel', "{0}, dirty window", window.title) : window.title, + ariaLabel: window.dirty ? localize('windowDirtyAriaLabel', "{0}, dirty window", window.title) : window.title, iconClasses: getIconClasses(this.modelService, this.modeService, resource, fileKind), - description: (currentWindowId === window.id) ? nls.localize('current', "Current Window") : undefined, + description: (currentWindowId === window.id) ? localize('current', "Current Window") : undefined, buttons: currentWindowId !== window.id ? window.dirty ? [this.closeDirtyWindowAction] : [this.closeWindowAction] : undefined }; }); @@ -188,7 +188,7 @@ export abstract class BaseSwitchWindow extends Action { export class SwitchWindow extends BaseSwitchWindow { static readonly ID = 'workbench.action.switchWindow'; - static readonly LABEL = nls.localize('switchWindow', "Switch Window..."); + static readonly LABEL = localize('switchWindow', "Switch Window..."); constructor( id: string, @@ -210,7 +210,7 @@ export class SwitchWindow extends BaseSwitchWindow { export class QuickSwitchWindow extends BaseSwitchWindow { static readonly ID = 'workbench.action.quickSwitchWindow'; - static readonly LABEL = nls.localize('quickSwitchWindow', "Quick Switch Window..."); + static readonly LABEL = localize('quickSwitchWindow', "Quick Switch Window..."); constructor( id: string, 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..7b045ab3243 100644 --- a/src/vs/workbench/electron-sandbox/desktop.main.ts +++ b/src/vs/workbench/electron-sandbox/desktop.main.ts @@ -9,35 +9,41 @@ 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 { 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 { ElectronIPCMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; +import { SimpleConfigurationService, simpleFileSystemProvider, SimpleSignService, SimpleNativeWorkbenchEnvironmentService, SimpleWorkspaceService, SimpleLogService } from 'vs/workbench/electron-sandbox/sandbox.simpleservices'; +import { LoggerChannelClient } from 'vs/platform/log/common/logIpc'; class DesktopMain extends Disposable { @@ -65,7 +71,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 +116,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,6 +151,10 @@ class DesktopMain extends Disposable { // Product serviceCollection.set(IProductService, this.productService); + // Logger + const loggerService = new LoggerChannelClient(mainProcessService.getChannel('logger')); + serviceCollection.set(ILoggerService, loggerService); + // Log const logService = new SimpleLogService(); serviceCollection.set(ILogService, logService); @@ -209,6 +222,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 +236,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 +270,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/parts/dialogs/dialogHandler.ts b/src/vs/workbench/electron-sandbox/parts/dialogs/dialogHandler.ts index 6e9b7b93f98..3805a175897 100644 --- a/src/vs/workbench/electron-sandbox/parts/dialogs/dialogHandler.ts +++ b/src/vs/workbench/electron-sandbox/parts/dialogs/dialogHandler.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { fromNow } from 'vs/base/common/date'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { isLinux, isLinuxSnap, isWindows } from 'vs/base/common/platform'; @@ -58,13 +58,13 @@ export class NativeDialogHandler implements IDialogHandler { if (confirmation.primaryButton) { buttons.push(confirmation.primaryButton); } else { - buttons.push(nls.localize({ key: 'yesButton', comment: ['&& denotes a mnemonic'] }, "&&Yes")); + buttons.push(localize({ key: 'yesButton', comment: ['&& denotes a mnemonic'] }, "&&Yes")); } if (confirmation.secondaryButton) { buttons.push(confirmation.secondaryButton); } else if (typeof confirmation.secondaryButton === 'undefined') { - buttons.push(nls.localize('cancelButton', "Cancel")); + buttons.push(localize('cancelButton', "Cancel")); } const opts: MessageBoxOptions = { @@ -166,7 +166,7 @@ export class NativeDialogHandler implements IDialogHandler { const osProps = await this.nativeHostService.getOSProperties(); const detailString = (useAgo: boolean): string => { - return nls.localize({ key: 'aboutDetail', comment: ['Electron, Chrome, Node.js and V8 are product names that need no translation'] }, + return localize({ key: 'aboutDetail', comment: ['Electron, Chrome, Node.js and V8 are product names that need no translation'] }, "Version: {0}\nCommit: {1}\nDate: {2}\nElectron: {3}\nChrome: {4}\nNode.js: {5}\nV8: {6}\nOS: {7}", version, this.productService.commit || 'Unknown', @@ -182,8 +182,8 @@ export class NativeDialogHandler implements IDialogHandler { const detail = detailString(true); const detailToCopy = detailString(false); - const ok = nls.localize('okButton', "OK"); - const copy = mnemonicButtonLabel(nls.localize({ key: 'copy', comment: ['&& denotes a mnemonic'] }, "&&Copy")); + const ok = localize('okButton', "OK"); + const copy = mnemonicButtonLabel(localize({ key: 'copy', comment: ['&& denotes a mnemonic'] }, "&&Copy")); let buttons: string[]; if (isLinux) { buttons = [copy, ok]; diff --git a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts index c2258de1123..10a7e668c9d 100644 --- a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { getZoomFactor } from 'vs/base/browser/browser'; -import * as DOM from 'vs/base/browser/dom'; +import { $, addDisposableListener, append, Dimension, EventType, hide, prepend, runAtThisOrScheduleAtNextAnimationFrame, show } from 'vs/base/browser/dom'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -86,9 +86,9 @@ export class TitlebarPart extends BrowserTitleBarPart { if (this.resizer) { if (maximized) { - DOM.hide(this.resizer); + hide(this.resizer); } else { - DOM.show(this.resizer); + show(this.resizer); } } @@ -98,9 +98,9 @@ export class TitlebarPart extends BrowserTitleBarPart { private onMenubarFocusChanged(focused: boolean) { if ((isWindows || isLinux) && this.currentMenubarVisibility !== 'compact' && this.dragRegion) { if (focused) { - DOM.hide(this.dragRegion); + hide(this.dragRegion); } else { - DOM.show(this.dragRegion); + show(this.dragRegion); } } } @@ -110,8 +110,8 @@ export class TitlebarPart extends BrowserTitleBarPart { if ((isWindows || isLinux) && this.currentMenubarVisibility === 'toggle' && visible) { // Hack to fix issue #52522 with layered webkit-app-region elements appearing under cursor if (this.dragRegion) { - DOM.hide(this.dragRegion); - setTimeout(() => DOM.show(this.dragRegion!), 50); + hide(this.dragRegion); + setTimeout(() => show(this.dragRegion!), 50); } } @@ -173,27 +173,27 @@ export class TitlebarPart extends BrowserTitleBarPart { if (this.appIcon) { this.onUpdateAppIconDragBehavior(); - this._register(DOM.addDisposableListener(this.appIcon, DOM.EventType.DBLCLICK, (e => { + this._register(addDisposableListener(this.appIcon, EventType.DBLCLICK, (e => { this.nativeHostService.closeWindow(); }))); } // Draggable region that we can manipulate for #52522 - this.dragRegion = DOM.prepend(this.element, DOM.$('div.titlebar-drag-region')); + this.dragRegion = prepend(this.element, $('div.titlebar-drag-region')); // Window Controls (Native Windows/Linux) if (!isMacintosh) { - this.windowControls = DOM.append(this.element, DOM.$('div.window-controls-container')); + this.windowControls = append(this.element, $('div.window-controls-container')); // Minimize - const minimizeIcon = DOM.append(this.windowControls, DOM.$('div.window-icon.window-minimize' + Codicon.chromeMinimize.cssSelector)); - this._register(DOM.addDisposableListener(minimizeIcon, DOM.EventType.CLICK, e => { + const minimizeIcon = append(this.windowControls, $('div.window-icon.window-minimize' + Codicon.chromeMinimize.cssSelector)); + this._register(addDisposableListener(minimizeIcon, EventType.CLICK, e => { this.nativeHostService.minimizeWindow(); })); // Restore - this.maxRestoreControl = DOM.append(this.windowControls, DOM.$('div.window-icon.window-max-restore')); - this._register(DOM.addDisposableListener(this.maxRestoreControl, DOM.EventType.CLICK, async e => { + this.maxRestoreControl = append(this.windowControls, $('div.window-icon.window-max-restore')); + this._register(addDisposableListener(this.maxRestoreControl, EventType.CLICK, async e => { const maximized = await this.nativeHostService.isMaximized(); if (maximized) { return this.nativeHostService.unmaximizeWindow(); @@ -203,13 +203,13 @@ export class TitlebarPart extends BrowserTitleBarPart { })); // Close - const closeIcon = DOM.append(this.windowControls, DOM.$('div.window-icon.window-close' + Codicon.chromeClose.cssSelector)); - this._register(DOM.addDisposableListener(closeIcon, DOM.EventType.CLICK, e => { + const closeIcon = append(this.windowControls, $('div.window-icon.window-close' + Codicon.chromeClose.cssSelector)); + this._register(addDisposableListener(closeIcon, EventType.CLICK, e => { this.nativeHostService.closeWindow(); })); // Resizer - this.resizer = DOM.append(this.element, DOM.$('div.resizer')); + this.resizer = append(this.element, $('div.resizer')); this._register(this.layoutService.onMaximizeChange(maximized => this.onDidChangeMaximized(maximized))); this.onDidChangeMaximized(this.layoutService.isWindowMaximized()); @@ -218,7 +218,7 @@ export class TitlebarPart extends BrowserTitleBarPart { return ret; } - updateLayout(dimension: DOM.Dimension): void { + updateLayout(dimension: Dimension): void { this.lastLayoutDimensions = dimension; if (getTitleBarStyle(this.configurationService) === 'custom') { @@ -247,10 +247,10 @@ export class TitlebarPart extends BrowserTitleBarPart { } } - DOM.runAtThisOrScheduleAtNextAnimationFrame(() => this.adjustTitleMarginToCenter()); + runAtThisOrScheduleAtNextAnimationFrame(() => this.adjustTitleMarginToCenter()); if (this.customMenubar) { - const menubarDimension = new DOM.Dimension(0, dimension.height); + const menubarDimension = new Dimension(0, dimension.height); this.customMenubar.layout(menubarDimension); } } diff --git a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts index 4a2f05f35f9..fb480250176 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'; @@ -53,6 +51,7 @@ import { BrowserKeyboardLayoutService } from 'vs/workbench/services/keybinding/b import { TerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminalInstanceService'; import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; +import { ConsoleLogger, LogService } from 'vs/platform/log/common/log'; //#region Environment @@ -177,13 +176,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,6 +185,17 @@ export class SimpleConfigurationService extends BaseSimpleConfigurationService i //#endregion +//#region Signing + +export class SimpleSignService implements ISignService { + + declare readonly _serviceBrand: undefined; + + async sign(value: string): Promise { return value; } +} + +//#endregion + //#region Logger export class SimpleLogService extends LogService { @@ -203,15 +206,6 @@ export class SimpleLogService extends LogService { } -export class SimpleSignService implements ISignService { - - declare readonly _serviceBrand: undefined; - - async sign(value: string): Promise { return value; } -} - -//#endregion - //#region Files diff --git a/src/vs/workbench/services/authentication/browser/authenticationService.ts b/src/vs/workbench/services/authentication/browser/authenticationService.ts index 69df3cd2cdc..6c1efc704be 100644 --- a/src/vs/workbench/services/authentication/browser/authenticationService.ts +++ b/src/vs/workbench/services/authentication/browser/authenticationService.ts @@ -110,8 +110,8 @@ export interface IAuthenticationService { unregisterAuthenticationProvider(id: string): void; isAccessAllowed(providerId: string, accountName: string, extensionId: string): boolean; showGetSessionPrompt(providerId: string, accountName: string, extensionId: string, extensionName: string): Promise; - selectSession(providerId: string, extensionId: string, extensionName: string, possibleSessions: AuthenticationSession[]): Promise; - requestSessionAccess(providerId: string, extensionId: string, extensionName: string, possibleSessions: AuthenticationSession[]): void; + selectSession(providerId: string, extensionId: string, extensionName: string, possibleSessions: readonly AuthenticationSession[]): Promise; + requestSessionAccess(providerId: string, extensionId: string, extensionName: string, possibleSessions: readonly AuthenticationSession[]): void; completeSessionAccessRequest(providerId: string, extensionId: string, extensionName: string): Promise requestNewSession(providerId: string, scopes: string[], extensionId: string, extensionName: string): Promise; sessionsUpdate(providerId: string, event: AuthenticationSessionsChangeEvent): void; @@ -124,14 +124,14 @@ export interface IAuthenticationService { declaredProviders: AuthenticationProviderInformation[]; readonly onDidChangeDeclaredProviders: Event; - getSessions(providerId: string, activateImmediate?: boolean): Promise>; + getSessions(id: string, scopes?: string[], activateImmediate?: boolean): Promise>; getLabel(providerId: string): string; supportsMultipleAccounts(providerId: string): boolean; - login(providerId: string, scopes: string[], activateImmediate?: boolean): Promise; - logout(providerId: string, sessionId: string): Promise; + createSession(providerId: string, scopes: string[], activateImmediate?: boolean): Promise; + removeSession(providerId: string, sessionId: string): Promise; manageTrustedExtensionsForAccount(providerId: string, accountName: string): Promise; - signOutOfAccount(providerId: string, accountName: string): Promise; + removeAccountSessions(providerId: string, accountName: string, sessions: AuthenticationSession[]): Promise; } export interface AllowedExtension { @@ -315,7 +315,6 @@ export class AuthenticationService extends Disposable implements IAuthentication const provider = this._authenticationProviders.get(id); if (provider) { this._onDidChangeSessions.fire({ providerId: id, label: provider.label, event: event }); - await provider.updateSessionItems(event); if (event.added) { await this.updateNewSessionRequests(provider, event.added); @@ -473,7 +472,7 @@ export class AuthenticationService extends Disposable implements IAuthentication quickPick.placeholder = nls.localize('getSessionPlateholder', "Select an account for '{0}' to use or Esc to cancel", extensionName); quickPick.onDidAccept(async _ => { - const session = quickPick.selectedItems[0].session ?? await this.login(providerId, availableSessions[0].scopes as string[]); + const session = quickPick.selectedItems[0].session ?? await this.createSession(providerId, availableSessions[0].scopes as string[]); const accountName = session.account.label; const allowList = readAllowedExtensions(this.storageService, providerId, accountName); @@ -610,7 +609,7 @@ export class AuthenticationService extends Disposable implements IAuthentication handler: async (accessor) => { const authenticationService = accessor.get(IAuthenticationService); const storageService = accessor.get(IStorageService); - const session = await authenticationService.login(providerId, scopes); + const session = await authenticationService.createSession(providerId, scopes); // Add extension to allow list since user explicitly signed in on behalf of it const allowList = readAllowedExtensions(storageService, providerId, session.account.label); @@ -694,28 +693,28 @@ export class AuthenticationService extends Disposable implements IAuthentication return Promise.race([didRegister, didTimeout]); } - async getSessions(id: 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(); + return await authProvider.getSessions(scopes); } catch (_) { throw new Error(`No authentication provider '${id}' is currently registered.`); } } - async login(id: string, scopes: string[], activateImmediate: boolean = false): Promise { + async createSession(id: string, scopes: string[], activateImmediate: boolean = false): Promise { try { const authProvider = this._authenticationProviders.get(id) || await this.tryActivateProvider(id, activateImmediate); - return await authProvider.login(scopes); + return await authProvider.createSession(scopes); } catch (_) { throw new Error(`No authentication provider '${id}' is currently registered.`); } } - async logout(id: string, sessionId: string): Promise { + async removeSession(id: string, sessionId: string): Promise { const authProvider = this._authenticationProviders.get(id); if (authProvider) { - return authProvider.logout(sessionId); + return authProvider.removeSession(sessionId); } else { throw new Error(`No authentication provider '${id}' is currently registered.`); } @@ -730,10 +729,10 @@ export class AuthenticationService extends Disposable implements IAuthentication } } - async signOutOfAccount(id: string, accountName: string): Promise { + async removeAccountSessions(id: string, accountName: string, sessions: AuthenticationSession[]): Promise { const authProvider = this._authenticationProviders.get(id); if (authProvider) { - return authProvider.signOut(accountName); + return authProvider.removeAccountSessions(accountName, sessions); } else { throw new Error(`No authentication provider '${id}' is currently registered.`); } diff --git a/src/vs/workbench/services/configuration/browser/configuration.ts b/src/vs/workbench/services/configuration/browser/configuration.ts index 04f37bd3a1a..91d485e0233 100644 --- a/src/vs/workbench/services/configuration/browser/configuration.ts +++ b/src/vs/workbench/services/configuration/browser/configuration.ts @@ -22,6 +22,7 @@ import { IConfigurationModel } from 'vs/platform/configuration/common/configurat import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { hash } from 'vs/base/common/hash'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { ILogService } from 'vs/platform/log/common/log'; export class UserConfiguration extends Disposable { @@ -38,6 +39,7 @@ export class UserConfiguration extends Disposable { private readonly scopes: ConfigurationScope[] | undefined, private readonly fileService: IFileService, private readonly uriIdentityService: IUriIdentityService, + private readonly logService: ILogService, ) { super(); this.userConfiguration.value = new UserSettings(this.userSettingsResource, this.scopes, uriIdentityService.extUri, this.fileService); @@ -56,7 +58,7 @@ export class UserConfiguration extends Disposable { const folder = this.uriIdentityService.extUri.dirname(this.userSettingsResource); const standAloneConfigurationResources: [string, URI][] = [TASKS_CONFIGURATION_KEY].map(name => ([name, this.uriIdentityService.extUri.joinPath(folder, `${name}.json`)])); - const fileServiceBasedConfiguration = new FileServiceBasedConfiguration(folder.toString(), [this.userSettingsResource], standAloneConfigurationResources, this.scopes, this.fileService, this.uriIdentityService); + const fileServiceBasedConfiguration = new FileServiceBasedConfiguration(folder.toString(), [this.userSettingsResource], standAloneConfigurationResources, this.scopes, this.fileService, this.uriIdentityService, this.logService); const configurationModel = await fileServiceBasedConfiguration.loadConfiguration(); this.userConfiguration.value = fileServiceBasedConfiguration; @@ -89,8 +91,9 @@ class FileServiceBasedConfiguration extends Disposable { private readonly settingsResources: URI[], private readonly standAloneConfigurationResources: [string, URI][], private readonly scopes: ConfigurationScope[] | undefined, - private fileService: IFileService, - private uriIdentityService: IUriIdentityService + private readonly fileService: IFileService, + private readonly uriIdentityService: IUriIdentityService, + private readonly logService: ILogService, ) { super(); this.allResources = [...this.settingsResources, ...this.standAloneConfigurationResources.map(([, resource]) => resource)]; @@ -107,9 +110,13 @@ class FileServiceBasedConfiguration extends Disposable { const resolveContents = async (resources: URI[]): Promise<(string | undefined)[]> => { return Promise.all(resources.map(async resource => { try { - const content = await this.fileService.readFile(resource); - return content.value.toString(); + const content = (await this.fileService.readFile(resource)).value.toString(); + if (!content) { + this.logService.debug(`Configuration file '${resource.toString()}' is empty`); + } + return content; } catch (error) { + this.logService.trace(`Error while resolving configuration file '${resource.toString()}': ${errors.getErrorMessage(error)}`); if ((error).fileOperationResult !== FileOperationResult.FILE_NOT_FOUND && (error).fileOperationResult !== FileOperationResult.FILE_NOT_DIRECTORY) { errors.onUnexpectedError(error); @@ -694,6 +701,7 @@ export class FolderConfiguration extends Disposable { private readonly workbenchState: WorkbenchState, fileService: IFileService, uriIdentityService: IUriIdentityService, + logService: ILogService, private readonly configurationCache: IConfigurationCache ) { super(); @@ -704,12 +712,12 @@ export class FolderConfiguration extends Disposable { this.folderConfiguration = this.cachedFolderConfiguration; whenProviderRegistered(workspaceFolder.uri, fileService) .then(() => { - this.folderConfiguration = this._register(this.createFileServiceBasedConfiguration(fileService, uriIdentityService)); + this.folderConfiguration = this._register(this.createFileServiceBasedConfiguration(fileService, uriIdentityService, logService)); this._register(this.folderConfiguration.onDidChange(e => this.onDidFolderConfigurationChange())); this.onDidFolderConfigurationChange(); }); } else { - this.folderConfiguration = this._register(this.createFileServiceBasedConfiguration(fileService, uriIdentityService)); + this.folderConfiguration = this._register(this.createFileServiceBasedConfiguration(fileService, uriIdentityService, logService)); this._register(this.folderConfiguration.onDidChange(e => this.onDidFolderConfigurationChange())); } } @@ -727,10 +735,10 @@ export class FolderConfiguration extends Disposable { this._onDidChange.fire(); } - private createFileServiceBasedConfiguration(fileService: IFileService, uriIdentityService: IUriIdentityService) { + private createFileServiceBasedConfiguration(fileService: IFileService, uriIdentityService: IUriIdentityService, logService: ILogService) { const settingsResources = [uriIdentityService.extUri.joinPath(this.configurationFolder, `${FOLDER_SETTINGS_NAME}.json`)]; const standAloneConfigurationResources: [string, URI][] = [TASKS_CONFIGURATION_KEY, LAUNCH_CONFIGURATION_KEY].map(name => ([name, uriIdentityService.extUri.joinPath(this.configurationFolder, `${name}.json`)])); - return new FileServiceBasedConfiguration(this.configurationFolder.toString(), settingsResources, standAloneConfigurationResources, WorkbenchState.WORKSPACE === this.workbenchState ? FOLDER_SCOPES : WORKSPACE_SCOPES, fileService, uriIdentityService); + return new FileServiceBasedConfiguration(this.configurationFolder.toString(), settingsResources, standAloneConfigurationResources, WorkbenchState.WORKSPACE === this.workbenchState ? FOLDER_SCOPES : WORKSPACE_SCOPES, fileService, uriIdentityService, logService); } private updateCache(): Promise { diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 5577ffbb2b0..a21ce8f01ec 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -12,12 +12,12 @@ import { Queue, Barrier, runWhenIdle, Promises } from 'vs/base/common/async'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { IWorkspaceContextService, Workspace as BaseWorkspace, WorkbenchState, IWorkspaceFolder, IWorkspaceFoldersChangeEvent, WorkspaceFolder, toWorkspaceFolder, isWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ConfigurationModel, DefaultConfigurationModel, ConfigurationChangeEvent, AllKeysConfigurationChangeEvent, mergeChanges } from 'vs/platform/configuration/common/configurationModels'; -import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, keyFromOverrideIdentifier, isConfigurationOverrides, IConfigurationData, IConfigurationValue, IConfigurationChange } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, keyFromOverrideIdentifier, isConfigurationOverrides, IConfigurationData, IConfigurationValue, IConfigurationChange, ConfigurationTargetToString } from 'vs/platform/configuration/common/configuration'; import { Configuration } from 'vs/workbench/services/configuration/common/configurationModels'; 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'; @@ -104,7 +104,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat this.logService = logService; this._configuration = new Configuration(this.defaultConfiguration, new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap(), this.workspace); this.cachedFolderConfigs = new ResourceMap(); - this.localUserConfiguration = this._register(new UserConfiguration(environmentService.settingsResource, remoteAuthority ? LOCAL_MACHINE_SCOPES : undefined, fileService, uriIdentityService)); + this.localUserConfiguration = this._register(new UserConfiguration(environmentService.settingsResource, remoteAuthority ? LOCAL_MACHINE_SCOPES : undefined, fileService, uriIdentityService, logService)); this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration))); if (remoteAuthority) { const remoteUserConfiguration = this.remoteUserConfiguration = this._register(new RemoteUserConfiguration(remoteAuthority, configurationCache, fileService, uriIdentityService, remoteAgentService)); @@ -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); } @@ -693,7 +693,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat return Promise.all([...folders.map(folder => { let folderConfiguration = this.cachedFolderConfigs.get(folder.uri); if (!folderConfiguration) { - folderConfiguration = new FolderConfiguration(folder, FOLDER_CONFIG_FOLDER_NAME, this.getWorkbenchState(), this.fileService, this.uriIdentityService, this.configurationCache); + folderConfiguration = new FolderConfiguration(folder, FOLDER_CONFIG_FOLDER_NAME, this.getWorkbenchState(), this.fileService, this.uriIdentityService, this.logService, this.configurationCache); this._register(folderConfiguration.onDidChange(() => this.onWorkspaceFolderConfigurationChanged(folder))); this.cachedFolderConfigs.set(folder.uri, this._register(folderConfiguration)); } @@ -796,6 +796,9 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat private triggerConfigurationChange(change: IConfigurationChange, previous: { data: IConfigurationData, workspace?: Workspace } | undefined, target: ConfigurationTarget): void { if (change.keys.length) { + if (target !== ConfigurationTarget.DEFAULT) { + this.logService.debug(`Configuration keys changed in ${ConfigurationTargetToString(target)} target`, ...change.keys); + } const configurationChangeEvent = new ConfigurationChangeEvent(change, previous, this._configuration, this.workspace); configurationChangeEvent.source = target; configurationChangeEvent.sourceConfig = this.getTargetConfiguration(target); 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/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index b99e476e2df..2d1a99a455c 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IResourceEditorInput, ITextEditorOptions, IEditorOptions, EditorActivation } from 'vs/platform/editor/common/editor'; import { SideBySideEditor, IEditorInput, IEditorPane, GroupIdentifier, IFileEditorInput, IUntitledTextResourceEditorInput, IResourceDiffEditorInput, IEditorInputFactoryRegistry, Extensions as EditorExtensions, EditorInput, SideBySideEditorInput, IEditorInputWithOptions, isEditorInputWithOptions, EditorOptions, TextEditorOptions, IEditorIdentifier, IEditorCloseEvent, ITextEditorPane, ITextDiffEditorPane, IRevertOptions, SaveReason, EditorsOrder, isTextEditorPane, IWorkbenchEditorConfiguration, EditorResourceAccessor, IVisibleEditorPane } from 'vs/workbench/common/editor'; @@ -1182,7 +1182,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { infos.forEach(info => { enumValues.push(info.id); - enumDescriptions.push(nls.localize('editorAssociations.viewType.sourceDescription', "Source: {0}", info.providerDisplayName)); + enumDescriptions.push(localize('editorAssociations.viewType.sourceDescription', "Source: {0}", info.providerDisplayName)); }); updateViewTypeSchema(enumValues, enumDescriptions); 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..3e98f0e75c1 100644 --- a/src/vs/workbench/services/gettingStarted/common/gettingStartedContent.ts +++ b/src/vs/workbench/services/gettingStarted/common/gettingStartedContent.ts @@ -12,6 +12,7 @@ import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; const setupIcon = registerIcon('getting-started-setup', Codicon.heart, localize('getting-started-setup-icon', "Icon used for the setup category of getting started")); const beginnerIcon = registerIcon('getting-started-beginner', Codicon.lightbulb, localize('getting-started-beginner-icon', "Icon used for the beginner category of getting started")); const codespacesIcon = registerIcon('getting-started-codespaces', Codicon.github, localize('getting-started-codespaces-icon', "Icon used for the codespaces category of getting started")); +const extensionsIcon = registerIcon('getting-started-extensions', Codicon.extensions, localize('getting-started-extensions-icon', "Icon used for the extensions category of getting started")); type GettingStartedItem = { @@ -40,6 +41,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"), @@ -249,6 +270,16 @@ export const content: GettingStartedContent = [ } ] } - } + }, + { + id: 'ExtensionContrib', + title: localize('gettingStarted.extensionContrib.title', "Discover Your Extensions"), + icon: extensionsIcon, + description: localize('gettingStarted.extensionContrib.description', "Learn about features contributed by installed extensions."), + content: { + type: 'items', + items: [] + } + } ]; diff --git a/src/vs/workbench/services/gettingStarted/common/gettingStartedRegistry.ts b/src/vs/workbench/services/gettingStarted/common/gettingStartedRegistry.ts index c6f32091b3e..f77f204417e 100644 --- a/src/vs/workbench/services/gettingStarted/common/gettingStartedRegistry.ts +++ b/src/vs/workbench/services/gettingStarted/common/gettingStartedRegistry.ts @@ -7,9 +7,11 @@ import { Emitter, Event } from 'vs/base/common/event'; import { FileAccess } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { ContextKeyExpr, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; +import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { content } from 'vs/workbench/services/gettingStarted/common/gettingStartedContent'; +import { localize } from 'vs/nls'; export const enum GettingStartedCategory { Beginner = 'Beginner', @@ -165,3 +167,96 @@ content.forEach(category => { Registry.add(GettingStartedRegistryID, registryImpl); export const GettingStartedRegistry: IGettingStartedRegistry = Registry.as(GettingStartedRegistryID); + +ExtensionsRegistry.registerExtensionPoint({ + extensionPoint: 'gettingStarted', + jsonSchema: { + doNotSuggest: true, + description: localize('gettingStarted', "Contribute items to help users in getting started with your extension. Rendering and progression through these items is managed by core. Experimental, requires proposedApi."), + type: 'array', + items: { + type: 'object', + required: ['id', 'title', 'description', 'button', 'media'], + properties: { + id: { + type: 'string', + description: localize('gettingStarted.id', "Unique identifier for this item."), + }, + title: { + type: 'string', + description: localize('gettingStarted.title', "Title of item.") + }, + description: { + type: 'string', + description: localize('gettingStarted.description', "Description of item.") + }, + button: { + description: localize('gettingStarted.button', "The item's button, which can either link to an external resource or run a command"), + oneOf: [ + { + type: 'object', + required: ['title', 'command'], + properties: { + title: { + type: 'string', + description: localize('gettingStarted.button.title', "Title of button.") + }, + command: { + type: 'string', + description: localize('gettingStarted.button.command', "Command to run when button is clicked. Running this command will mark the item completed.") + } + } + }, + { + type: 'object', + required: ['title', 'link'], + properties: { + title: { + type: 'string', + description: localize('gettingStarted.button.title', "Title of button.") + }, + link: { + type: 'string', + description: localize('gettingStarted.button.link', "Link to open when button is clicked. Opening this link will mark the item completed.") + } + } + } + ] + }, + media: { + type: 'object', + required: ['path', 'altText'], + description: localize('gettingStarted.media', "Image to show alongside this item."), + properties: { + path: { + description: localize('gettingStarted.media.path', "Either a single string path to an image to be used on all color themes, or separate paths for light, dark, and high contrast themes."), + + oneOf: [ + { + type: 'string', + }, + { + type: 'object', + required: ['hc', 'light', 'dark'], + properties: { + hc: { type: 'string' }, + light: { type: 'string' }, + dark: { type: 'string' }, + } + }, + ] + }, + altText: { + type: 'string', + description: localize('gettingStarted.media.altText', "Alternate text to display when the image cannot be loaded or in screen readers.") + } + } + }, + when: { + type: 'string', + description: localize('gettingStarted.when', "Context key expression to control the visibility of this getting started item.") + } + } + } + } +}); diff --git a/src/vs/workbench/services/gettingStarted/common/gettingStartedService.ts b/src/vs/workbench/services/gettingStarted/common/gettingStartedService.ts index b5784ee9ddb..8377ec8f9bf 100644 --- a/src/vs/workbench/services/gettingStarted/common/gettingStartedService.ts +++ b/src/vs/workbench/services/gettingStarted/common/gettingStartedService.ts @@ -11,9 +11,14 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag import { Memento } from 'vs/workbench/common/memento'; import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { Disposable } from 'vs/base/common/lifecycle'; import { IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { URI } from 'vs/base/common/uri'; +import { joinPath } from 'vs/base/common/resources'; +import { FileAccess } from 'vs/base/common/network'; export const IGettingStartedService = createDecorator('gettingStartedService'); @@ -63,11 +68,14 @@ export class GettingStartedService extends Disposable implements IGettingStarted private commandListeners = new Map(); private eventListeners = new Map(); + private trackedExtensions = new Set(); + constructor( @IStorageService private readonly storageService: IStorageService, @ICommandService private readonly commandService: ICommandService, @IContextKeyService private readonly contextService: IContextKeyService, @IUserDataAutoSyncEnablementService readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService, + @IExtensionService private readonly extensionService: IExtensionService, ) { super(); @@ -80,7 +88,20 @@ export class GettingStartedService extends Disposable implements IGettingStarted } }); - this._register(this.registry.onDidAddCategory(category => this._onDidAddCategory.fire(this.getCategoryProgress(category)))); + this.extensionService.getExtensions().then(extensions => { + extensions.forEach(extension => this.registerExtensionContributions(extension)); + }); + + this.extensionService.onDidChangeExtensions(() => { + this.extensionService.getExtensions().then(extensions => { + extensions.forEach(extension => this.registerExtensionContributions(extension)); + }); + }); + + this._register(this.registry.onDidAddCategory(category => + this._onDidAddCategory.fire(this.getCategoryProgress(category)) + )); + this._register(this.registry.onDidAddTask(task => { this.registerDoneListeners(task); this._onDidAddTask.fire(this.getTaskProgress(task)); @@ -93,6 +114,50 @@ export class GettingStartedService extends Disposable implements IGettingStarted })); } + private registerExtensionContributions(extension: IExtensionDescription) { + const convertPaths = (path: string | { hc: string, dark: string, light: string }): { hc: URI, dark: URI, light: URI } => { + const convertPath = (path: string) => path.startsWith('https://') + ? URI.parse(path, true) + : FileAccess.asBrowserUri(joinPath(extension.extensionLocation, path)); + + if (typeof path === 'string') { + const converted = convertPath(path); + return { hc: converted, dark: converted, light: converted }; + } else { + return { + hc: convertPath(path.hc), + light: convertPath(path.light), + dark: convertPath(path.dark) + }; + } + }; + + if (!this.trackedExtensions.has(ExtensionIdentifier.toKey(extension.identifier))) { + this.trackedExtensions.add(ExtensionIdentifier.toKey(extension.identifier)); + + if (extension.contributes?.gettingStarted?.length) { + if (!extension.enableProposedApi) { + console.warn('Extension', extension.identifier.value, 'contributes getting started content but has not enabled proposedApi. The contributed content will be disregarded.'); + return; + } + + extension.contributes?.gettingStarted.forEach((content, index) => { + this.registry.registerTask({ + button: content.button, + description: content.description, + media: { type: 'image', altText: content.media.altText, path: convertPaths(content.media.path) }, + doneOn: content.button.command ? { commandExecuted: content.button.command } : { eventFired: `linkOpened:${content.button.link}` }, + id: content.id, + title: content.title, + when: ContextKeyExpr.deserialize(content.when) ?? ContextKeyExpr.true(), + category: 'ExtensionContrib', + order: index, + }); + }); + } + } + } + private registerDoneListeners(task: IGettingStartedTask) { if (task.doneOn.commandExecuted) { const existing = this.commandListeners.get(task.doneOn.commandExecuted); 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..4ace2bc3fba 100644 --- a/src/vs/workbench/services/log/electron-sandbox/logService.ts +++ b/src/vs/workbench/services/log/electron-sandbox/logService.ts @@ -5,14 +5,13 @@ import { LogService, ConsoleLogger, MultiplexLogService, ILogger } from 'vs/platform/log/common/log'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; -import { LogLevelChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc'; +import { LogLevelChannelClient, FollowerLogService, LoggerChannelClient } from 'vs/platform/log/common/logIpc'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { LoggerService } from 'vs/workbench/services/log/electron-sandbox/loggerService'; import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services'; export class NativeLogService extends LogService { - constructor(name: string, loggerService: LoggerService, mainProcessService: IMainProcessService, environmentService: INativeWorkbenchEnvironmentService) { + constructor(name: string, loggerService: LoggerChannelClient, mainProcessService: IMainProcessService, environmentService: INativeWorkbenchEnvironmentService) { const disposables = new DisposableStore(); const loggerClient = new LogLevelChannelClient(mainProcessService.getChannel('logLevel')); @@ -37,5 +36,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 deleted file mode 100644 index 50795cdf780..00000000000 --- a/src/vs/workbench/services/log/electron-sandbox/loggerService.ts +++ /dev/null @@ -1,61 +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 } from 'vs/base/parts/ipc/common/ipc'; -import { LogLevel, ILoggerService, ILogger, AbstractMessageLogger, ILoggerOptions } from 'vs/platform/log/common/log'; -import { URI } from 'vs/base/common/uri'; -import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services'; - -export class LoggerService implements ILoggerService { - - declare readonly _serviceBrand: undefined; - private readonly channel: IChannel; - - constructor( - @IMainProcessService mainProcessService: IMainProcessService - ) { - this.channel = mainProcessService.getChannel('logger'); - } - - createConsoleMainLogger(): ILogger { - return new Logger(this.channel); - } - - createLogger(file: URI, options?: ILoggerOptions): ILogger { - return new Logger(this.channel, file, options); - } - -} - -class Logger extends AbstractMessageLogger { - - private isLoggerCreated: boolean = false; - private buffer: [LogLevel, string][] = []; - - constructor( - private readonly channel: IChannel, - private readonly file?: URI, - loggerOptions?: ILoggerOptions, - ) { - super(loggerOptions?.always); - (this.file ? this.channel.call('createLogger', [file, loggerOptions]) : this.channel.call('createConsoleMainLogger', [file, loggerOptions])) - .then(() => { - this._log(this.buffer); - this.isLoggerCreated = true; - }); - } - - protected log(level: LogLevel, message: string) { - this._log([[level, message]]); - } - - private _log(messages: [LogLevel, string][]) { - if (this.isLoggerCreated) { - this.channel.call('log', [this.file, messages]); - } else { - this.buffer.push(...messages); - } - } -} diff --git a/src/vs/workbench/services/notification/common/notificationService.ts b/src/vs/workbench/services/notification/common/notificationService.ts index 2c52dc75ffe..122f6e79b35 100644 --- a/src/vs/workbench/services/notification/common/notificationService.ts +++ b/src/vs/workbench/services/notification/common/notificationService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { INotificationService, INotification, INotificationHandle, Severity, NotificationMessage, INotificationActions, IPromptChoice, IPromptOptions, IStatusMessageOptions, NoOpNotification, NeverShowAgainScope, NotificationsFilter } from 'vs/platform/notification/common/notification'; import { NotificationsModel, ChoiceAction } from 'vs/workbench/common/notifications'; import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; @@ -75,7 +75,7 @@ export class NotificationService extends Disposable implements INotificationServ const neverShowAgainAction = toDispose.add(new Action( 'workbench.notification.neverShowAgain', - nls.localize('neverShowAgain', "Don't Show Again"), + localize('neverShowAgain', "Don't Show Again"), undefined, true, async () => { // Close notification @@ -123,7 +123,7 @@ export class NotificationService extends Disposable implements INotificationServ } const neverShowAgainChoice = { - label: nls.localize('neverShowAgain', "Don't Show Again"), + label: localize('neverShowAgain', "Don't Show Again"), run: () => this.storageService.store(id, true, scope, StorageTarget.USER), isSecondary: options.neverShowAgain.isSecondary }; diff --git a/src/vs/workbench/services/progress/test/browser/progressIndicator.test.ts b/src/vs/workbench/services/progress/test/browser/progressIndicator.test.ts index afe18caf17f..e90d4794abe 100644 --- a/src/vs/workbench/services/progress/test/browser/progressIndicator.test.ts +++ b/src/vs/workbench/services/progress/test/browser/progressIndicator.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IAction, IActionViewItem } from 'vs/base/common/actions'; +import { IAction } from 'vs/base/common/actions'; import { IEditorControl } from 'vs/workbench/common/editor'; import { CompositeScope, CompositeProgressIndicator } from 'vs/workbench/services/progress/browser/progressIndicator'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -27,7 +27,6 @@ class TestViewlet implements IViewlet { getActions(): IAction[] { return []; } getSecondaryActions(): IAction[] { return []; } getContextMenuActions(): IAction[] { return []; } - getActionViewItem(action: IAction): IActionViewItem { return null!; } getControl(): IEditorControl { return null!; } focus(): void { } getOptimalWidth(): number { return 10; } diff --git a/src/vs/workbench/services/remote/common/remoteExplorerService.ts b/src/vs/workbench/services/remote/common/remoteExplorerService.ts index 89c8acfcae0..f3420ff3d97 100644 --- a/src/vs/workbench/services/remote/common/remoteExplorerService.ts +++ b/src/vs/workbench/services/remote/common/remoteExplorerService.ts @@ -16,11 +16,17 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { IAddressProvider } from 'vs/platform/remote/common/remoteAgentConnection'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { isNumber, isObject, isString } from 'vs/base/common/types'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { hash } from 'vs/base/common/hash'; export const IRemoteExplorerService = createDecorator('remoteExplorerService'); 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', @@ -244,7 +250,7 @@ export class TunnelModel extends Disposable { // onCandidateChanged returns the removed candidates public onCandidatesChanged: Event> = this._onCandidatesChanged.event; private _candidateFilter: ((candidates: CandidatePort[]) => Promise) | undefined; - private tunnelRestoreValue: string | undefined; + private tunnelRestoreValue: Promise; private _onEnvironmentTunnelsSet: Emitter = new Emitter(); public onEnvironmentTunnelsSet: Event = this._onEnvironmentTunnelsSet.event; private _environmentTunnelsSet: boolean = false; @@ -256,10 +262,11 @@ export class TunnelModel extends Disposable { @IConfigurationService private readonly configurationService: IConfigurationService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @IRemoteAuthorityResolverService private readonly remoteAuthorityResolverService: IRemoteAuthorityResolverService, + @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService ) { super(); this.portsAttributes = new PortsAttributes(configurationService); - this.tunnelRestoreValue = this.storageService.get(TUNNELS_TO_RESTORE, StorageScope.WORKSPACE); + this.tunnelRestoreValue = this.getTunnelRestoreValue(); this.forwarded = new Map(); this.remoteTunnels = new Map(); this.tunnelService.tunnels.then(tunnels => { @@ -283,7 +290,7 @@ export class TunnelModel extends Disposable { }); this.detected = new Map(); - this._register(this.tunnelService.onTunnelOpened(tunnel => { + this._register(this.tunnelService.onTunnelOpened(async (tunnel) => { const key = makeAddress(tunnel.tunnelRemoteHost, tunnel.tunnelRemotePort); if ((!this.forwarded.has(key)) && tunnel.localAddress) { const matchingCandidate = mapHasAddressLocalhostOrAllInterfaces(this._candidates ?? new Map(), tunnel.tunnelRemoteHost, tunnel.tunnelRemotePort); @@ -299,15 +306,15 @@ export class TunnelModel extends Disposable { restore: true }); } - this.storeForwarded(); + await this.storeForwarded(); this.remoteTunnels.set(key, tunnel); this._onForwardPort.fire(this.forwarded.get(key)!); })); - this._register(this.tunnelService.onTunnelClosed(address => { + this._register(this.tunnelService.onTunnelClosed(async (address) => { const key = makeAddress(address.host, address.port); if (this.forwarded.has(key)) { this.forwarded.delete(key); - this.storeForwarded(); + await this.storeForwarded(); this._onClosePort.fire(address); } })); @@ -319,22 +326,50 @@ export class TunnelModel extends Disposable { return isPublic ? TunnelPrivacy.Public : this.tunnelService.canMakePublic ? TunnelPrivacy.Private : TunnelPrivacy.ConstantPrivate; } + private async getStorageKey(): Promise { + const workspace = this.workspaceContextService.getWorkspace(); + const workspaceHash = workspace.configuration ? hash(workspace.configuration.path) : (workspace.folders.length > 0 ? hash(workspace.folders[0].uri.path) : undefined); + return `${TUNNELS_TO_RESTORE}.${this.environmentService.remoteAuthority}.${workspaceHash}`; + } + + private async getTunnelRestoreValue(): Promise { + const deprecatedValue = this.storageService.get(TUNNELS_TO_RESTORE, StorageScope.WORKSPACE); + if (deprecatedValue) { + this.storageService.remove(TUNNELS_TO_RESTORE, StorageScope.WORKSPACE); + await this.storeForwarded(); + return deprecatedValue; + } + + return this.storageService.get(await this.getStorageKey(), StorageScope.GLOBAL); + } + async restoreForwarded() { if (this.configurationService.getValue('remote.restoreForwardedPorts')) { - if (this.tunnelRestoreValue) { - const tunnels = JSON.parse(this.tunnelRestoreValue) ?? []; + const tunnelRestoreValue = await this.tunnelRestoreValue; + if (tunnelRestoreValue) { + const tunnels = JSON.parse(tunnelRestoreValue) ?? []; for (let tunnel of tunnels) { if (!mapHasAddressLocalhostOrAllInterfaces(this.detected, tunnel.remoteHost, tunnel.remotePort)) { await this.forward({ host: tunnel.remoteHost, port: tunnel.remotePort }, tunnel.localPort, tunnel.name, undefined, undefined, tunnel.privacy === TunnelPrivacy.Public); } } + } else { + // It's possible that at restore time the value hasn't synced. + const key = await this.getStorageKey(); + const listener = this.storageService.onDidChangeValue(async (e) => { + if (e.key === key) { + listener.dispose(); + this.tunnelRestoreValue = Promise.resolve(this.storageService.get(await this.getStorageKey(), StorageScope.GLOBAL)); + await this.restoreForwarded(); + } + }); } } } - private storeForwarded() { + private async storeForwarded() { if (this.configurationService.getValue('remote.restoreForwardedPorts')) { - this.storageService.store(TUNNELS_TO_RESTORE, JSON.stringify(Array.from(this.forwarded.values()).filter(value => value.restore)), StorageScope.WORKSPACE, StorageTarget.USER); + this.storageService.store(await this.getStorageKey(), JSON.stringify(Array.from(this.forwarded.values()).filter(value => value.restore)), StorageScope.GLOBAL, StorageTarget.USER); } } @@ -367,7 +402,7 @@ export class TunnelModel extends Disposable { const key = makeAddress(remote.host, remote.port); this.forwarded.set(key, newForward); this.remoteTunnels.set(key, tunnel); - this.storeForwarded(); + await this.storeForwarded(); this._onForwardPort.fire(newForward); return tunnel; } @@ -380,12 +415,12 @@ export class TunnelModel extends Disposable { } } - name(host: string, port: number, name: string) { + async name(host: string, port: number, name: string) { const existingForwarded = mapHasAddressLocalhostOrAllInterfaces(this.forwarded, host, port); const key = makeAddress(host, port); if (existingForwarded) { existingForwarded.name = name; - this.storeForwarded(); + await this.storeForwarded(); this._onPortName.fire({ host, port }); return; } else if (this.detected.has(key)) { @@ -492,12 +527,12 @@ export class TunnelModel extends Disposable { return this._candidates ? this.candidates : undefined; } - private updateAttributes() { + private async updateAttributes() { // If the label changes in the attributes, we should update it. for (let forwarded of this.forwarded.values()) { const attributes = this.portsAttributes.getAttributes(forwarded.remotePort); if (attributes && attributes.label && attributes.label !== forwarded.name) { - this.name(forwarded.remoteHost, forwarded.remotePort, attributes.label); + await this.name(forwarded.remoteHost, forwarded.remotePort, attributes.label); } } } @@ -550,8 +585,9 @@ class RemoteExplorerService implements IRemoteExplorerService { @IConfigurationService configurationService: IConfigurationService, @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, @IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService, + @IWorkspaceContextService workspaceContextService: IWorkspaceContextService ) { - this._tunnelModel = new TunnelModel(tunnelService, storageService, configurationService, environmentService, remoteAuthorityResolverService); + this._tunnelModel = new TunnelModel(tunnelService, storageService, configurationService, environmentService, remoteAuthorityResolverService, workspaceContextService); } set targetType(name: string[]) { 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/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index 8e31df89bb1..9fe5b186c31 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import { ITextFileService, ITextFileStreamContent, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult, ITextFileSaveOptions, ITextFileEditorModelManager, IResourceEncoding, stringToSnapshot, ITextFileSaveAsOptions } from 'vs/workbench/services/textfile/common/textfiles'; import { IRevertOptions, IEncodingSupport } from 'vs/workbench/common/editor'; @@ -143,7 +143,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex // validate binary if (options?.acceptTextOnly && decoder.detected.seemsBinary) { - throw new TextFileOperationError(nls.localize('fileBinaryError', "File seems to be binary and cannot be opened as text"), TextFileOperationResult.FILE_IS_BINARY, options); + throw new TextFileOperationError(localize('fileBinaryError', "File seems to be binary and cannot be opened as text"), TextFileOperationResult.FILE_IS_BINARY, options); } return [bufferStream, decoder]; @@ -414,9 +414,9 @@ export abstract class AbstractTextFileService extends Disposable implements ITex private async confirmOverwrite(resource: URI): Promise { const confirm: IConfirmation = { - message: nls.localize('confirmOverwrite', "'{0}' already exists. Do you want to replace it?", basename(resource)), - detail: nls.localize('irreversible', "A file or folder with the name '{0}' already exists in the folder '{1}'. Replacing it will overwrite its current contents.", basename(resource), basename(dirname(resource))), - primaryButton: nls.localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace"), + message: localize('confirmOverwrite', "'{0}' already exists. Do you want to replace it?", basename(resource)), + detail: localize('irreversible', "A file or folder with the name '{0}' already exists in the folder '{1}'. Replacing it will overwrite its current contents.", basename(resource), basename(dirname(resource))), + primaryButton: localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace"), type: 'warning' }; diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index a6f0de819d3..cfde3de4dad 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { assertIsDefined, withNullAsUndefined } from 'vs/base/common/types'; @@ -987,7 +987,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Decode: Load with encoding else { if (this.isDirty()) { - this.notificationService.info(nls.localize('saveFileFirst', "The file is dirty. Please save it first before reopening it with another encoding.")); + this.notificationService.info(localize('saveFileFirst', "The file is dirty. Please save it first before reopening it with another encoding.")); return; } 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/userData/browser/userDataInit.ts b/src/vs/workbench/services/userData/browser/userDataInit.ts index 380f47eafde..c001c65863c 100644 --- a/src/vs/workbench/services/userData/browser/userDataInit.ts +++ b/src/vs/workbench/services/userData/browser/userDataInit.ts @@ -5,7 +5,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ExtensionsInitializer } from 'vs/platform/userDataSync/common/extensionsSync'; -import { GlobalStateInitializer } from 'vs/platform/userDataSync/common/globalStateSync'; +import { GlobalStateInitializer, UserDataSyncStoreTypeSynchronizer } from 'vs/platform/userDataSync/common/globalStateSync'; import { KeybindingsInitializer } from 'vs/platform/userDataSync/common/keybindingsSync'; import { SettingsInitializer } from 'vs/platform/userDataSync/common/settingsSync'; import { SnippetsInitializer } from 'vs/platform/userDataSync/common/snippetsSync'; @@ -16,8 +16,8 @@ import { ILogService } from 'vs/platform/log/common/log'; import { UserDataSyncStoreClient } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; import { IProductService } from 'vs/platform/product/common/productService'; import { IRequestService } from 'vs/platform/request/common/request'; -import { ISyncExtension, IUserDataInitializer, IUserDataSyncLogService, IUserDataSyncStoreClient, IUserDataSyncStoreManagementService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; -import { getCurrentAuthenticationSessionInfo } from 'vs/workbench/services/authentication/browser/authenticationService'; +import { ISyncExtension, IUserData, IUserDataInitializer, IUserDataSyncLogService, IUserDataSyncStoreClient, IUserDataSyncStoreManagementService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; +import { AuthenticationSessionInfo, getCurrentAuthenticationSessionInfo } from 'vs/workbench/services/authentication/browser/authenticationService'; import { getSyncAreaLabel } from 'vs/workbench/services/userDataSync/common/userDataSync'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -30,6 +30,8 @@ import { IExtensionService, toExtensionDescription } from 'vs/workbench/services import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { mark } from 'vs/base/common/performance'; import { IIgnoredExtensionsManagementService } from 'vs/platform/userDataSync/common/ignoredExtensions'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { isEqual } from 'vs/base/common/resources'; export const IUserDataInitializationService = createDecorator('IUserDataInitializationService'); export interface IUserDataInitializationService { @@ -47,6 +49,7 @@ export class UserDataInitializationService implements IUserDataInitializationSer private readonly initialized: SyncResource[] = []; private readonly initializationFinished = new Barrier(); + private globalStateUserData: IUserData | null = null; constructor( @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @@ -88,12 +91,6 @@ export class UserDataInitializationService implements IUserDataInitializationSer return; } - const userDataSyncStore = this.userDataSyncStoreManagementService.userDataSyncStore; - if (!userDataSyncStore) { - this.logService.trace(`Skipping initializing user data as sync service is not provided`); - return; - } - if (!this.environmentService.options?.credentialsProvider) { this.logService.trace(`Skipping initializing user data as credentials provider is not provided`); return; @@ -110,6 +107,15 @@ export class UserDataInitializationService implements IUserDataInitializationSer return; } + await this.initializeUserDataSyncStore(authenticationSession); + + const userDataSyncStore = this.userDataSyncStoreManagementService.userDataSyncStore; + if (!userDataSyncStore) { + this.logService.trace(`Skipping initializing user data as sync service is not provided`); + return; + } + + this.logService.info(`Using settings sync service ${userDataSyncStore.url.toString()} for initialization`); const userDataSyncStoreClient = new UserDataSyncStoreClient(userDataSyncStore.url, this.productService, this.requestService, this.logService, this.environmentService, this.fileService, this.storageService); userDataSyncStoreClient.setAuthToken(authenticationSession.accessToken, authenticationSession.providerId); return userDataSyncStoreClient; @@ -119,6 +125,37 @@ export class UserDataInitializationService implements IUserDataInitializationSer return this._userDataSyncStoreClientPromise; } + private async initializeUserDataSyncStore(authenticationSession: AuthenticationSessionInfo): Promise { + const userDataSyncStore = this.userDataSyncStoreManagementService.userDataSyncStore; + if (!userDataSyncStore?.canSwitch) { + return; + } + + const disposables = new DisposableStore(); + try { + const userDataSyncStoreClient = disposables.add(new UserDataSyncStoreClient(userDataSyncStore.url, this.productService, this.requestService, this.logService, this.environmentService, this.fileService, this.storageService)); + userDataSyncStoreClient.setAuthToken(authenticationSession.accessToken, authenticationSession.providerId); + + // Cache global state data for global state initialization + this.globalStateUserData = await userDataSyncStoreClient.read(SyncResource.GlobalState, null); + + if (this.globalStateUserData) { + const userDataSyncStoreType = new UserDataSyncStoreTypeSynchronizer(userDataSyncStoreClient, this.storageService, this.environmentService, this.fileService, this.logService).getSyncStoreType(this.globalStateUserData); + if (userDataSyncStoreType) { + await this.userDataSyncStoreManagementService.switch(userDataSyncStoreType); + + // Unset cached global state data if urls are changed + if (!isEqual(userDataSyncStore.url, this.userDataSyncStoreManagementService.userDataSyncStore?.url)) { + this.logService.info('Switched settings sync store'); + this.globalStateUserData = null; + } + } + } + } finally { + disposables.dispose(); + } + } + async whenInitializationFinished(): Promise { await this.initializationFinished.wait(); } @@ -158,7 +195,7 @@ export class UserDataInitializationService implements IUserDataInitializationSer this.initialized.push(syncResource); this.logService.trace(`Initializing ${getSyncAreaLabel(syncResource)}`); const initializer = this.createSyncResourceInitializer(syncResource, instantiationService); - const userData = await userDataSyncStoreClient.read(syncResource, null); + const userData = await userDataSyncStoreClient.read(syncResource, syncResource === SyncResource.GlobalState ? this.globalStateUserData : null); await initializer.initialize(userData); this.logService.info(`Initialized ${getSyncAreaLabel(syncResource)}`); } catch (error) { diff --git a/src/vs/workbench/services/userDataSync/browser/userDataAutoSyncEnablementService.ts b/src/vs/workbench/services/userDataSync/browser/userDataAutoSyncEnablementService.ts index af14b952ca6..41540b10fc8 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataAutoSyncEnablementService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataAutoSyncEnablementService.ts @@ -29,13 +29,14 @@ export class WebUserDataAutoSyncEnablementService extends UserDataAutoSyncEnable } setEnablement(enabled: boolean) { - if (this.canToggleEnablement()) { - if (this.enabled !== enabled) { - this.enabled = enabled; - super.setEnablement(enabled); - if (this.workbenchEnvironmentService.options?.settingsSyncOptions?.enablementHandler) { - this.workbenchEnvironmentService.options.settingsSyncOptions.enablementHandler(this.enabled); - } + if (enabled && !this.canToggleEnablement()) { + return; + } + if (this.enabled !== enabled) { + this.enabled = enabled; + super.setEnablement(enabled); + if (this.workbenchEnvironmentService.options?.settingsSyncOptions?.enablementHandler) { + this.workbenchEnvironmentService.options.settingsSyncOptions.enablementHandler(this.enabled); } } } diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts index b066db65e06..48602a7f767 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts @@ -31,6 +31,9 @@ import { URI } from 'vs/base/common/uri'; import { IViewsService, ViewContainerLocation, IViewDescriptorService } from 'vs/workbench/common/views'; import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { isWeb } from 'vs/base/common/platform'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { UserDataSyncStoreClient } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; +import { UserDataSyncStoreTypeSynchronizer } from 'vs/platform/userDataSync/common/globalStateSync'; type UserAccountClassification = { id: { classification: 'EndUserPseudonymizedInformation', purpose: 'BusinessInsight' }; @@ -109,6 +112,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, @IUserDataSyncStoreManagementService private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService, @ILifecycleService private readonly lifecycleService: ILifecycleService, + @IInstantiationService private readonly instantiationService: IInstantiationService, ) { super(); this.syncEnablementContext = CONTEXT_SYNC_ENABLEMENT.bindTo(contextKeyService); @@ -280,6 +284,11 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat } await this.userDataAutoSyncService.turnOn(); + + if (this.userDataSyncStoreManagementService.userDataSyncStore?.canSwitch) { + await this.synchroniseUserDataSyncStoreType(); + } + this.notificationService.info(localize('sync turned on', "{0} is turned on", title)); } @@ -287,6 +296,25 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat return this.userDataAutoSyncService.turnOff(everywhere); } + async synchroniseUserDataSyncStoreType(): Promise { + if (!this.userDataSyncAccountService.account) { + throw new Error('Cannot update because you are signed out from settings sync. Please sign in and try again.'); + } + if (!isWeb || !this.userDataSyncStoreManagementService.userDataSyncStore) { + // Not supported + return; + } + + const userDataSyncStoreUrl = this.userDataSyncStoreManagementService.userDataSyncStore.type === 'insiders' ? this.userDataSyncStoreManagementService.userDataSyncStore.stableUrl : this.userDataSyncStoreManagementService.userDataSyncStore.insidersUrl; + const userDataSyncStoreClient = this.instantiationService.createInstance(UserDataSyncStoreClient, userDataSyncStoreUrl); + userDataSyncStoreClient.setAuthToken(this.userDataSyncAccountService.account.token, this.userDataSyncAccountService.account.authenticationProviderId); + await this.instantiationService.createInstance(UserDataSyncStoreTypeSynchronizer, userDataSyncStoreClient).sync(this.userDataSyncStoreManagementService.userDataSyncStore.type); + } + + syncNow(): Promise { + return this.userDataAutoSyncService.triggerSync(['Sync Now'], false, true); + } + private async onBeforeShutdown(manualSyncTask: IManualSyncTask): Promise { const result = await this.dialogService.confirm({ type: 'warning', @@ -471,7 +499,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat } let sessionId: string, accountName: string, accountId: string; if (isAuthenticationProvider(result)) { - const session = await this.authenticationService.login(result.id, result.scopes); + const session = await this.authenticationService.createSession(result.id, result.scopes); sessionId = session.id; accountName = session.account.label; accountId = session.account.id; diff --git a/src/vs/workbench/services/userDataSync/common/userDataSync.ts b/src/vs/workbench/services/userDataSync/common/userDataSync.ts index e46b1531589..c40f3fa8966 100644 --- a/src/vs/workbench/services/userDataSync/common/userDataSync.ts +++ b/src/vs/workbench/services/userDataSync/common/userDataSync.ts @@ -63,6 +63,9 @@ export interface IUserDataSyncWorkbenchService { resetSyncedData(): Promise; showSyncActivity(): Promise; + syncNow(): Promise; + + synchroniseUserDataSyncStoreType(): Promise; } export function getSyncAreaLabel(source: SyncResource): string { diff --git a/src/vs/workbench/services/userDataSync/electron-sandbox/userDataSyncService.ts b/src/vs/workbench/services/userDataSync/electron-sandbox/userDataSyncService.ts index 8e4493669f3..59ca1f122e8 100644 --- a/src/vs/workbench/services/userDataSync/electron-sandbox/userDataSyncService.ts +++ b/src/vs/workbench/services/userDataSync/electron-sandbox/userDataSyncService.ts @@ -3,256 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { SyncStatus, SyncResource, IUserDataSyncService, UserDataSyncError, ISyncResourceHandle, ISyncTask, IManualSyncTask, IUserDataManifest, ISyncResourcePreview, IResourcePreview } from 'vs/platform/userDataSync/common/userDataSync'; -import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { Emitter, Event } from 'vs/base/common/event'; -import { IChannel } from 'vs/base/parts/ipc/common/ipc'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { URI } from 'vs/base/common/uri'; +import { IUserDataSyncService } from 'vs/platform/userDataSync/common/userDataSync'; +import { registerSharedProcessRemoteService } from 'vs/platform/ipc/electron-sandbox/services'; +import { UserDataSyncChannelClient } from 'vs/platform/userDataSync/common/userDataSyncServiceIpc'; -export class UserDataSyncService extends Disposable implements IUserDataSyncService { - - declare readonly _serviceBrand: undefined; - - private readonly channel: IChannel; - - private _status: SyncStatus = SyncStatus.Uninitialized; - get status(): SyncStatus { return this._status; } - private _onDidChangeStatus: Emitter = this._register(new Emitter()); - readonly onDidChangeStatus: Event = this._onDidChangeStatus.event; - - get onDidChangeLocal(): Event { return this.channel.listen('onDidChangeLocal'); } - - private _conflicts: [SyncResource, IResourcePreview[]][] = []; - get conflicts(): [SyncResource, IResourcePreview[]][] { return this._conflicts; } - private _onDidChangeConflicts: Emitter<[SyncResource, IResourcePreview[]][]> = this._register(new Emitter<[SyncResource, IResourcePreview[]][]>()); - readonly onDidChangeConflicts: Event<[SyncResource, IResourcePreview[]][]> = this._onDidChangeConflicts.event; - - private _lastSyncTime: number | undefined = undefined; - get lastSyncTime(): number | undefined { return this._lastSyncTime; } - private _onDidChangeLastSyncTime: Emitter = this._register(new Emitter()); - readonly onDidChangeLastSyncTime: Event = this._onDidChangeLastSyncTime.event; - - private _onSyncErrors: Emitter<[SyncResource, UserDataSyncError][]> = this._register(new Emitter<[SyncResource, UserDataSyncError][]>()); - readonly onSyncErrors: Event<[SyncResource, UserDataSyncError][]> = this._onSyncErrors.event; - - get onDidResetLocal(): Event { return this.channel.listen('onDidResetLocal'); } - get onDidResetRemote(): Event { return this.channel.listen('onDidResetRemote'); } - - constructor( - @ISharedProcessService private readonly sharedProcessService: ISharedProcessService - ) { - super(); - const userDataSyncChannel = sharedProcessService.getChannel('userDataSync'); - this.channel = { - call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise { - return userDataSyncChannel.call(command, arg, cancellationToken) - .then(null, error => { throw UserDataSyncError.toUserDataSyncError(error); }); - }, - listen(event: string, arg?: any): Event { - return userDataSyncChannel.listen(event, arg); - } - }; - this.channel.call<[SyncStatus, [SyncResource, IResourcePreview[]][], number | undefined]>('_getInitialData').then(([status, conflicts, lastSyncTime]) => { - this.updateStatus(status); - this.updateConflicts(conflicts); - if (lastSyncTime) { - this.updateLastSyncTime(lastSyncTime); - } - this._register(this.channel.listen('onDidChangeStatus')(status => this.updateStatus(status))); - this._register(this.channel.listen('onDidChangeLastSyncTime')(lastSyncTime => this.updateLastSyncTime(lastSyncTime))); - }); - this._register(this.channel.listen<[SyncResource, IResourcePreview[]][]>('onDidChangeConflicts')(conflicts => this.updateConflicts(conflicts))); - this._register(this.channel.listen<[SyncResource, Error][]>('onSyncErrors')(errors => this._onSyncErrors.fire(errors.map(([source, error]) => ([source, UserDataSyncError.toUserDataSyncError(error)]))))); - } - - createSyncTask(): Promise { - throw new Error('not supported'); - } - - async createManualSyncTask(): Promise { - const { id, manifest, status } = await this.channel.call<{ id: string, manifest: IUserDataManifest | null, status: SyncStatus }>('createManualSyncTask'); - return new ManualSyncTask(id, manifest, status, this.sharedProcessService); - } - - replace(uri: URI): Promise { - return this.channel.call('replace', [uri]); - } - - reset(): Promise { - return this.channel.call('reset'); - } - - resetRemote(): Promise { - return this.channel.call('resetRemote'); - } - - resetLocal(): Promise { - return this.channel.call('resetLocal'); - } - - hasPreviouslySynced(): Promise { - return this.channel.call('hasPreviouslySynced'); - } - - hasLocalData(): Promise { - return this.channel.call('hasLocalData'); - } - - accept(syncResource: SyncResource, resource: URI, content: string | null, apply: boolean): Promise { - return this.channel.call('accept', [syncResource, resource, content, apply]); - } - - resolveContent(resource: URI): Promise { - return this.channel.call('resolveContent', [resource]); - } - - async getLocalSyncResourceHandles(resource: SyncResource): Promise { - const handles = await this.channel.call('getLocalSyncResourceHandles', [resource]); - return handles.map(({ created, uri }) => ({ created, uri: URI.revive(uri) })); - } - - async getRemoteSyncResourceHandles(resource: SyncResource): Promise { - const handles = await this.channel.call('getRemoteSyncResourceHandles', [resource]); - return handles.map(({ created, uri }) => ({ created, uri: URI.revive(uri) })); - } - - async getAssociatedResources(resource: SyncResource, syncResourceHandle: ISyncResourceHandle): Promise<{ resource: URI, comparableResource: URI }[]> { - const result = await this.channel.call<{ resource: URI, comparableResource: URI }[]>('getAssociatedResources', [resource, syncResourceHandle]); - return result.map(({ resource, comparableResource }) => ({ resource: URI.revive(resource), comparableResource: URI.revive(comparableResource) })); - } - - async getMachineId(resource: SyncResource, syncResourceHandle: ISyncResourceHandle): Promise { - return this.channel.call('getMachineId', [resource, syncResourceHandle]); - } - - private async updateStatus(status: SyncStatus): Promise { - this._status = status; - this._onDidChangeStatus.fire(status); - } - - private async updateConflicts(conflicts: [SyncResource, IResourcePreview[]][]): Promise { - // Revive URIs - this._conflicts = conflicts.map(([syncResource, conflicts]) => - ([ - syncResource, - conflicts.map(r => - ({ - ...r, - localResource: URI.revive(r.localResource), - remoteResource: URI.revive(r.remoteResource), - previewResource: URI.revive(r.previewResource), - })) - ])); - this._onDidChangeConflicts.fire(this._conflicts); - } - - private updateLastSyncTime(lastSyncTime: number): void { - if (this._lastSyncTime !== lastSyncTime) { - this._lastSyncTime = lastSyncTime; - this._onDidChangeLastSyncTime.fire(lastSyncTime); - } - } -} - -class ManualSyncTask implements IManualSyncTask { - - private readonly channel: IChannel; - - get onSynchronizeResources(): Event<[SyncResource, URI[]][]> { return this.channel.listen<[SyncResource, URI[]][]>('onSynchronizeResources'); } - - private _status: SyncStatus; - get status(): SyncStatus { return this._status; } - - constructor( - readonly id: string, - readonly manifest: IUserDataManifest | null, - status: SyncStatus, - sharedProcessService: ISharedProcessService, - ) { - const manualSyncTaskChannel = sharedProcessService.getChannel(`manualSyncTask-${id}`); - this._status = status; - const that = this; - this.channel = { - async call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise { - try { - const result = await manualSyncTaskChannel.call(command, arg, cancellationToken); - that._status = await manualSyncTaskChannel.call('_getStatus'); - return result; - } catch (error) { - throw UserDataSyncError.toUserDataSyncError(error); - } - }, - listen(event: string, arg?: any): Event { - return manualSyncTaskChannel.listen(event, arg); - } - }; - } - - async preview(): Promise<[SyncResource, ISyncResourcePreview][]> { - const previews = await this.channel.call<[SyncResource, ISyncResourcePreview][]>('preview'); - return this.deserializePreviews(previews); - } - - async accept(resource: URI, content?: string | null): Promise<[SyncResource, ISyncResourcePreview][]> { - const previews = await this.channel.call<[SyncResource, ISyncResourcePreview][]>('accept', [resource, content]); - return this.deserializePreviews(previews); - } - - async merge(resource?: URI): Promise<[SyncResource, ISyncResourcePreview][]> { - const previews = await this.channel.call<[SyncResource, ISyncResourcePreview][]>('merge', [resource]); - return this.deserializePreviews(previews); - } - - async discard(resource: URI): Promise<[SyncResource, ISyncResourcePreview][]> { - const previews = await this.channel.call<[SyncResource, ISyncResourcePreview][]>('discard', [resource]); - return this.deserializePreviews(previews); - } - - async discardConflicts(): Promise<[SyncResource, ISyncResourcePreview][]> { - const previews = await this.channel.call<[SyncResource, ISyncResourcePreview][]>('discardConflicts'); - return this.deserializePreviews(previews); - } - - async apply(): Promise<[SyncResource, ISyncResourcePreview][]> { - const previews = await this.channel.call<[SyncResource, ISyncResourcePreview][]>('apply'); - return this.deserializePreviews(previews); - } - - pull(): Promise { - return this.channel.call('pull'); - } - - push(): Promise { - return this.channel.call('push'); - } - - stop(): Promise { - return this.channel.call('stop'); - } - - dispose(): void { - this.channel.call('dispose'); - } - - private deserializePreviews(previews: [SyncResource, ISyncResourcePreview][]): [SyncResource, ISyncResourcePreview][] { - return previews.map(([syncResource, preview]) => - ([ - syncResource, - { - isLastSyncFromCurrentMachine: preview.isLastSyncFromCurrentMachine, - resourcePreviews: preview.resourcePreviews.map(r => ({ - ...r, - localResource: URI.revive(r.localResource), - remoteResource: URI.revive(r.remoteResource), - previewResource: URI.revive(r.previewResource), - acceptedResource: URI.revive(r.acceptedResource), - })) - } - ])); - } -} - -registerSingleton(IUserDataSyncService, UserDataSyncService); +registerSharedProcessRemoteService(IUserDataSyncService, 'userDataSync', { channelClientCtor: UserDataSyncChannelClient }); diff --git a/src/vs/workbench/services/userDataSync/electron-sandbox/userDataSyncStoreManagementService.ts b/src/vs/workbench/services/userDataSync/electron-sandbox/userDataSyncStoreManagementService.ts index c42315a7454..32d1362bf3a 100644 --- a/src/vs/workbench/services/userDataSync/electron-sandbox/userDataSyncStoreManagementService.ts +++ b/src/vs/workbench/services/userDataSync/electron-sandbox/userDataSyncStoreManagementService.ts @@ -5,17 +5,16 @@ import { IUserDataSyncStoreManagementService, UserDataSyncStoreType, IUserDataSyncStore } from 'vs/platform/userDataSync/common/userDataSync'; import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services'; -import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { AbstractUserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; import { IProductService } from 'vs/platform/product/common/productService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { URI } from 'vs/base/common/uri'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { UserDataSyncStoreManagementServiceChannelClient } from 'vs/platform/userDataSync/common/userDataSyncIpc'; class UserDataSyncStoreManagementService extends AbstractUserDataSyncStoreManagementService implements IUserDataSyncStoreManagementService { - private readonly channel: IChannel; + private readonly channelClient: UserDataSyncStoreManagementServiceChannelClient; constructor( @IProductService productService: IProductService, @@ -24,28 +23,16 @@ class UserDataSyncStoreManagementService extends AbstractUserDataSyncStoreManage @ISharedProcessService sharedProcessService: ISharedProcessService, ) { super(productService, configurationService, storageService); - this.channel = sharedProcessService.getChannel('userDataSyncStoreManagement'); - this._register(this.channel.listen('onDidChangeUserDataSyncStore')(() => this.updateUserDataSyncStore())); + this.channelClient = this._register(new UserDataSyncStoreManagementServiceChannelClient(sharedProcessService.getChannel('userDataSyncStoreManagement'))); + this._register(this.channelClient.onDidChangeUserDataSyncStore(() => this.updateUserDataSyncStore())); } async switch(type: UserDataSyncStoreType): Promise { - return this.channel.call('switch', [type]); + return this.channelClient.switch(type); } async getPreviousUserDataSyncStore(): Promise { - const userDataSyncStore = await this.channel.call('getPreviousUserDataSyncStore'); - return this.revive(userDataSyncStore); - } - - private revive(userDataSyncStore: IUserDataSyncStore): IUserDataSyncStore { - return { - url: URI.revive(userDataSyncStore.url), - defaultUrl: URI.revive(userDataSyncStore.defaultUrl), - insidersUrl: URI.revive(userDataSyncStore.insidersUrl), - stableUrl: URI.revive(userDataSyncStore.stableUrl), - canSwitch: userDataSyncStore.canSwitch, - authenticationProviders: userDataSyncStore.authenticationProviders, - }; + return this.channelClient.getPreviousUserDataSyncStore(); } } diff --git a/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts b/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts index a851ffe2676..64cd61a6431 100644 --- a/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts +++ b/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts @@ -5,7 +5,7 @@ import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { URI } from 'vs/base/common/uri'; -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IJSONEditingService, JSONEditingError, JSONEditingErrorCode } from 'vs/workbench/services/configuration/common/jsonEditing'; import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, IWorkspacesService, rewriteWorkspaceFileForNewLocation, WORKSPACE_FILTER, IEnterWorkspaceResult, hasWorkspaceFileExtension, WORKSPACE_EXTENSION, isUntitledWorkspace, IStoredWorkspace } from 'vs/platform/workspaces/common/workspaces'; @@ -51,8 +51,8 @@ export abstract class AbstractWorkspaceEditingService implements IWorkspaceEditi async pickNewWorkspacePath(): Promise { let workspacePath = await this.fileDialogService.showSaveDialog({ - saveLabel: mnemonicButtonLabel(nls.localize('save', "Save")), - title: nls.localize('saveWorkspace', "Save Workspace"), + saveLabel: mnemonicButtonLabel(localize('save', "Save")), + title: localize('saveWorkspace', "Save Workspace"), filters: WORKSPACE_FILTER, defaultUri: await this.fileDialogService.defaultWorkspacePath(undefined, UNTITLED_WORKSPACE_FILENAME) }); @@ -292,19 +292,19 @@ export abstract class AbstractWorkspaceEditingService implements IWorkspaceEditi } private onInvalidWorkspaceConfigurationFileError(): void { - const message = nls.localize('errorInvalidTaskConfiguration', "Unable to write into workspace configuration file. Please open the file to correct errors/warnings in it and try again."); + const message = localize('errorInvalidTaskConfiguration', "Unable to write into workspace configuration file. Please open the file to correct errors/warnings in it and try again."); this.askToOpenWorkspaceConfigurationFile(message); } private onWorkspaceConfigurationFileDirtyError(): void { - const message = nls.localize('errorWorkspaceConfigurationFileDirty', "Unable to write into workspace configuration file because the file is dirty. Please save it and try again."); + const message = localize('errorWorkspaceConfigurationFileDirty', "Unable to write into workspace configuration file because the file is dirty. Please save it and try again."); this.askToOpenWorkspaceConfigurationFile(message); } private askToOpenWorkspaceConfigurationFile(message: string): void { this.notificationService.prompt(Severity.Error, message, [{ - label: nls.localize('openWorkspaceConfigurationFile', "Open Workspace Configuration"), + label: localize('openWorkspaceConfigurationFile', "Open Workspace Configuration"), run: () => this.commandService.executeCommand('workbench.action.openWorkspaceConfigFile') }] ); 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 daff0ecda29..f048e4d411d 100644 --- a/src/vs/workbench/services/workspaces/common/workspaceTrust.ts +++ b/src/vs/workbench/services/workspaces/common/workspaceTrust.ts @@ -12,16 +12,29 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; 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; @@ -81,12 +94,38 @@ 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; + const folderPath = folder.fsPath; + if (trustState === WorkspaceTrustState.Unknown) { const before = this.trustStateInfo.localFolders.length; - this.trustStateInfo.localFolders = this.trustStateInfo.localFolders.filter(info => info.uri !== folder.toString()); + this.trustStateInfo.localFolders = this.trustStateInfo.localFolders.filter(info => isEqual(URI.file(info.uri).fsPath, folderPath)); if (this.trustStateInfo.localFolders.length !== before) { changed = true; @@ -94,7 +133,7 @@ export class WorkspaceTrustModel extends Disposable implements IWorkspaceTrustMo } else { let found = false; for (const trustInfo of this.trustStateInfo.localFolders) { - if (trustInfo.uri === folder.toString()) { + if (isEqual(URI.file(trustInfo.uri).fsPath, folderPath)) { found = true; if (trustInfo.trustState !== trustState) { trustInfo.trustState = trustState; @@ -104,7 +143,7 @@ export class WorkspaceTrustModel extends Disposable implements IWorkspaceTrustMo } if (!found) { - this.trustStateInfo.localFolders.push({ uri: folder.toString(), trustState }); + this.trustStateInfo.localFolders.push({ uri: folderPath, trustState }); changed = true; } } @@ -115,13 +154,26 @@ export class WorkspaceTrustModel extends Disposable implements IWorkspaceTrustMo } getFolderTrustState(folder: URI): WorkspaceTrustState { + let result = WorkspaceTrustState.Unknown; + let maxLength = -1; + + const folderPath = folder.fsPath; for (const trustInfo of this.trustStateInfo.localFolders) { - if (trustInfo.uri === folder.toString()) { - return trustInfo.trustState; + const trustInfoPath = URI.file(trustInfo.uri).fsPath; + + if (isEqualOrParent(folderPath, trustInfoPath)) { + if (trustInfoPath.length > maxLength) { + maxLength = trustInfoPath.length; + result = trustInfo.trustState; + } } } - return WorkspaceTrustState.Unknown; + return result; + } + + getTrustStateInfo(): IWorkspaceTrustStateInfo { + return this.trustStateInfo; } } @@ -154,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; @@ -200,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/api/extHostTypes.test.ts b/src/vs/workbench/test/browser/api/extHostTypes.test.ts index d7d0339ed80..055c9ea4a8f 100644 --- a/src/vs/workbench/test/browser/api/extHostTypes.test.ts +++ b/src/vs/workbench/test/browser/api/extHostTypes.test.ts @@ -657,7 +657,6 @@ suite('ExtHostTypes', function () { assert.deepStrictEqual(obj.custom, notebookDocumentMetadataDefaults.custom); assert.deepStrictEqual(obj.displayOrder, notebookDocumentMetadataDefaults.displayOrder); assert.strictEqual(obj.editable, notebookDocumentMetadataDefaults.editable); - assert.deepStrictEqual(obj.languages, notebookDocumentMetadataDefaults.languages); assert.strictEqual(obj.runState, notebookDocumentMetadataDefaults.runState); assert.strictEqual(obj.runnable, notebookDocumentMetadataDefaults.runnable); assert.strictEqual(obj.trusted, notebookDocumentMetadataDefaults.trusted); diff --git a/src/vs/workbench/test/browser/api/mainThreadDocuments.test.ts b/src/vs/workbench/test/browser/api/mainThreadDocuments.test.ts index 57be38dfd98..1c48801f6c5 100644 --- a/src/vs/workbench/test/browser/api/mainThreadDocuments.test.ts +++ b/src/vs/workbench/test/browser/api/mainThreadDocuments.test.ts @@ -46,7 +46,7 @@ suite('BoundModelReferenceCollection', () => { dispose() { disposed.push(0); } - }); + }, 6); col.add( URI.parse('test://boofar'), @@ -55,7 +55,7 @@ suite('BoundModelReferenceCollection', () => { dispose() { disposed.push(1); } - }); + }, 6); col.add( URI.parse('test://xxxxxxx'), @@ -64,7 +64,7 @@ suite('BoundModelReferenceCollection', () => { dispose() { disposed.push(2); } - }); + }, 70); assert.deepStrictEqual(disposed, [0, 1]); }); 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/application.ts b/test/automation/src/application.ts index 0440d68d4e5..066b8f63fde 100644 --- a/test/automation/src/application.ts +++ b/test/automation/src/application.ts @@ -73,7 +73,7 @@ export class Application { await this.code.waitForElement('.explorer-folders-view'); if (expectWalkthroughPart) { - await this.code.waitForActiveElement(`.editor-instance[data-editor-id="workbench.editor.walkThroughPart"] > div > div[tabIndex="0"]`); + await this.code.waitForActiveElement(`.editor-instance > div > div.welcomePageFocusElement[tabIndex="0"]`); } } 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/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 235eac2e493..4fbbc3752b8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6672,10 +6672,10 @@ node-libs-browser@^2.2.1: util "^0.11.0" vm-browserify "^1.0.1" -node-pty@0.11.0-beta1: - version "0.11.0-beta1" - resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.11.0-beta1.tgz#961cf7ab56e0a689b71a19a46371ea090050f312" - integrity sha512-nHMB0K3LZTqjWv3X11XFdy/L4V2eMEk0RbmbVN02GPOkXdMy2NITI0px/x0JtNeIolRPq6r5hf5NUcNc2LJizw== +node-pty@0.10.0-beta19: + version "0.10.0-beta19" + resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.10.0-beta19.tgz#b7cbfba53f7b2a816efe8c9302dd083cc5874458" + integrity sha512-4UIOGMvpofUbe+ZniBUtY8zc/psMURSzbMonQgIhK7JlMQsUwcbkDIrKzStVLJX0FkeZpUNlsVtK7qqzHvrUZA== dependencies: nan "^2.14.0" @@ -9596,10 +9596,10 @@ ts-morph@^3.1.3: multimatch "^4.0.0" typescript "^3.0.1" -tsec@0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/tsec/-/tsec-0.1.1.tgz#aed25d4aeea0d1f641d4c941c3da23db1a7d1ce2" - integrity sha512-hAQJlm4SXO5c8//rWNdIwa8CX9wcXeyxo0S+D5pgU+m88LEbXqd+FGyVaysbfnVf6yywpQFte3dpXaudOl+NeQ== +tsec@0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/tsec/-/tsec-0.1.3.tgz#e3bc072c3307ba532c45efc8050d5eb8d52dadee" + integrity sha512-ek9ga1ZHFpLb96ydHQXgsAlgk2uEiVswmwyGNGuMakl2KFnJqLa2zvG7Qqxoo1PhSooyY7WNP1cgSYzgWTHetg== dependencies: glob "^7.1.1" @@ -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"